关于python:以属性装饰器的下划线开头的变量

Variables starting with underscore for property decorator

我是Python的新手。所以,如果这是一个基本问题,请原谅我。我在网上研究过这个话题,但找不到解释。我用的是Python3.6分布。

我正在尝试为属性创建一个简单的getter和setter。我会带你经历我所犯的错误。

1
2
3
4
5
6
class Person:
    def __init__(self,name):
        self.name=name

bob = Person('Bob Smith')
print(bob.name)

这打印了我同意的第一个名字,我没有重写printgetattribute方法。而且,这里没有财产。这是为了测试基本代码是否有效。

让我们修改代码以添加属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Person:
    def __init__(self,name):
        self.name=name

    @property
    def name(self):
       "name property docs"
        print('fetch...')
        return self.name


bob = Person('Bob Smith')
print(bob.name)

一旦我用pycharm编写上述代码,就会得到一个黄色的灯泡图标,说明变量必须是私有的。我不明白理由。

忽略上面的内容,如果我运行上面的代码,我会得到:

1
2
3
4
Traceback (most recent call last):   File"C:\..., in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)   File"
<ipython-input-25-62e9a426d2a9>", line 2, in <module>
    bob = Person('Bob Smith')   File"
<ipython-input-24-6c55f4b7326f>", line 4, in __init__
    self.name=name AttributeError: can't set attribute

现在,我研究了这个主题,发现有两个修复方法(不知道为什么这样做有效):

修正1:将变量name改为_name

1
2
3
4
5
6
7
8
9
10
11
12
13
class Person:
    def __init__(self,name):
        self._name=name #Changed name to _name

    @property
    def name(self):
       "name property docs"
        print('fetch...')
        return self._name #Changed name to _name


bob = Person('Bob Smith')
print(bob.name)

这很好地工作,因为它正确地打印输出。

修正2:将属性名从name(self)改为_name(self),将变量名从_name改为name

1
2
3
4
5
6
7
8
9
10
11
12
13
class Person:
    def __init__(self,name):
        self.name=name #changed to name

    @property
    def _name(self): #Changed to _name
       "name property docs"
        print('fetch...')
        return self.name #changed to name


bob = Person('Bob Smith')
print(bob.name)

现在,这项工作按预期打印。

作为下一步,我使用decorator创建了settergetterdeleter属性。它们遵循上面描述的类似命名约定,即变量名或方法名的前缀_

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@_name.setter
def _name(self,value):
   "name property setter"
    print('change...')
    self.name=value

@_name.deleter
def _name(self):
    print('remove')
    del self.name


bob = Person('Bob Smith')
print(bob.name)
bob.name = 'Bobby Smith'
print(bob.name)
del bob.name

问题:我不太清楚为什么python 3.x强制向变量名或方法名添加_

根据带有public getter和private setter的python属性,前面和后面带有下划线的python属性有什么区别,以及https://www.python.org/dev/peps/pep-0008/命名约定,下划线前缀对于用户来说是一个弱指标,即该变量是一个私有变量,但是没有额外的机制(通过pyTHON,类似于Java所做的那样来检查或纠正这种行为。

所以,现在最大的问题是,为什么我需要用下划线来处理属性?我相信这些下划线前缀只是为了让用户知道这是一个私有变量。

我正在用卢茨的书来学习Python,上面的例子是从他的书中得到启发的。


让我们看一下代码修复1:

1
2
3
4
5
6
7
8
9
10
11
12
class Person:
    def __init__(self,name):
        self._name=name #Changed name to _name

    @property
    def name(self):
       "name property docs"
        print('fetch...')
        return self._name #Changed name to _name

bob = Person('Bob Smith')
print(bob.name)
  • 定义self._name = name—这是您的支持字段。
  • 您定义了一个方法def name(self),并将其属性为@property
  • 通过bob = Person('Bob Smith')创建类的实例

那你就去做print(bob.name)——你在这里打什么电话?

您的变量称为self._namebob.name()将调用"非属性"方法。为什么bob.name仍然有效?它是由@property decorator完成的。

如果你定义了:

1
2
3
4
5
def tata(self):
    print(self.name)  # also no () after self.name

bob = Person('Bob Smith')
bob.tata()

它还将调用@property方法,因为您可以通过'fetch...'输出进行检查。所以每次调用yourclassinstance.name都会通过@property访问器-这就是为什么你不能同时调用self.name的原因。

因此,如果您从def name(self)内部访问self.name,就会得到堆栈溢出

这是纯粹的观察——如果你想知道到底发生了什么,你必须检查@property的实现。在这里,您可能会获得更多的洞察力:@property decorator是如何工作的?或者python属性如何工作?