我这样做:
def set_property(property,value):
def get_property(property):
或者object.property = value
value = object.property
如何在Python中使用getter和setter的Pythonic方式?
我这样做:
def set_property(property,value):
def get_property(property):
或者object.property = value
value = object.property
如何在Python中使用getter和setter的Pythonic方式?
试试这个:Python 属性
示例代码如下:
class C(object):
def __init__(self):
self._x = None
@property
def x(self):
"""I'm the 'x' property."""
print("getter of x called")
return self._x
@x.setter
def x(self, value):
print("setter of x called")
self._x = value
@x.deleter
def x(self):
print("deleter of x called")
del self._x
c = C()
c.x = 'foo' # setter called
foo = c.x # getter called
del c.x # deleter called
如何使用Pythonic的方式来使用getter和setter?
“Pythonic”的方式不是使用“getter”和“setter”,而是像问题所示使用普通属性,并使用del
删除(但名称已更改以保护无辜者...内置函数):
value = 'something'
obj.attribute = value
value = obj.attribute
del obj.attribute
property
装饰器而无需更改用户代码:class Obj:
"""property demo"""
#
@property # first decorate the getter method
def attribute(self): # This getter method name is *the* name
return self._attribute
#
@attribute.setter # the property decorates with `.setter` now
def attribute(self, value): # name, e.g. "attribute", is the same
self._attribute = value # the "value" name isn't special
#
@attribute.deleter # decorate with `.deleter`
def attribute(self): # again, the method name is the same
del self._attribute
obj = Obj()
obj.attribute = value
the_value = obj.attribute
del obj.attribute
def set_property(property,value):
def get_property(property):
首先,上述代码无法正常工作,因为你没有为属性设置实例提供参数(通常是self
),正确的写法应该是:class Obj:
def set_property(self, property, value): # don't do this
...
def get_property(self, property): # don't do this either
...
__setattr__
和__getattr__
的目的。setattr
和getattr
。setattr(object, 'property_name', value)
getattr(object, 'property_name', default_value) # default is optional
@property
装饰器用于创建getter和setter。
例如,我们可以修改设置行为以限制所设置的值:
class Protective(object):
@property
def protected_value(self):
return self._protected_value
@protected_value.setter
def protected_value(self, value):
if acceptable(value): # e.g. type or range check
self._protected_value = value
property
而直接使用属性。这是Python用户所期望的。按照最小惊讶原则,除非你有非常充分的理由,否则应该尽量满足用户的期望。class Protective(object):
"""protected property demo"""
#
def __init__(self, start_protected_value=0):
self.protected_value = start_protected_value
#
@property
def protected_value(self):
return self._protected_value
#
@protected_value.setter
def protected_value(self, value):
if value != int(value):
raise TypeError("protected_value must be an integer")
if 0 <= value <= 100:
self._protected_value = int(value)
else:
raise ValueError("protected_value must be " +
"between 0 and 100 inclusive")
#
@protected_value.deleter
def protected_value(self):
raise AttributeError("do not delete, protected_value can be set to 0")
__init__
引用 self.protected_value
,但属性方法引用 self._protected_value
。这样做是为了让 __init__
通过公共 API 使用属性,确保它是“受保护的”。)>>> p1 = Protective(3)
>>> p1.protected_value
3
>>> p1 = Protective(5.0)
>>> p1.protected_value
5
>>> p2 = Protective(-5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __init__
File "<stdin>", line 15, in protected_value
ValueError: protectected_value must be between 0 and 100 inclusive
>>> p1.protected_value = 7.3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 17, in protected_value
TypeError: protected_value must be an integer
>>> p1.protected_value = 101
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 15, in protected_value
ValueError: protectected_value must be between 0 and 100 inclusive
>>> del p1.protected_value
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 18, in protected_value
AttributeError: do not delete, protected_value can be set to 0
是的,它们确实很重要。 .setter
和.deleter
会复制原始属性。这使得子类能够适当地修改行为而不更改父级的行为。
class Obj:
"""property demo"""
#
@property
def get_only(self):
return self._attribute
#
@get_only.setter
def get_or_set(self, value):
self._attribute = value
#
@get_or_set.deleter
def get_set_or_delete(self):
del self._attribute
为了让这个工作正常运行,你必须使用相应的名称:
obj = Obj()
# obj.get_only = 'value' # would error
obj.get_or_set = 'value'
obj.get_set_or_delete = 'new value'
the_value = obj.get_only
del obj.get_set_or_delete
# del obj.get_or_set # would error
我不确定这个用法有什么用处,但如果你想要一个只能进行获取、设置、删除操作的属性,可以使用该方法。最好保持语义相同的属性名称相同。
从简单的属性开始。
如果以后需要设置、获取和删除功能,则可以使用 property 装饰器添加它。
避免使用名称为 set_...
和 get_...
的函数,因为这就是属性的作用。
__init__
方法引用了 self.protected_value
,但是 getter 和 setter 引用了 self._protected_value
。您能否解释一下这是如何工作的?我测试了您的代码,发现它可以正常运行 - 所以这不是一个错别字。 在您的示例代码中,self.protected_value
实际上是对 _protected_value
的访问,它们两者是等效的。Python 中的命名约定是使用前导下划线来表示一个变量或方法是受保护的,但并不会阻止对其进行访问。因此,在 __init__
方法中,您可以使用 self.protected_value
来初始化 _protected_value
。而在 getter 和 setter 方法中,使用 _protected_value
是更常见的写法,因为这些方法通常也是受保护的,并且使用前导下划线更符合 Python 的约定。 - codeforester__init__
中使用非公共API而不是使用属性来“保护”它,那么这是没有意义的。 - Russia Must Remove Putinself.protected_value = start_protected_value
实际上是在调用setter函数;我以为这只是一个赋值语句。 - codeforesterIn [1]: class test(object):
def __init__(self):
self.pants = 'pants'
@property
def p(self):
return self.pants
@p.setter
def p(self, value):
self.pants = value * 2
....:
In [2]: t = test()
In [3]: t.p
Out[3]: 'pants'
In [4]: t.p = 10
In [5]: t.p
Out[5]: 20
使用@property
和@attribute.setter
不仅可以帮助您使用“Python式”的方式,还可以在创建对象和修改对象时检查属性的有效性。
class Person(object):
def __init__(self, p_name=None):
self.name = p_name
@property
def name(self):
return self._name
@name.setter
def name(self, new_name):
if type(new_name) == str: #type checking for name property
self._name = new_name
else:
raise Exception("Invalid value for name")
通过这种方式,您实际上可以将_name
属性从客户端开发人员中“隐藏”,并对名称属性类型进行检查。请注意,即使在初始化期间也会调用setter方法。因此:
p = Person(12)
将导致:
Exception: Invalid value for name
但是:
>>>p = person('Mike')
>>>print(p.name)
Mike
>>>p.name = 'George'
>>>print(p.name)
George
>>>p.name = 2.3 # Causes an exception
self.name
应该改为 self._name
,而且 p = person('Mike')
应该改为 p = Person('Mike')
。编辑队列已满... 干杯! - lcnittl__init__
方法中将self.name
更改为self._name
,则在对象实例化时我们将失去验证。这样,Person(12)
就可以被创建(尽管之后无法更新为另一个数字作为名称)。 - Farzad Vertigo这是一个旧问题,但主题非常重要且一直很受关注。如果有人想超越简单的getter/setter,我已经撰写了一篇关于在Python中实现超强属性的文章,支持插槽、可观察性和减少样板代码。
这是一个旧问题,但主题非常重要且一直很受关注。如果有人想超越简单的getter/setter,我已经撰写了一篇关于在Python中实现超强属性的文章,支持插槽、可观察性和减少样板代码。from objects import properties, self_properties
class Car:
with properties(locals(), 'meta') as meta:
@meta.prop(read_only=True)
def brand(self) -> str:
"""Brand"""
@meta.prop(read_only=True)
def max_speed(self) -> float:
"""Maximum car speed"""
@meta.prop(listener='_on_acceleration')
def speed(self) -> float:
"""Speed of the car"""
return 0 # Default stopped
@meta.prop(listener='_on_off_listener')
def on(self) -> bool:
"""Engine state"""
return False
def __init__(self, brand: str, max_speed: float = 200):
self_properties(self, locals())
def _on_off_listener(self, prop, old, on):
if on:
print(f"{self.brand} Turned on, Runnnnnn")
else:
self._speed = 0
print(f"{self.brand} Turned off.")
def _on_acceleration(self, prop, old, speed):
if self.on:
if speed > self.max_speed:
print(f"{self.brand} {speed}km/h Bang! Engine exploded!")
self.on = False
else:
print(f"{self.brand} New speed: {speed}km/h")
else:
print(f"{self.brand} Car is off, no speed change")
这个类可以像这样使用:
mycar = Car('Ford')
# Car is turned off
for speed in range(0, 300, 50):
mycar.speed = speed
# Car is turned on
mycar.on = True
for speed in range(0, 350, 50):
mycar.speed = speed
这段代码将产生以下输出:
Ford Car is off, no speed change
Ford Car is off, no speed change
Ford Car is off, no speed change
Ford Car is off, no speed change
Ford Car is off, no speed change
Ford Car is off, no speed change
Ford Turned on, Runnnnnn
Ford New speed: 0km/h
Ford New speed: 50km/h
Ford New speed: 100km/h
Ford New speed: 150km/h
Ford New speed: 200km/h
Ford 250km/h Bang! Engine exploded!
Ford Turned off.
Ford Car is off, no speed change
更多信息关于如何以及为什么在这里:https://mnesarco.github.io/blog/2020/07/23/python-metaprogramming-properties-on-steroids
属性非常有用,因为您可以将其与赋值一起使用,但也可以包括验证。您可以看到此代码,其中使用装饰器@property和@property_name.setter创建方法:
# Python program displaying the use of @property
class AgeSet:
def __init__(self):
self._age = 0
# using property decorator a getter function
@property
def age(self):
print("getter method called")
return self._age
# a setter function
@age.setter
def age(self, a):
if(a < 18):
raise ValueError("Sorry your age is below eligibility criteria")
print("setter method called")
self._age = a
pkj = AgeSet()
pkj.age = int(input("set the age using setter: "))
print(pkj.age)
我在这篇文章中写了更多细节:https://pythonhowtoprogram.com/how-to-create-getter-setter-class-properties-in-python-3/
你可以使用访问器/修改器(即@attr.setter
和@property
),也可以不使用,但最重要的是保持一致性!
如果你仅仅是使用@property
来访问属性,例如:
class myClass:
def __init__(a):
self._a = a
@property
def a(self):
return self._a
使用它来访问每个*属性!使用@property
访问一些属性而留下其他一些属性公共(即没有下划线的名称)没有访问器是不好的实践,例如不要这样做
class myClass:
def __init__(a, b):
self.a = a
self.b = b
@property
def a(self):
return self.a
self.b
没有明确的访问器。@attribute.setter
,但请保持一致!当您执行例如:class myClass:
def __init__(a, b):
self.a = a
self.b = b
@a.setter
def a(self, value):
return self.a = value
*例外情况是当您显式想要使某些变量可访问或可变但不是这两者或者希望在访问或修改属性时执行一些其他逻辑。这是我个人使用@property和@attribute.setter(否则不需要公共属性的显式访问器/ mutators)。
最后,PEP8和Google Style Guide建议:
对于简单的公共数据属性,最好仅暴露属性名称,而不使用复杂的访问器/ mutator方法。请记住,Python提供了一条易于实现的路径,以便在发现简单数据属性需要增加功能行为时进行未来增强。在这种情况下,使用属性将函数实现隐藏在简单数据属性访问语法背后。
另一方面,根据Google Style GuidePython语言规则/属性的建议是:
在新代码中使用属性来访问或设置您通常会使用简单轻量级的访问者或setter方法的数据。属性应该用@property装饰器创建。
此方法的优点:
通过消除简单属性访问的显式获取和设置方法调用来提高可读性。允许计算变得懒惰。被认为是维护类接口的Pythonic方式。在性能方面,当直接访问变量是合理的时,允许属性绕过需要微不足道的访问器方法。这还允许在不破坏接口的情况下添加访问器方法。
缺点:
必须从
object
继承Python 2。可以隐藏副作用,就像操作符重载一样。对子类可能会产生困惑。
@property
计算其中一个属性,那么让其他属性也使用@property
似乎是一个不明智的决定。 - Quelklefproperty
装饰器的整个要点就是不需要编写样板getter和setter。 Python不是Java。此外,从Google建议提供的示例中可以看出,他们谈论的是您的getter实际上正在做某事的情况,而不仅仅是 return self._some_var
。因此,这与PEP8并不矛盾。 - juanpa.arrivillagavalid_if_not=self.is_finalized)
。对于糟糕的属性,您每次都必须进行lambda转换。此外,可以将显式设置组合在序列中,例如:.set_name('asd').set_value(3).
对于糟糕的属性,您无法这样做。因此不要使用属性。 - iperov__getattribute__
和__setattr__
。class MyClass:
def __init__(self, attrvalue):
self.myattr = attrvalue
def __getattribute__(self, attr):
if attr == "myattr":
#Getter for myattr
def __setattr__(self, attr):
if attr == "myattr":
#Setter for myattr
请注意,__getattr__
和__getattribute__
不是同一个东西。当属性未被找到时,只会调用__getattr__
。
class ChangingPassword(object):
def __init__(self, username, password):
"""use _ for change to read only type(protected)."""
self.username = username
self._password = password
def username(self):
return self.username
@property
def password(self):
return self._password
@password.setter
def password(self, new_password: int):
if isinstance(new_password, int):
if self._password != new_password:
self._password = new_password
else:
raise ValueError('Enter different value!')
user01 = ChangingPassword('Herment', 1321)
print(user01.password)
user01.password = 6301
print(user01.password)
._x
的引用(它只是一个普通的属性而不是属性),会绕过property
封装。只有对.x
的引用会经过property
。 - ShadowRanger