我了解到,在类名称空间中创建变量然后在类构造函数中更改其值被认为是不好的做法。
(我的资料来源之一:SoftwareEngineering SE: Is it a good practice to declare instance variables as None in a class in Python。)
考虑以下代码:
# lib.py
class mixin:
def __init_subclass__(cls, **kwargs):
cls.check_mixin_subclass_validity(cls)
super().__init_subclass__(**kwargs)
def check_mixin_subclass_validity(subclass):
assert hasattr(subclass, 'necessary_var'), \
'Missing necessary_var'
def method_used_by_subclass(self):
return self.necessary_var * 3.14
# app.py
class my_subclass(mixin):
necessary_var = None
def __init__(self, some_value):
self.necessary_var = some_value
def run(self):
# DO SOME STUFF
self.necessary_var = self.method_used_by_subclass()
# DO OTHER STUFF
为强制其子类声明变量 necessary_var ,类mixin
使用元类subclass_validator
。
我知道使其在 app.py
端起作用的唯一方法是将 necessary_var 初始化为类变量。
我正在丢失某些东西,或者这是唯一的方法吗?
答案 0 :(得分:0)
您应该检查属性和方法是否在类实例化时存在,而不是在实例化之前。 abc
模块就是这样做的,并且有充分的理由可以像这样工作。
首先,我想指出的是,您似乎要检查的是实例属性是否存在。
由于Python具有动态特性,因此无法在创建实例之前(即调用__init__
之后)进行操作。我们可以定义Mixin.__init__
,但随后我们将不得不依靠您API的用户来保持良好的卫生状况并始终调用super().__init__
。
因此,一种选择是创建一个元类,并在其__call__
方法中添加一个检查。
class MetaMixin(type):
def __call__(self, *args, **kwargs):
instance = super().__call__(*args, **kwargs)
assert hasattr(instance, 'necessary_var')
class Mixin(metaclass=MetaMixin):
pass
class Foo(Mixin):
def __init__(self):
self.necessary_var = ...
Foo() # Works fine
class Bar(Mixin):
pass
Bar() # AssertionError
为使自己确信在实例化时执行此操作是一种好习惯,我们可以考虑使用此行为的abc
模块。
from abc import abstractmethod, ABC
class AbstractMixin(ABC):
@abstractmethod
def foo(self):
...
class Foo(AbstractMixin):
pass
# Right now, everything is still all good
Foo() # TypeError: Can't instantiate abstract class Foo with abstract methods foo
您可以看到TypeError
在实例化Foo()
而不是在类创建时被提高。
这样做的原因是,并非每个类都将被实例化,请考虑以下示例,在该示例中,我们要从Mixin
继承来创建一个新的mixin,以检查更多属性。
class Mixin:
def __init_subclass__(cls, **kwargs):
assert hasattr(cls, 'necessary_var')
super().__init_subclass__(**kwargs)
class MoreMixin(Mixin):
def __init_subclass__(cls, **kwargs):
assert hasattr(cls, 'other_necessary_var')
super().__init_subclass__(**kwargs)
# AssertionError was raised at that point
class Foo(MoreMixin):
necessary_var = ...
other_necessary_var = ...
如您所见,AssertionError
是在创建MoreMixin
类时提出的。这显然不是期望的行为,因为Foo
类实际上是正确构建的,而这正是我们的mixin应该检查的。
最后,应在实例化时完成某些属性或方法的存在,否则,您将避免使用大量有用的继承技术。这就是abc
模块这样做的原因,这就是我们应该这样做的原因。