类方法中的受保护属性

Protected attributes in classmethod

Python 约定建议在受保护属性的名称前加上下划线。据我所知,受保护的属性只能在给定的 class 及其子 class 中使用。当我尝试在替代初始化程序中使用受保护的属性时,您能否向我提供一些关于为什么 pylint returns protected-access 警告的直觉,例如

class Test(object):

    def __init__(self, name):
        self.name = name
        self._count = 0
        self._bound = 1  # max value

    @classmethod
    def specific_state(cls, name, bound):
        test = cls(name)
        test._bound = bound

我确实理解在这种特定情况下(如上例所示)我处理了一个对象的实例,但它仍在 class 定义中,因此从我的角度来看似乎没问题。 pylint在这件事上是不是太严谨了还是我理解有误?

在我看来,pylint这一点太过火了。我怀疑在这方面会有太多分歧。我不能代表 pylint 开发人员,但我 猜测 这更多是类型推断的问题,而不是一定被认为是他们的理想行为。


对于引用下划线开头的成员,不同的人可能会告诉你不同的看法。我个人的意见是

  • 如果在同一个模块中,公平游戏
    • 但是,您仍然应该尽可能地限制这种情况,以避免尽可能地将 class/function 实现捆绑在一起。
  • 如果成员属于不同的模块,则不再是公平游戏,应将其视为实现细节。
    • 在这里将实现捆绑在一起本质上是危险的,因为对一个实现的更改很容易在没有警告的情况下破坏其他代码。

所以这意味着如果我在 foo.py 中定义了 class Foo(成员 _member)并且如果我创建一个子 class Foo(我们称它为 Bar)在 bar.py 中,我认为 Bar 不应明确引用 _member 属性。但是,如果您将 Bar 移动到 foo.py,那么就可以了。事实上,class Baz(也在 foo.py 中定义)也应该被允许依赖于 Foo 的内部——但这样做最好有充分的理由.

大多数情况下,我不同意将下划线前缀成员视为 "protected"(在 Java 意义上)。我认为它们应该被视为 "implementation details"。 Protected 在 Java 中工作得更好,因为如果你更改实现(例如删除受保护的成员),代码将在编译时失败并让你知道你的 subclass 依赖的受保护成员是不再存在。 Python 没有这样的内置安全措施。

因为下划线前缀名称是 "implementation details",如果它是相同的 class(就像你在问题中描述的情况),那么它是相同的实现,所以(对我来说)允许访问这些成员是显而易见的。我会 # pylint: disable=protected-access 不假思索 :-)。

来自 pylint.checkers.classes.ClassChecker 中的 _check_protected_attribute_access 方法:

'''Given an attribute access node (set or get), check if attribute
access is legitimate. Call _check_first_attr with node before calling
this method. Valid cases are:
* self._attr in a method or cls._attr in a classmethod. Checked by
_check_first_attr.
* Klass._attr inside "Klass" class.
* Klass2._attr inside "Klass" class when Klass2 is a base class of
    Klass.
'''

您可以看到您的案例不在上述案例中。因此可以假设 protected-access 警告是合法的。

但是:

  1. pylints 代码本身发生了完全相同的情况。 (例如 exceptions.py:338):

    # pylint: disable=protected-access
    exc = exc._proxied
    

    所以基本上他们在那种情况下禁用了他们的检查器。

  2. 在c++中以下代码有效:

            class X
    {
    private:
        int a;
    protected:
        int b;
    public:
        static void staticMethod(X& x)
        {
            x.a = 1;
            x.b = 1;
        }
    };
    

    因此在静态方法中访问 protected/private 成员是有效的。

我会说 pylint 在这种情况下过于敏感。您可以使用评论来禁用那里的检查器。产生警告的原因可能是静态检查器很难实现更复杂的行为。