Python: add a class to another classes bases - python

im trying to add a class to another classes bases:
class A(object):
def __init__(self, name):
self.name = name
class AMixin(object):
def mixinExample(self):
return ("in AMixin.mixinExample" + self.name)
def MixIn(TargetClass, MixInClass):
if MixInClass not in TargetClass.__bases__:
TargetClass.__bases__ += (MixInClass,)
if __name__ == "__main__":
a_instance = A("Q7")
MixIn(A,AMixin)
print(a_instance.mixinExample())
and i get this error:
TypeError: Cannot create a consistent method resolution
order (MRO) for bases object, AMixin
I got this error because both of the classes (A and AMixin) inherent from 'object'?

You're trying to set A's bases to (object, AMixin). This ordering means that object methods should override AMixin methods.
However, AMixin inherits from object, and that means that AMixin methods should override object methods.
It is not possible for both of those requirements to be satisfied, hence the error.

Related

Extend Python class's init method

I have a BaseClass and an AbstractClass that inherits from the BaseClass. This is the structure I have in place
class BaseClass(object):
def __init__(self, initialize=True):
self.name = 'base_class'
self.start = 0
if initialize:
self.start = 100
class AbstractClass(BaseClass):
def __init__(self):
self.name = 'asbtract_class'
super(BaseClass, self).__init__()
I want to pass the abstract class an initialize parameter that gets transferred to the base class and if True sets the object's start value to 100.
I tried using the super(BaseClass, self).__init__() but the abstract class gets no start attribute. I get an error when I try to access it.
How can I pass a value the initialize argument to the AbstractClass and use the BaseClass's __init__ method to set the start attribute on the AbstractClass.
The code I used
best = BaseClass()
abs = AbstractClass()
abs.start # AttributeError: 'AbstractClass' object has no attribute 'start'
To invoke the constructor of the super class you should use the class name of the sub class and not the super class, i.e.:
class AbstractClass(BaseClass):
def __init__(self):
super(AbstractClass, self).__init__()
self.name = 'abstract_class'
Note also that I changed the order of invoking the constructor of the super class and setting the name attribute. If you set it before calling the super, the attribute would be overridden by the constructor of the super class, which is most likely not what you intended
And as #Sraw pointed out, for python 3 the notation of calling the super no longer requires the referencing of the class name and can be simplified to
class AbstractClass(BaseClass):
def __init__(self):
super().__init__()

Check if a function raises NotImplementedError before calling it in Python

I have the following simplified scheme:
class NetworkAnalyzer(object):
def __init__(self):
print('is _score_funct implemented?')
#staticmethod
def _score_funct(network):
raise NotImplementedError
class LS(NetworkAnalyzer):
#staticmethod
def _score_funct(network):
return network
and I am looking for what I should use instead of print('is _score_funct implemented?') in order to figure out if a subclass has already implemented _score_funct(network) or not.
Note: If there is a more pythonic/conventional way of structuring the code, I would also appreciate its mention. The reason I defined it this way is, some NetworkAnalyzer subclasses have _score_funct in their definition, and the ones that dont have it will have different initialization of variables although they will have the same structure
Use an abstract base class and you won't be able to instantiate the class unless it implements all of the abstract methods:
import abc
class NetworkAnalyzerInterface(abc.ABC):
#staticmethod
#abc.abstractmethod
def _score_funct(network):
pass
class NetworkAnalyzer(NetworkAnalyzerInterface):
def __init__(self):
pass
class LS(NetworkAnalyzer):
#staticmethod
def _score_funct(network):
return network
class Bad(NetworkAnalyzer):
pass
ls = LS() # Ok
b = Bad() # raises TypeError: Can't instantiate abstract class Bad with abstract methods _score_funct
I'm not a metaclass/class specialist but here's a method that works in your simple case (not sure it works as-is in a complex/nested class namespace):
To check if the method was overridden, you could try a getattr on the function name, then check the qualified name (class part is enough using string partitionning):
class NetworkAnalyzer(object):
def __init__(self):
funcname = "_score_funct"
d = getattr(self,funcname)
print(d.__qualname__.partition(".")[0] == self.__class__.__name__)
if _score_funct is defined in LS, d.__qualname__ is LS._score_funct, else it's NetworkAnalyzer._score_funct.
That works if the method is implemented at LS class level. Else you could replace by:
d.__qualname__.partition(".")[0] != "NetworkAnalyzer"
Of course if the method is overridden with some code which raises an NotImplementedError, that won't work... This method doesn't inspect methods code (which is hazardous anyway)

How to make a Python 2.x AND 3.x abstract base class? [duplicate]

