Reputation: 956
I have a class; let's call it Foo
. It has a key_type
class attribute, which contains a type:
class Key: pass
class Foo:
key_type = Key
I would like to run some method on the key type when it's initialized(*) and whenever it changes.
So I made key_type
a property in a metaclass:
class Key: pass
class OtherKey: pass
class MetaFoo(type):
_key_type = None
@property
def key_type(cls):
return cls._key_type
@key_type.setter
def key_type(cls, value):
print(f'Setting key_type to {value}')
cls._key_type = value
class Foo(metaclass=MetaFoo):
key_type = Key
if __name__ == "__main__":
print(f"Foo's key type: {Foo.key_type}")
Foo.key_type = OtherKey
print(f"Foo's key type: {Foo.key_type}")
Output:
Foo's key type: None
Setting key_type to <class '__main__.OtherKey'>
Foo's key type: <class '__main__.OtherKey'>
It seems like the definition of _key_type
in the meta class overrode the definition of key_type
in the main class. But most importantly, the setter was not called with the Key
type.
Expected output:
Setting key_type to <class '__main__.Key'>
Foo's key type: <class '__main__.Key'>
Setting key_type to <class '__main__.OtherKey'>
Foo's key type: <class '__main__.OtherKey'>
(*) The reason I also want it to happen when it's initialized is that Foo can be inherited from. I want to know (either in MetaFoo or in Foo) if a child class uses a different key_type
.
Upvotes: 1
Views: 182
Reputation: 6458
The definition of key_type
in class Foo
actually added a key-value pair to the third parameter (which is a dict) for the initialization of MetaFoo
, nothing else it will do.
Therefore, you can manipulate the initialization of MetaFoo
to explicitly call your setter method. This can be done by overriding __init__
method of your metaclass:
class Key: pass
class OtherKey: pass
class MetaFoo(type):
_key_type = None
@property
def key_type(cls):
return cls._key_type
@key_type.setter
def key_type(cls, value):
print(f'Setting key_type to {value}')
cls._key_type = value
def __init__(self, name, bases, kw):
super(MetaFoo, self).__init__(name, bases, kw)
for key, val in kw.items():
setattr(self, key, val)
class Foo(metaclass=MetaFoo):
key_type = Key
if __name__ == "__main__":
print(f"Foo's key type: {Foo.key_type}")
Foo.key_type = OtherKey
print(f"Foo's key type: {Foo.key_type}")
The output:
Setting key_type to <class '__main__.Key'>
Foo's key type: <class '__main__.Key'>
Setting key_type to <class '__main__.OtherKey'>
Foo's key type: <class '__main__.OtherKey'>
Upvotes: 4