python - 当父类需要声明它时,避免在python子类中使用类变量

标签 python mixins metaclass class-variables

我读到在类命名空间中创建一个变量然后在类构造函数中更改它的值被认为是不好的做法。

(我的来源之一: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 初始化为类变量。

我遗漏了什么或者这是唯一的方法吗?

最佳答案

简答

您应该在类实例化时检查属性和方法是否存在,而不是之前。这就是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

如您所见TypeErrorFoo() 的实例化时被提升而不是在类创作中。

但为什么它会这样呢?

原因是不是每个类都会被实例化,考虑我们要从 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 时被提出类(class)。这显然不是自 Foo 以来所期望的行为。类实际上是正确构建的,这就是我们的 mixin 应该检查的内容。

总而言之,某些属性或方法的存在应该在实例化时完成,否则,您将阻止大量有用的继承技术。这就是为什么 abc模块就是这样做的,这就是我们应该这样做的原因。

关于python - 当父类需要声明它时,避免在python子类中使用类变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55225816/

相关文章:

templates - 有没有办法使用 mixin 或模板从 D 中的类生成接口(interface)?

sass - Bootstrap 4 媒体查询 mixins 无法通过 CDN 与 Bootstrap 4 一起使用

python - deepcopy 不尊重元类

python - 为什么将我的数据转换为 ndarray 会给我 'python' 信号 SIGBUS 终止(地址未对齐错误)?

python - 关于 Flask 中的 as_view 函数

python - 如何修改此正则表达式以使其与 Python 中的 '&' 不匹配?

Python 默认元类

python - 如何在每次创建页面时生成一定长度的随机 url?

json - Pug - 与条件 .json 对象混合

reflection - Squeak 中的拦截消息