I'm trying to get a python2 program working in python3, it has the following Meta class definition. Which works just fine on Py2. What's the "best" way to have this be compatible with both py2 and py3?
It's failing in the unit test where it does:
try:
raise Actor.DoesNotExist
except Actor.DoesNotExist:
pass
Failure is:
AttributeError: type object 'Actor' has no attribute 'DoesNotExist'
The base meta class definition is:
class MetaDocument(type):
def __new__(meta,name,bases,dct):
class DoesNotExist(BaseException):
pass
class MultipleDocumentsReturned(BaseException):
pass
dct['DoesNotExist'] = DoesNotExist
dct['MultipleDocumentsReturned'] = MultipleDocumentsReturned
class_type = type.__new__(meta, name, bases, dct)
if not class_type in document_classes:
if name == 'Document' and bases == (object,):
pass
else:
document_classes.append(class_type)
return class_type
class Document(object):
__metaclass__ = MetaDocument
You could use the MetaDocument() metaclass as a factory to produce a class replacing your Document class, re-using the class attributes:
class Document(object):
# various and sundry methods and attributes
body = vars(Document).copy()
body.pop('__dict__', None)
body.pop('__weakref__', None)
Document = MetaDocument(Document.__name__, Document.__bases__, body)
This doesn't require you to build the 3rd argument, the class body, manually.
You can turn this into a class decorator:
def with_metaclass(mcls):
def decorator(cls):
body = vars(cls).copy()
# clean out class body
body.pop('__dict__', None)
body.pop('__weakref__', None)
return mcls(cls.__name__, cls.__bases__, body)
return decorator
then use as:
#with_metaclass(MetaDocument)
class Document(object):
# various and sundry methods and attributes
Alternatively, use the six library for this:
#six.add_metaclass(MetaDocument)
class Document(object):
where the #six.add_metaclass() decorator also takes care of any __slots__ you may have defined; my simpler version above doesn't.
six also has a six.with_metaclass() base-class factory:
class Document(six.with_metaclass(MetaDocument)):
which injects an extra base class into the MRO.
six has a utility for this.
class Document(six.with_metaclass(MetaDocument, object)):
# class definition, without the __metaclass__
The only side effect is that the class hierarchy changes from
>>> Document.__mro__
(<class 'test.Document'>, <type 'object'>)
to
>>> Document.__mro__
(<class 'test.Document'>, <class 'test.NewBase'>, <type 'object'>)
because with_metaclass actually returns a new class with the appropriate metaclass.

Abstract classes and PyMongo; can't instantiate abstract class

