Reputation: 168
Here is a basic Metaclass
intending to provide a custom __init__
:
class D(type):
def __init__(cls, name, bases, attributes):
super(D, cls).__init__(name, bases, attributes)
for hook in R.registered_hooks:
setattr(cls, hook.__qualname__, hook)
The for loop iterates over a list of functions, calling setattr(cls, hook.__qualname__, hook)
for each of it.
Here is a Class that use the above Metaclass:
class E(metaclass=D):
def __init__(self):
pass
The weird thing:
E().__dict__ prints {}
But when I call
`E.__dict__ prints {'f1': <function f1 at 0x001>, 'f2': <function f2 at 0x002>}`
I was expecting to add those function as attributes to the instance attributes, since the __init__
provides a custom initialization for a Class but it seems like the attributes were added to Class attributes.
What is the cause of this and how can I add attributes to the instance, in this scenario? Thanks!
Upvotes: 0
Views: 173
Reputation: 208
If you're using a metaclass, that means you're changing the definition of a class, which indirectly affects the attributes of instances of that class. Assuming you actually want to do this:
You're creating a custom __init__
method. As with normal instantiation, __init__
is called on an object that has been already created. Generally when you're doing something meaningful with metaclasses, you'll want to add to the class's dict before instantiation, which is done in the __new__
method to avoid issues that may come up in subclassing. It should look something like this:
class D(type):
def __new__(cls, name, bases, dct):
for hook in R.registered_hooks:
dct[hook.__qualname__] = hook
return super(D, cls).__new__(cls, name, bases, dct)
This will not modify the __dict__
of instances of that class, but attribute resolution for instances also looks for class attributes. So if you add foo
as an attribute for E
in the metaclass, E().foo
will return the expected value even though it's not visible in E().__dict__
.
Upvotes: 2
Reputation: 22304
For __init__
to be called on the instantiation of an object obj
, you need to have defined type(obj).__init__
. By using a metaclass, you defined type(type(obj)).__init__
. You are working at the wrong level.
In this case, you only need inheritance, not a metaclass.
class D():
def __init__(self, *args):
print('D.__init__ was called')
class E(D):
pass
e = E() # prints: D.__init__ was called
Note that you will still find that e.__dict__
is empty.
print(e.__dict__) # {}
This is because you instances have no attributes, methods are stored and looked up in the class.
Upvotes: 2
Reputation: 531075
They are added as instance attributes. The instance, though, is the class, as an instance of the metaclass. You aren't defining a __init__
method for E
; you are defining the method that type
uses to initialize E
itself.
If you just want attributes added to the instance of E
, you're working at the wrong level; you should define a mixin and use multiple inheritance for that.
Upvotes: 3