python 3.2 plugin factory: instantiation from class/metaclass - python

I'm riffing from the information here: Metaclass not being called in subclasses
My problem is that I'm unable to create an instance of an object using this class registry. If I use "regular" construction methods, then it seems to instantiate objects correctly; but when I try to use the class object associated with registry, then I get an error that I'm passing an incorrect number of arguments. (Seems to be calling the metaclass new and not my constructor...??)
I'm not clear why it's failing, because I thought I should be able to create an instance from the class object by using "callable" syntax.
Seems I'm getting the metaclass put in the registry and not the class itself? But I don't see an easy way to access the class itself in the new call.
Here is my code example, which fails to instantiate a variable 'd':
registry = [] # list of subclasses
class PluginMetaclass(type):
def __new__(cls, name, bases, attrs):
print(cls)
print(name)
registry.append((name, cls))
return super(PluginMetaclass, cls).__new__(cls, name, bases, attrs)
class Plugin(metaclass=PluginMetaclass):
def __init__(self, stuff):
self.stuff = stuff
# in your plugin modules
class SpamPlugin(Plugin):
def __init__(self, stuff):
self.stuff = stuff
class BaconPlugin(Plugin):
def __init__(self, stuff):
self.stuff = stuff
c = SpamPlugin(0)
b = BaconPlugin(0)
mycls = registry[1][1]
d = mycls(0)
Thanks for any help.

I think the issue you're having is that the cls parameter passed to a metaclass constructor is actually a reference to the metaclass and not the class which is being created. Since __new__ is a classmethod of PluginMetaclass, it's associated with that class just like any regular classmethod. You probably want to be registering the newly created class object you're getting from super(PluginMetaclass, cls).__new__(..).
This modified version worked for me on 3.2:
class PluginMetaclass(type):
def __new__(cls, name, bases, attrs):
print("Called metaclass: %r" % cls)
print("Creating class with name: %r" % name)
newclass = super(PluginMetaclass, cls).__new__(cls, name, bases, attrs)
print("Registering class: %r" % newclass)
registry.append((name, newclass))
return newclass
and the print() calls show what's going on behind the scenes:
>>> registry = []
>>>
>>> class Plugin(metaclass=PluginMetaclass):
... def __init__(self, stuff):
... self.stuff = stuff
...
Called metaclass: <class '__main__.PluginMetaclass'>
Creating class with name: 'Plugin'
Registering class: <class '__main__.Plugin'>
>>> class SpamPlugin(Plugin):
... def __init__(self, stuff):
... self.stuff = stuff
...
Called metaclass: <class '__main__.PluginMetaclass'>
Creating class with name: 'SpamPlugin'
Registering class: <class '__main__.SpamPlugin'>
>>> class BaconPlugin(Plugin):
... def __init__(self, stuff):
... self.stuff = stuff
...
Called metaclass: <class '__main__.PluginMetaclass'>
Creating class with name: 'BaconPlugin'
Registering class: <class '__main__.BaconPlugin'>
>>> c = SpamPlugin(0)
>>> b = BaconPlugin(0)
>>> mycls = registry[1][1]
>>> d = mycls(0)
>>> d
<__main__.SpamPlugin object at 0x010478D0>
>>> registry
[('Plugin', <class '__main__.Plugin'>),
('SpamPlugin', <class '__main__.SpamPlugin'>),
('BaconPlugin', <class '__main__.BaconPlugin'>)]
Edit: #drone115b also solved this by using __init__ instead of __new__ in PluginMetaclass. That's probably the better way to go in most cases.

Related

Difference between inheriting from object and object.__class__ for python?

