以底线开头的属性装饰器变量

时间:2018-07-14 17:01:51

标签: python python-3.x properties decorator python-decorators

我是Python的新手。因此,如果这是一个基本问题,请原谅我。我在Internet和SO上对此主题进行了研究,但找不到解释。我正在使用Anaconda 3.6发行版。

我正在尝试为属性创建一个简单的getter和setter。我将引导您解决我遇到的错误。

class Person:
    def __init__(self,name):
        self.name=name

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

这会打印出我同意没有覆盖printgetattribute方法的名字。另外,这里没有财产。这是为了测试基本代码是否有效。

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

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中编写以上代码后,立即得到一个黄色的灯泡图标,指出该变量必须是私有的。我不明白这个理由。

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

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

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

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)

现在,可以正常打印了。

下一步,我使用装饰器创建了settergetterdeleter属性。它们遵循如上所述的类似命名约定-即在变量名或方法名前加上_前缀:

@_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强制将_添加到变量名或方法名。

根据Python property with public getter and private setterWhat is the difference in python attributes with underscore in front and backhttps://www.python.org/dev/peps/pep-0008/#naming-conventions,下划线前缀对用户来说是一个较弱的指示,表明该变量是私有变量,但是没有适当的额外机制(通过Python,类似于Java所做的)来检查或纠正这种行为。

因此,眼前的大问题是,为什么在使用属性时需要加下划线?我相信这些下划线前缀只是让用户知道这是一个私有变量。


我正在使用Lutz的书来学习Python,上面的示例是从他的书中得到启发的。

1 个答案:

答案 0 :(得分:1)

让我们来修改代码1:

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._name-bob.name()会调用“非属性”方法。.为什么bob.name仍然有效-由@property装饰器完成。

如果定义,会发生什么情况

def tata(self):
    print(self.name)  # also no () after self.name

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

当您可以通过'fetch...'输出进行检查时,它还会调用@property方法。因此,每次yourclassinstance.name的调用都会通过@property访问器-这就是为什么您无法与self.name一起使用的原因。

因此,如果您从def name(self)内部访问self.name,则会导致堆栈溢出

这是纯粹的观察-如果您想查看实际发生的情况,则必须检查@property的实现。您可能会在这里获得更多的见识:How does the @property decorator work?How do Python properties work?