考虑以下课程:
class MyObject(object):
__slots__ = ('_att1', '_att2')
def __init__(self):
self._att1 = None
self._att2 = None
@property
def att1(self):
"""READ-ONLY property. """
return self._att1
@property
def att2(self):
"""att2 property description. """
return self._att2
@att2.setter
def att2(self, val):
self._att2 = val
使用属性装饰器的一个优点是我们可以添加一些文档
a = MyObject()
help(a)
Help on MyObject in module __main__ object:
class MyObject(__builtin__.object)
| Methods defined here:
|
| __init__(self)
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| att1
| READ-ONLY property.
|
| att2
| att2 property description.
如果该类要由最终用户使用,则可以访问这些属性 通过使用属性。
a.att2 = "new value"
但是,如果我要从MyObject
派生一个类,在派生类中使用“protected”变量是否可以接受?也就是说,
class Derived(MyObject):
__slots__ = ()
def __init__(self):
self._att1 = 1
self._att2 = 0
@property
def att2(self):
"""att2 adds 1 to itself in every call. """
self._att2 += 1
return self._att2
@att2.setter
def att2(self, val):
self._att2 = val
我知道如果MyObject
是来自第三方的对象,那么带有下划线的属性可能会发生变化,因此如果他们做了更改,我的代码就会中断。但是,由于我使用MyObject
作为基类,我认为可以将它与我自己的派生类一起使用。
我最近开始使用pylint
,这让我意识到我在派生类中使用“受保护”变量这一事实。我决定在这里发布一个问题的原因是要知道这是否可以接受,以便我可以在pylint
中禁止这些警告。如果不是,那么处理这个问题的标准是什么?
我想提出的另一点是关于属性访问。什么会更快
a.att1
或
a._att1
我的印象是,通过执行a.att1
python,首先会查看对象字典,或插槽,就像我正在使用的示例中一样。如果在查找函数时没有找到它(就像__getattr__
的情况那样)。在我的类定义中进行大量计算时,我宁愿访问位于字典或插槽中的内容,而不是我使用装饰器定义的内容。这在python社区中被认为是不好的做法吗?我只是问这个问题,因为pylint
的默认配置告诉了我,我想继续努力保持良好的标准。
让我们尽量不要讨论__slots__
和过早优化。如果可能的话
假设在我的原始帖子中我没有使用__slots__
并且所有内容都在对象的字典中。
答案 0 :(得分:5)
带下划线的属性不是“受保护”,而是“私有” - 它们可能会发生变化。即使你的班级是从它衍生出来的。您应该使用父属性来访问此数据。
谈到表现。当然,属性访问比属性访问慢一点,因为它涉及函数调用,但您不应该关心它。顺便说一句,它与__getattr__
和所有这些东西无关。在字典中也会将属性作为普通属性查找,它们只是实现descriptor protocol。
答案 1 :(得分:4)
简短回答:“过早优化是万恶之源。”
信任pylint。看看你是否确实遇到了性能问题。如果是的话,配置文件,而不是瓶颈。更改循环或数据结构可能更有效。
答案 2 :(得分:1)
Python在私有和受保护的类成员之间没有正式的区别。即使私人和公共之间的区别也相当薄弱,因为如果用户努力尝试,任何东西都可以被访问。如果私有与受保护的区别对您的应用程序有意义,您可以简单地记录_foo
(或foo
)不是公共用户界面的一部分,但子类可以依赖它来始终拥有某些语义。 (那些语义是由你决定的。)
现在,我不知道为一个简单的只读属性执行此操作是否值得文档工作(函数调用开销可能不是太糟糕,因此子类可以像其他人一样使用该属性)。但是,如果你有一个属性做了很多工作,比如数据库查询或HTTP请求,那么为子类暴露一些内容以获得他们需要的部分可能会有一些意义。
如果您认为需要公开“受保护”值,唯一的问题是您是否应该将下划线放在变量名称前面。下划线对诸如dir
之类的内容以及可能在文档生成工具中有一些影响,但它并没有改变代码运行的方式。
所以它确实是代码风格的问题,无论如何它都无关紧要。您可以关闭下划线并在文档中放置大警告文本,以便用户知道他们不应该弄乱内部,或者您可以使用下划线并使pylint警告静音(如果有必要,强制有一些警告)子类实现者的额外文档。)