I can see code below
class MetaStrategy(StrategyBase.__class__): pass
I am not sure why not just write code like below
class MetaStrategy(StrategyBase): pass
Definition schematic
class StrategyBase(DataAccessor):
pass
class DataAccessor(LineIterator):
pass
class LineIterator(with_metaclass(MetaLineIterator, LineSeries)):
pass
def with_metaclass(meta, *bases):
class metaclass(meta):
def __new__(cls, name, this_bases, d):
return meta(name, bases, d)
return type.__new__(metaclass, str('temporary_class'), (), {})
If you call self.__class__ from a subclass instance, self.__class__ will use that type of the subclass.
Any class that is expressly specified while using the class will be used naturally.
Take the example below:
class Foo(object):
def create_new(self):
return self.__class__()
def create_new2(self):
return Foo()
class Bar(Foo):
pass
b = Bar()
c = b.create_new()
print type(c) # We got an instance of Bar
d = b.create_new2()
print type(d) # we got an instance of Foo

Python Lazy Loading Property not working for Class method

I have a class Foo that uses lazy loading for Foo.bar.
class Foo(object):
#property
def bar(self):
if not hasattr(self, '_bar'):
self._initBar()
return self._bar
def _initBar(self):
self._bar = 'bar'
foo = Foo()
print(foo.bar) # prints "bar"
However when i try to convert Foo to use class methods only, Foo.bar is not giving me bar, but instead giving:
<property object at 0x000001CA1C344728>
Why is it not giving me bar?
class Foo(object):
#property
#classmethod
def bar(cls):
if not hasattr(cls, '_bar'):
cls._initBar()
return cls._bar
#classmethod
def _initBar(cls):
cls._bar = 'bar'
print(Foo.bar) # prints "<property object at 0x000001CA1C344728>"
The property built-in is a handy tool in Python that presents an easy use case of a more powerful mechanism, which is the "descriptor protocol".
Basically, any object when retrieved from an instance or from a class is first checked if it has one of __get__, __set__ or __del__ methods. property wraps getter functions to be called by __get__ when the attribute is retrieved from an instance, but returns the property object itself when it is retrieved from the class instead. (That is even a common use case for other descriptors)
Thus, if you want a property like behavior for class attributes, you have to create your own descriptor class, sporting the __get__ method - or, simply create your class with a metaclass, and use property as is, on the metaclass. The drawbacks of the later are many: you willneed one custom metaclass for each class where you want the managed class attributes being just the first of them. On the other hand, creating your own descriptor is quite easy:
class MyProperty:
def __init__(self, initializer):
self.initializer = initializer
def __set_name__(self, owner, name):
self.name = name
def __get__(self, instance, owner):
if not hasattr(owner, "_" + self.name):
initializer = getattr(owner, self.initializer)
initializer()
return getattr(owner, "_" + self.name)
class Foo:
bar = MyProperty("_initBar")
#classmethod
def _initBar(cls):
cls._bar = 'bar'
Please note that __set_name__ is only implemented from Python 3.6 on. On older Python's, including Python 2.x, you should use:
class MyProperty(object):
def __init__(self, initializer, name):
self.initializer = initializer
self.name = name
def __get__(self, instance, owner):
if not hasattr(owner, "_" + self.name):
initializer = getattr(owner, self.initializer)
initializer(owner)
return getattr(owner, "_" + self.name)
class Foo(object):
bar = MyProperty("_initBar", name='bar')
#classmethod
def _initBar(cls):
cls._bar = 'bar'
You could use a metaclass, because property objects are meant to be accessed through the instance and if your intended instance is the class itself, then you need to put the property on the class of the class, i.e. the metaclass:
In [37]: class MetaFoo(type):
...: #property
...: def bar(cls):
...: if not hasattr(cls, '_bar'):
...: print("hard at work!")
...: cls._init_bar()
...: return cls._bar
...:
In [38]: class Foo(metaclass=MetaFoo):
...: #classmethod
...: def _init_bar(cls):
...: cls._bar = 'bar'
...:
In [39]: Foo.bar
hard at work!
Out[39]: 'bar'
In [40]: Foo.bar
Out[40]: 'bar'
Of course, while this may be possible, I'm agnostic about whether or not it is not advisable.
Edit
As #jsbueno demonstrates, it is much more sane to simply define your own descriptor, which can give you a much more flexible behavior.

Is there a way to make code run when a class is derived from a particular parent class in Python?

