python - 让 python mixins 使用更安全(并告诉 linter 闭嘴)

标签 python class

考虑这个玩具混合:

class MyMixin:
    def foo(self):
        return self.x + self.y

它与实际提供 xy 的类一起使用。但是:

  • linter 不知道这一点,所以提示
  • 我可以创建一个类及其实例,但当我尝试使用 foo (请参阅下一个代码)
class A(MyMixin):
    def __init__(self, x=0):
        self.x = x
        
a = A(10)  # no problem!
a.foo()  # problem: AttributeError: 'A' object has no attribute 'y'

相反,我想要

  • linter 不会提示 MyMixin 中缺少 xy
  • 当我在没有提供必要属性的情况下使用它时收到通知(此通知可以以 linter 的形式(所有可选)出现,在创建 A 时出现语法错误,和/或在实例化时出现语法错误) A)

我觉得元类可以做到这一点,但在提到元怪物时已经可以听到悲惨的故事了。

还有其他建议吗?

最佳答案

首先关注您的主要问题:在创建使用 mixin 的类时如何知道是否缺少属性。事实上,Python 代码没有简单的方法,即使它具有自省(introspection)功能来了解方法内设置或需要哪些属性。

Linters 通过“作弊”来实现这一点:它们解析代码并从 Python 代码的“外部”而不是从“内部”静态地查看它。即:它们遵循源代码的文本,而通过在运行时使用内省(introspection),我们将函数作为对象,并且我们必须跟踪其字节码以查看它将使用哪些属性。

但是,如果您可以接受在类主体本身中声明所需的属性,我认为如果您合并需要类不知道的属性的方法,则可以使 linter 保持沉默,并且让 Python 尖叫。

我们将使用抽象基类提供的功能,并稍微扩展它们,以便必须使用 mixin 在类中覆盖所需的属性。

所以这里是,用一些样板来消除 linter 中的其他警告:

"""module doc"""


from abc import ABC, abstractmethod

# This thing works as an "abstract attribute": 
# The class can't be instantiated unless it is overriden
# in the class declaration:
AbstractField = lambda: property(abstractmethod(lambda s: s))

class Aa(ABC):
    """doc"""

    x = AbstractField()
    y = AbstractField()

    def b_b(self):
        """doc"""
        return self.x + self.y


class Bb(Aa):
    """doc"""
    x = 0
    y = 0

    def __init__(self):
        """doc"""
        print(self.b_b())

# Class 'Bb' can be declared, but if both x and y are not
# declared in the class body, instantiating it would raise
# a runtime error, due to the abstractclass mechanism:

b = Bb()

(env39) [gwidion@village tmp01]$ pylint -d R0903 module.py 

--------------------------------------------------------------------
Your code has been rated at 10.00/10 (previous run: 10.00/10, +0.00)

现在,由于您正在关注 linter 和类似的工具,这些工具有时会带来更多的麻烦,而不是它们的帮助,您可能会想要创建 也为这些属性提供适当的注释。

我不太精通正确的类型提示,但创建“属性”的联合和字段在运行时应包含的实际类型似乎可以完成这项工作(即:MyPy 运行时没有错误或警告,linter 确实如此)不显示错误,代码按预期工作):

"""module doc"""


from abc import ABC, abstractmethod as abstract
from typing import Union

ABIntField = Union[property, int]


AbstractField = lambda: property(abstract(lambda s: s))

class Aa(ABC):
    """doc"""

    x: ABIntField = AbstractField()
    y: ABIntField = AbstractField()

    def b_b(self):
        """doc"""
        return self.x + self.y


class Bb(Aa):
    """doc"""
    x = 0
    y = 0

    def __init__(self):
        """doc"""
        print(self.b_b())


b = Bb()

我试图想出其他方法来实现这一点 - 尽管有可能(如果很难)有一个元类(或一些 __init_subclass__ 代码)来找出所需的属性mixin 方法,无需在主体中声明它们,相反:让 linter 知道“那些属性已被覆盖”是不可能的。

上述方式是“显而易见的事情”。根据项目的不同,可能值得在 AbstractField 上打包更多功能,达到不需要 ABIntField 注释的程度。

关于python - 让 python mixins 使用更安全(并告诉 linter 闭嘴),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66973562/

相关文章:

python - 使用 Python 加载 XML 数据

Java反射,获取泛型类参数的名称

css - 嵌套 CSS 类?

带有变量的 PHP 模板类?

python - 为什么分配给多个目标(标识符/属性)会产生奇怪的结果?

python 将元组中的项目分组,不重复

python - 相同结构的示例 XML 的解析方式与完整 XML 不同

jquery - 两个(类)和(类)之间平滑过渡 :hover

c++ - C++ 对象如何处理被分配数组文字?

python - 如何从文件中打印值?