声明类属性时如何触发设置器?

时间:2019-05-02 21:51:19

标签: python

我有一堂课;我们称之为Foo。它具有key_type类属性,该属性包含类型:

class Key: pass

class Foo:
  key_type = Key

我想在键类型被初始化(*)或更改时对键类型运行某种方法。

因此,我将key_type设置为元类的一个属性:

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}")

输出:

Foo's key type: None
Setting key_type to <class '__main__.OtherKey'>
Foo's key type: <class '__main__.OtherKey'>

似乎元类中_key_type的定义取代了主类中key_type的定义。但最重要的是,未使用Key类型调用setter。

预期输出:

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'>

(*)我也希望它在初始化时发生的原因是可以从中继承Foo。我想知道(无论是在MetaFoo中还是在Foo中)子类是否使用其他key_type

1 个答案:

答案 0 :(得分:4)

key_typeclass Foo的定义实际上是将键值对添加到了用于初始化MetaFoo的第三个参数(这是一个dict),它无能为力。

因此,您可以操纵MetaFoo的初始化来显式调用您的setter方法。这可以通过重写元类的__init__方法来实现:

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}")

输出:

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'>