For example, if I create the class Foo, then later derive the subclass Bar, I want the myCode() method of Foo to run.
class Foo(object):
x = 0
def __init__(self):
pass
def myCode(self):
if(self.x == 0):
raise Exception("nope")
class Bar(Foo): #This is where I want myCode() to execute
def baz(self):
pass
This should happen any time a class is derived from the base class Foo. Is it possible to do this in Python? I'm using Python 3 if it matters.
Note: In my real code, Foo is actually an abstract base class.
Edit: I also need access to derived class member data and methods in myCode().
Use a metaclass:
class MetaClass:
def __init__(cls, name, bases, dictionary):
if name is not 'Parent':
print('Subclass created with name: %s' % name)
super().__init__(name, bases, dictionary)
class Parent(metaclass=MetaClass):
pass
class Subclass(Parent):
pass
Output:
Subclass created with name: Subclass
Metaclasses control how classes themselves are created. Subclass inherits its metaclass from Parent, and thus that code gets run when it is defined.
Edit: As for your use case with an abstract base class, off the top of my head I think you'd just need to define your metaclass as a subclass of ABCMeta, but I didn't test that.
May this code can help you:
class Foo:
def myCode(self):
print('myCode within Foo')
def __init__(self):
if type(self) != Foo:
self.myCode()
class Bar(Foo):
def __init__(self):
super(Bar, self).__init__()
def baz(self):
pass
Test:
>>>
>>> f = Foo()
>>> b = Bar()
myCode within Foo
>>>
This works:
class MyMeta(type):
def __new__(cls, name, parents, dct):
if name is not 'Foo':
if 'x' not in dct:
raise Exception("Nein!")
elif 'x' in dct and dct['x'] == 0:
raise Exception("Nope!")
return super(MyMeta, cls).__new__(cls, name, parents, dct)
Output:
class Bar(Foo):
x = 0
> Exception: Nope!
This is my specific use case if anyone wants to comment on whether or not this is appropriate:
class MagmaMeta(type):
def __new__(cls, name, parents, dct):
# Check that Magma instances are valid.
if name is not 'Magma':
if 'CAYLEY_TABLE' not in dct:
raise Exception("Cannot create Magma instance without CAYLEY_TABLE")
else:
# Check for square CAYLEY_TABLE
for row in CAYLEY_TABLE:
if not len(row) == len(dct['CAYLEY_TABLE']):
raise Exception("CAYLEY_TABLE must be a square array")
# Create SET and ORDER from CAYLEY_TABLE
dct['SET'] = set([])
for rows in CAYLEY_TABLE:
for x in rows:
dct['SET'].add(x)
dct['ORDER'] = len(dct['SET'])
return super(MyMeta, cls).__new__(cls, name, parents, dct)

Access the class type when constructing a class attribute in python?

I'd like to do this:
class MyThing(object):
def __init__(self,owning_cls):
self.owning_cls = owning_cls
class MyClass(object):
thing = MyThing(self.__class__)
print MyClass.thing.owning_cls
This doesn't work - as there isn't a self to refer to in the class construction of MyClass.
Is there any way to achieve this (it's clearly trivial if we make thing an instance attribute, but I'd like to be a class attribute please!)?
Perform the call immediately after the class declaration:
class MyClass(object): pass
MyClass.thing = MyThing(MyClass)
Use a decorator. I find this to be a clean solution because it lets you keep more of the class definition together, rather than having to write additional class-related code after the class definition or forcing you to instantiate MyClass, etc.
class MyThing(object):
def __init__(self,owning_cls):
self.owning_cls = owning_cls
def set_thing(cls):
cls.thing = MyThing(cls)
return cls
#set_thing
class MyClass(object):
pass
>>> print MyClass.thing.owner_cls
<class '__main__.MyClass'>
Maybe you can initialize the class with __new__?
Use desciptor:
class Ownable(object):
def __init__(self, clz):
self._clz = clz
self._inst = None
def __get__(self, inst, owner_clz):
self._inst = self._inst or self._clz(owner_clz)
return self._inst
class MyThing(object):
def __init__(self, owner_clz):
self.owner_clz = owner_clz
class MyClass(object):
thing = Ownable(MyThing)
>>> print MyClass.thing.owner_clz
<class '__main__.MyClass'>
Ah, the use MetaClasses comment helps a lot here.
This looks like an "easy" way to achieve exactly what I want
class MyClassMetaclass(type):
def __new__(cls, name, bases, dct):
cls.thing = MyThing(name)
return super(MyClassMetaclass, cls).__new__(cls, name, bases, dct)
class MyThing(object):
def __init__(self,owning_cls):
self.owning_cls = owning_cls
class MyClass(object):
__metaclass__=MyClassMetaclass
print MyClass.thing.owning_cls

