wat*_*wer 6 python properties decorator python-3.x python-decorators
我是Python的新手.所以,如果这是一个基本问题,请原谅我.我在互联网和SO上研究过这个话题,但我找不到解释.我正在使用Anaconda 3.6发行版.
我正在尝试为属性创建一个简单的getter和setter.我将引导你完成我得到的错误.
class Person:
def __init__(self,name):
self.name=name
bob = Person('Bob Smith')
print(bob.name)
Run Code Online (Sandbox Code Playgroud)
这打印出我同意的第一个名字,我没有覆盖print或getattribute方法.此外,这里没有房产.这是为了测试基本代码是否有效.
让我们修改代码来添加属性:
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)
Run Code Online (Sandbox Code Playgroud)
一旦我在PyCharm中编写上面的代码,我就会得到一个黄色的灯泡图标,说明该变量必须是私有的.我不明白其理由.
忽略上面,如果我运行上面的代码,我得到:
Run Code Online (Sandbox Code Playgroud)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)
Run Code Online (Sandbox Code Playgroud)
这很好用,因为它正确打印输出.
修复#2:将 属性名称更改为to 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)
Run Code Online (Sandbox Code Playgroud)
现在,这可以按预期打印.
作为下一个步骤,我创建setter,getter和deleter使用装饰性质.它们遵循如上所述的类似命名约定 - 即_变量名称的前缀或方法名称:
@_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
Run Code Online (Sandbox Code Playgroud)
问题:我不确定为什么Python 3.x强制添加_变量名称或方法名称.
按照公共getter和setter方法私有财产的Python,什么是蟒蛇的区别在前面加上下划线属性和背部,并https://www.python.org/dev/peps/pep-0008/#naming-conventions,一下划线前缀是用户的一个弱指示,即该变量是一个私有变量,但是没有额外的机制(通过Python,类似于Java所做的)来检查或纠正这种行为.
所以,现在最大的问题是,为什么我需要使用属性的下划线?我相信那些下划线前缀只是让用户知道这是一个私有变量.
我正在使用Lutz的书来学习Python,上面的例子来自他的书.
让我们使用修复 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)
Run Code Online (Sandbox Code Playgroud)
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()
Run Code Online (Sandbox Code Playgroud)
它还会调用您的 @property 方法,您可以通过'fetch...'输出进行检查。因此,每次调用都yourclassinstance.name将通过 @property 访问器 - 这就是为什么你不能将self.name“变量”与它一起使用。
self.name如果你从内部访问def name(self)- 你会得到一个循环调用 - 因此:堆栈溢出。
这是纯粹的观察 - 如果您想了解到底发生了什么,您必须检查@property实施情况。
您可以在这里更深入地了解这些主题:
正如评论中指出的,使用 getter/setter 是一种反模式,除非它们实际上做了一些事情:
class Person:
"""Silly example for properties and setter/deleter that do something."""
def __init__(self,name):
self._name = name # bypass name setter by directly setting it
self._name_access_counter = 0
self._name_change_counter = 0
self._name_history = [name]
@property
def name(self):
"""Counts any access and returns name + count"""
self._name_access_counter += 1
return f'{self._name} ({self._name_access_counter})'
@name.setter
def name(self, value):
"""Allow only 3 name changes, and enforce names to be CAPITALs"""
if value == self._name:
return
new_value = str(value).upper()
if self._name_change_counter < 3:
self._name_change_counter += 1
print(f'({self._name_change_counter}/3 changes: {self._name} => {new_value}')
self._name_history.append(new_value)
self._name = new_value
else:
print(f"no change allowed: {self._name} => {new_value} not set!")
@name.deleter
def name(self):
"""Misuse of del - resets counters/history for example purposes"""
self._name_access_counter = 0
self._name_change_counter = 0
self._name_history = self._name_history[:1] # keep initial name
self._name = self._name_history[0] # reset to initial name
print("deleted history and reset changes")
@property
def history(self):
return self._name_history
Run Code Online (Sandbox Code Playgroud)
用法:
p = Person("Maria")
print(list(p.name for _ in range(5)))
for name in ["Luigi", "Mario", 42, "King"]:
p.name = name
print(p.name) # counter will count ANY get access
print(p.history)
del (p.name)
print(p.name)
print(p.history)
Run Code Online (Sandbox Code Playgroud)
输出:
# get 5 times and print as list
['Maria (1)', 'Maria (2)', 'Maria (3)', 'Maria (4)', 'Maria (5)']
# try to change 4 times
(1/3 changes: Maria => LUIGI
LUIGI (6)
(2/3 changes: LUIGI => MARIO
MARIO (7)
(3/3 changes: MARIO => 42
42 (8)
no change allowed: 42 => KING not set!
42 (9)
# print history so far
['Maria', 'LUIGI', 'MARIO', 'KING']
# delete name, print name and history after delete
deleted history and reset changes
Maria (1)
['Maria']
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
615 次 |
| 最近记录: |