I created the empty abstract class AbstractStorage and inherited the Storage class from it:
import abc
import pymongo as mongo
host = mongo.MongoClient()
print(host.alive()) # True
class AbstractStorage(metaclass=abc.ABCMeta):
pass
class Storage(AbstractStorage):
dbh = host
def __init__(self):
print('__init__')
Storage()
I expected the output to be
True
__init__
however, the one I'm getting is
True
Traceback (most recent call last):
File "/home/vaultah/run.py", line 16, in <module>
Storage()
TypeError: Can't instantiate abstract class Storage with abstract methods dbh
The problem (apparently) goes away if I remove metaclass=abc.ABCMeta (so that AbstractStorage becomes an ordinary class) and/or if I set dbh to some other value.
What's going on here?
This isn't really a problem with ABCs, it's a problem with PyMongo. There is an issue about it here. It seems that pymongo overrides __getattr__ to return some sort of database class. This means that host.__isabstractmethod__ returns a Database object, which is true in a boolean context. This cause ABCMeta to believe that host is an abstract method:
>>> bool(host.__isabstractmethod__)
True
The workaround described in the issue report is to manually set host.__isabstractmethod__ = False on your object. The last comment on the issue suggests a fix has been put in for pymongo 3.0.
Short Version
mongo.MongoClient returns an object that appears to be (is?) an abstract method, which you then assign to the dbh field in Storage. This makes Storage an abstract class, so instantiating it raises a TypeError.
Note that I don't have pymongo, so I can't tell you anything more about MongoClient than how it gets treated by ABCMeta.
Long Version
The ABCMeta.__new__ method looks inside each field of the new class it's creating. Any field that itself has a True (or "true-like") __isabstractmethod__ field is considered an abstract method. If a class has any non-overridden abstract methods, the whole class is considered abstract, so any attempt to instantiate it is an error.
From an earlier version of the standard library's abc.py:
def __new__(mcls, name, bases, namespace):
cls = super().__new__(mcls, name, bases, namespace)
# Compute set of abstract method names
abstracts = {name
for name, value in namespace.items()
if getattr(value, "__isabstractmethod__", False)}
# ...
cls.__abstractmethods__ = frozenset(abstracts)
# ...
This is not mentioned in the abc.ABCMeta class docs, but a bit lower, under the #abc.abstractmethod decorator:
In order to correctly interoperate with the abstract base class machinery, the descriptor must identify itself as abstract using __isabstractmethod__. In general, this attribute should be True if any of the methods used to compose the descriptor are abstract.
Example
I created a bogus "abstract-looking" class with an __isabstractmethod__ attribute, and two supposedly-concrete subclasses of AbstractStorage. You'll see that one produces the exact error you're getting:
#!/usr/bin/env python3
import abc
# I don't have pymongo, so I have to fake it. See CounterfeitAbstractMethod.
#import pymongo as mongo
class CounterfeitAbstractMethod():
"""
This class appears to be an abstract method to the abc.ABCMeta.__new__
method.
Normally, finding an abstract method in a class's namespace means
that class is also abstract, so instantiating that class is an
error.
If a class derived from abc.ABCMeta has an instance of
CounterfeitAbstractMethod as a value anywhere in its namespace
dictionary, any attempt to instantiate that class will raise a
TypeError: Can't instantiate abstract class <classname> with
abstract method <fieldname>.
"""
__isabstractmethod__ = True
class AbstractStorage(metaclass=abc.ABCMeta):
def __init__(self):
"""
Do-nothing initializer that prints the name of the (sub)class
being initialized.
"""
print(self.__class__.__name__ + ".__init__ executing.")
return
class ConcreteStorage(AbstractStorage):
"""
A concrete class that also _appears_ concrete to abc.ABCMeta. This
class can be instantiated normally.
"""
whatever = "Anything that doesn't appear to be an abstract method will do."
class BogusStorage(AbstractStorage):
"""
This is (supposedly) a concrete class, but its whatever field appears
to be an abstract method, making this whole class abstract ---
abc.ABCMeta will refuse to construct any this class.
"""
#whatever = mongo.MongoClient('localhost', 27017)
whatever = CounterfeitAbstractMethod()
def main():
"""
Print details of the ConcreteStorage and BogusStorage classes.
"""
for cls in ConcreteStorage, BogusStorage:
print(cls.__name__ + ":")
print(" whatever field: " + str(cls.whatever))
print(" abstract methods: " + str(cls.__abstractmethods__))
print(" Instantiating...")
print(" ", end="")
# KABOOM! Instantiating BogusStorage will raise a TypeError,
# because it appears to be an _abstract_ class.
instance = cls()
print(" instance: " + str(instance))
print()
return
if "__main__" == __name__:
main()
Running this produces:
$ ./storage.py
ConcreteStorage:
whatever field: Anything that doesn't appear to be an abstract method will do.
abstract methods: frozenset()
Instantiating...
ConcreteStorage.__init__ executing.
instance: <__main__.ConcreteStorage object at 0x253afd0>
BogusStorage:
whatever field: <__main__.CounterfeitAbstractMethod object at 0x253ad50>
abstract methods: frozenset({'whatever'})
Instantiating...
Traceback (most recent call last):
File "./storage.py", line 75, in <module>
main()
File "./storage.py", line 68, in main
instance = cls()
TypeError: Can't instantiate abstract class BogusStorage with abstract methods whatever

Python's Class Inheritance

# Defining a Base class to be shared among many other classes later:
class Base(dict):
"""Base is the base class from which all the class will derrive.
"""
name = 'name'
def __init__( self):
"""Initialise Base Class
"""
dict.__init__(self)
self[Base.name] = ""
# I create an instance of the Base class:
my_base_instance = Base()
# Since a Base class inherited from a build in 'dict' the instance of the class is a dictionary. I can print it out with:
print my_base_instance Results to: {'name': ''}
# Now I am defining a Project class which should inherit from an instance of Base class:
class Project(object):
def __init__(self):
print "OK"
self['id'] = ''
# Trying to create an instance of Project class and getting the error:
project_class = Project(base_class)
TypeError: __init__() takes exactly 1 argument (2 given)
there are two mistakes in your code:
1) Class inheritance
class Project(Base): # you should inherit from Base here...
def __init__(self):
print "OK"
self['id'] = ''
2) Instance definition (your __init__ does not requires any explicit parameter, and for sure not the ancestor class)
project_class = Project() # ...and not here since this is an instance, not a Class
When you're instantiating a class, you don't need to pass in base_class. That's done at definition. __init__ takes exactly 1 argument, which is self, and automatic. You just need to call
project_class = Project()
For Project to inherit from Base, you should not subclass it from object but from Base i.e class Project(Base). You get TypeError: init() takes exactly 1 argument (2 given) error when you instantiate Project class because the constructor takes only 1 parameter(self) and you pass base_class too. 'self' is passed implicitly by python.

Categories