Python: How to register all child classes with the father class upon creation

I have python class trees, each made up of an abstract base class and many deriving concrete classes. I want all concrete classes to be accessible through a base-class method, and I do not want to specify anything during child-class creation.
This is what my imagined solution looks like:
class BaseClassA(object):
# <some magic code around here>
#classmethod
def getConcreteClasses(cls):
# <some magic related code here>
class ConcreteClassA1(BaseClassA):
# no magic-related code here
class ConcreteClassA2(BaseClassA):
# no magic-related code here
As much as possible, I'd prefer to write the "magic" once as a sort of design pattern. I want to be able to apply it to different class trees in different scenarios (i.e. add a similar tree with "BaseClassB" and its concrete classes).
Thanks Internet!
you can use meta classes for that:
class AutoRegister(type):
def __new__(mcs, name, bases, classdict):
new_cls = type.__new__(mcs, name, bases, classdict)
#print mcs, name, bases, classdict
for b in bases:
if hasattr(b, 'register_subclass'):
b.register_subclass(new_cls)
return new_cls
class AbstractClassA(object):
__metaclass__ = AutoRegister
_subclasses = []
#classmethod
def register_subclass(klass, cls):
klass._subclasses.append(cls)
#classmethod
def get_concrete_classes(klass):
return klass._subclasses
class ConcreteClassA1(AbstractClassA):
pass
class ConcreteClassA2(AbstractClassA):
pass
class ConcreteClassA3(ConcreteClassA2):
pass
print AbstractClassA.get_concrete_classes()
I'm personnaly very wary of this kind of magic. Don't put too much of this in your code.
Here is a simple solution using modern python's (3.6+) __init__subclass__ defined in PEP 487. It allows you to avoid using a meta-class.
class BaseClassA(object):
_subclasses = []
#classmethod
def get_concrete_classes(cls):
return list(cls._subclasses)
def __init_subclass__(cls):
BaseClassA._subclasses.append(cls)
class ConcreteClassA1(BaseClassA):
pass # no magic-related code here
class ConcreteClassA2(BaseClassA):
pass # no magic-related code here
print(BaseClassA.get_concrete_classes())
You should know that part of the answer you're looking for is built-in. New-style classes automatically keep a weak reference to all of their child classes which can be accessed with the __subclasses__ method:
#classmethod
def getConcreteClasses(cls):
return cls.__subclasses__()
This won't return sub-sub-classes. If you need those, you can create a recursive generator to get them all:
#classmethod
def getConcreteClasses(cls):
for c in cls.__subclasses__():
yield c
for c2 in c.getConcreteClasses():
yield c2
Another way to do this, with a decorator, if your subclasses are either not defining __init__ or are calling their parent's __init__:
def lister(cls):
cls.classes = list()
cls._init = cls.__init__
def init(self, *args, **kwargs):
cls = self.__class__
if cls not in cls.classes:
cls.classes.append(cls)
cls._init(self, *args, **kwargs)
cls.__init__ = init
#classmethod
def getclasses(cls):
return cls.classes
cls.getclasses = getclasses
return cls
#lister
class A(object): pass
class B(A): pass
class C(A):
def __init__(self):
super(C, self).__init__()
b = B()
c = C()
c2 = C()
print 'Classes:', c.getclasses()
It will work whether or not the base class defines __init__.

Categories