【问题标题】:Accessing mangled member of another instance访问另一个实例的损坏成员
【发布时间】:2016-08-15 16:38:33
【问题描述】:

我使用这个逻辑来维护Object 实例的有向树:

class Object:
    def __init__(self, *parents: 'Object'):
        self.__parents = list(parents)

    @property
    def __ascendants(self):
        for parent in self.__parents:
            yield from parent.__ascendants
            yield parent

这段代码运行良好,但 PyLint 抱怨 __ascendants 是 parent 的受保护成员,对于 PyLint 来说,这是一个客户端类。

对于受保护的、未损坏的成员,这很好:我不应该访问这样的成员,因为它们可能会被 Object 子类覆盖。

但在这种情况下,由于属性被破坏,子类不可能覆盖它们,这就是为什么我允许自己甚至在外部对象上使用它们(提供给构造函数)。

TLDR ;我正在寻找一种方法让 PyLint 接受访问客户端子类的损坏属性,而不必每次都求助于#pylint: disable=protected-access,或者全局禁用警告。

看起来我可以使用astng 回调来注册MANAGER,并转换一个模块,以便PyLint 可以使用其他信息。但是,我只能添加存根成员(以便可以在没有警告的情况下使用动态添加的成员),我不确定我是否可以通过这种方式解决我的问题。

我也尝试添加assert isinstance(parent, Object),但无济于事。

编辑:

我能够编写代码使 PyLInt 不会引发 protected-access,而只会引发 bad-staticmethod-argument。我没有在这个特定的类中使用其他 staticmethod ,所以也许这是这个问题的一个可接受的答案:

class Object:
    def __init__(self, *parents: 'Object'):
        self.__parents = list(parents)

    @staticmethod
    def __get_ascendants(self: 'Object'):
        for parent in self.__parents:
            yield from self.__get_ascendants(parent)
            yield parent

编辑 2:(灵感来自 @shx2)

使用具有正确参数名称的 lambda 也会愚弄 Pylint:

class Object:
    def __init__(self, *parents: 'Object'):
        self.__parents = list(parents)

    @property
    def __ascendants(self):
        get_ascendants = lambda self: self.__ascendants

        for parent in self.__parents:
            yield from get_ascendants(parent)
            yield parent

编辑 3:因为名称不会从生成器表达式(或列表 omprehensions)中泄漏出来,所以也可以这样写:

from itertools import chain

class Object:
    def __init__(self, *parents: 'Object'):
        self.__parents = list(parents)

    @property
    def __ascendants(self):
        return chain(*(
           chain(self.__ascendants, (self, ))
           for self in self.__parents
        ))

【问题讨论】:

    标签: python oop code-analysis pylint private-members


    【解决方案1】:

    我正在寻找一种方法让 PyLint 接受访问客户端子类的损坏属性

    有办法欺骗pylint。

    一种方法是将parent伪装成self:

    @property
    def __ascendants(self):
        for parent in self.__parents:
            self = parent
            yield from self.__ascendants
            yield self
    

    另一个是间接访问属性,使用getattr。而不是:

    yield from parent.__ascendants
    

    做:

    yield from getattr(parent, '__ascendants')
    

    【讨论】:

    • 这相当于# pylint: disable=protected-access,但不方便的是它不会检查不存在的成员。
    • 啊,是的,这也有效!我实际上已经编辑了我的帖子,显示了我使用专用staticmethod 找到的另一种方式,它采用self(本质上与您的答案相似)。我不确定哪一个是最易读和/或最不老套的。你怎么看?
    • 嗯,两者都有些老套。 IMO 分配给self 的那个更好,因为它只处理变量名,而且只在一个地方。道理很简单:pylint 关心名字,所以我们改名字来愚弄它。使用staticmethod 的那个涉及到更多地方的更多更改(更改函数的签名,更改调用方式),并且更难破译它实现的目标。
    • 在重新绑定self(真正的)后,我没有找到任何使用其他属性的方法。如果我将其别名为另一个名称,pylint 会再次引发protected-access,除非我将其别名为cls 之类的名称,但这确实很笨拙且具有误导性。我能想到的唯一方法是在重新绑定之前从self 收集所有必需的方法,但这不适用于描述符。
    【解决方案2】:

    为什么要让ascendants 方法成为一个损坏的属性?如果您真的想使用如此复杂的继承并为每个超类保留多个parents 属性,则修改parents 将起作用。但是,似乎对 ascendants 函数的处理没有用处,因为它属于类,而不是对象本身:

    class Object(object):
        def __init__(self, parents):
            self.__parents = list(parents)
    
        def ascendants(self):
            for parent in self.__parents:
                yield from parent.ascendants()
                yield parent
    

    【讨论】:

    • 我只需要在Object. 上保留一个parents 属性,而不是在每个子类上。子类不需要访问父母、祖先和孩子。这个属性纯粹是这个类的内部属性,不需要(不应该)成为公共 API 的一部分,因为它只会被Object 上定义的其他方法使用。我不希望子类能够覆盖ascendants,这对他们来说不是很有用,因为他们无权访问_Object__parents。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-18
    • 1970-01-01
    • 2013-05-11
    相关资源
    最近更新 更多