我对如何在Python中使用@property
感兴趣。我已经阅读了python文档和那里的例子,在我看来,它只是一个玩具代码:
class C(object):
def __init__(self):
self._x = None
@property
def x(self):
"""I'm the 'x' property."""
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
我不知道我可以从填充属性装饰器的_x
中获得什么好处。为什么不实施为:
class C(object):
def __init__(self):
self.x = None
我认为,属性功能在某些情况下可能会有用。但当?有人可以给我一些现实世界的例子吗?
感谢。
答案 0 :(得分:87)
其他示例包括验证/过滤集合属性(强制它们处于界限或可接受范围内)以及对复杂或快速变化的术语进行惰性评估。
隐藏在属性后面的复杂计算:
class PDB_Calculator(object):
...
@property
def protein_folding_angle(self):
# number crunching, remote server calls, etc
# all results in an angle set in 'some_angle'
# It could also reference a cache, remote or otherwise,
# that holds the latest value for this angle
return some_angle
>>> f = PDB_Calculator()
>>> angle = f.protein_folding_angle
>>> angle
44.33276
验证:
class Pedometer(object)
...
@property
def stride_length(self):
return self._stride_length
@stride_length.setter
def stride_length(self, value):
if value > 10:
raise ValueError("This pedometer is based on the human stride - a stride length above 10m is not supported")
else:
self._stride_length = value
答案 1 :(得分:74)
一个简单的用例是设置一个只读实例属性,因为你知道在python中引导一个带有一个下划线_x
的变量名通常意味着它是私有(内部使用)但是有时我们希望能够读取实例属性而不是编写它,以便我们可以使用property
:
>>> class C(object):
def __init__(self, x):
self._x = x
@property
def x(self):
return self._x
>>> c = C(1)
>>> c.x
1
>>> c.x = 2
AttributeError Traceback (most recent call last)
AttributeError: can't set attribute
答案 2 :(得分:20)
查看this article以获得非常实际的用途。简而言之,它解释了如何在Python中放弃显式的getter / setter方法,因为如果你在某个阶段需要它们,你可以使用property
来实现无缝实现。
答案 3 :(得分:15)
我用过它的一件事是缓存缓慢查找,但不变,存储在数据库中的值。这适用于您的属性需要计算或任何其他长期操作(例如数据库检查,网络通信)的情况,您只需要按需执行。
class Model(object):
def get_a(self):
if not hasattr(self, "_a"):
self._a = self.db.lookup("a")
return self._a
a = property(get_a)
这是在一个Web应用程序中,任何给定的页面视图可能只需要这种类型的一个特定属性,但底层对象本身可能有几个这样的属性 - 在构造中初始化它们将是浪费,属性允许我成为灵活,哪些属性是懒惰的,哪些属性不是。
答案 4 :(得分:7)
属性只是一个字段的抽象,它可以让您更好地控制特定字段的操作方式和中间件计算。我们想到的几个用法是验证和先前的初始化和访问限制
@property
def x(self):
"""I'm the 'x' property."""
if self._x is None:
self._x = Foo()
return self._x
答案 5 :(得分:6)
使用setter和getter的属性的另一个不错的功能,它允许你继续使用OP =运算符(例如+ =, - =,* =等) 属性,同时仍然保留了setter和getter将提供的任何验证,访问控制,缓存等。
例如,如果您使用setter Person
和getter setage(newage)
编写了课程getage()
,那么要增加您必须写的年龄:
bob = Person('Robert', 25)
bob.setage(bob.getage() + 1)
但是如果你把age
作为一个属性,你可以写得更干净:
bob.age += 1
答案 6 :(得分:6)
通过阅读答案和评论,主题似乎是答案似乎缺少一个简单但有用的例子。我在这里添加了一个非常简单的例子,演示了@property
装饰器的简单使用。这是一个允许用户使用各种不同单位指定和获取距离测量的类,即in_feet
或in_metres
。
class Distance(object):
def __init__(self):
# This private attribute will store the distance in metres
# All units provided using setters will be converted before
# being stored
self._distance = 0.0
@property
def in_metres(self):
return self._distance
@in_metres.setter
def in_metres(self, val):
try:
self._distance = float(val)
except:
raise ValueError("The input you have provided is not recognised "
"as a valid number")
@property
def in_feet(self):
return self._distance * 3.2808399
@in_feet.setter
def in_feet(self, val):
try:
self._distance = float(val) / 3.2808399
except:
raise ValueError("The input you have provided is not recognised "
"as a valid number")
@property
def in_parsecs(self):
return self._distance * 3.24078e-17
@in_parsecs.setter
def in_parsecs(self, val):
try:
self._distance = float(val) / 3.24078e-17
except:
raise ValueError("The input you have provided is not recognised "
"as a valid number")
用法:
>>> distance = Distance()
>>> distance.in_metres = 1000.0
>>> distance.in_metres
1000.0
>>> distance.in_feet
3280.8399
>>> distance.in_parsecs
3.24078e-14
答案 7 :(得分:5)
对你的问题的简短回答是,在你的例子中,没有任何好处。您应该使用不涉及属性的表单。
属性存在的原因是,如果您的代码将来发生变化,并且您突然需要对数据执行更多操作:缓存值,保护访问权限,查询某些外部资源......无论如何,您可以轻松修改您的类为数据添加getter和setter而不更改界面,因此您不必在代码中的任何位置找到访问该数据的位置并更改它。
答案 8 :(得分:5)
是的,对于发布的原始示例,该属性与仅具有实例变量' x'完全相同。
这是关于python属性的最好的事情。从外部看,它们的工作方式与实例变量完全相同!这允许您使用课外的实例变量。
这意味着您的第一个示例实际上可以使用实例变量。如果事情发生了变化,然后你决定改变你的实现并且属性很有用,那么属性的接口仍然与类外的代码相同。 从实例变量到属性的更改不会影响类外的代码。
许多其他语言和编程课程将指示程序员不应该公开实例变量,而是使用“getter”'和'塞特斯'对于从课外访问的任何值,甚至是问题中引用的简单案例。
使用多种语言(例如Java)的类外的代码
object.get_i()
#and
object.set_i(value)
#in place of (with python)
object.i
#and
object.i = value
在实施课程时,有许多“getter”'和'塞特斯'完全和你的第一个例子一样:复制一个简单的实例变量。这些getter和setter是必需的,因为如果类实现发生更改,则类外的所有代码都需要更改。 但是python属性允许类外部的代码与实例变量相同。因此,如果添加属性或具有简单的实例变量,则不需要更改类外部的代码。 因此,与大多数面向对象语言不同,对于您的简单示例, 可以 使用实例变量而不是“getter”'和'塞特斯'实际上并不需要,知道如果您将来更改为属性,使用您的类的代码不需要更改。
这意味着如果存在复杂的行为,您只需要创建属性,对于非常常见的简单情况,如问题中所述,只需要一个简单的实例变量,您就可以使用实例变量。 / p>
答案 9 :(得分:4)
一开始很多人都没注意到的是你可以创建自己的属性子类。我发现这对于公开只读对象属性或属性是非常有用的,你可以读写但不能删除。它也是一种很好的方式来包装跟踪对象字段的修改等功能。
class reader(property):
def __init__(self, varname):
_reader = lambda obj: getattr(obj, varname)
super(reader, self).__init__(_reader)
class accessor(property):
def __init__(self, varname, set_validation=None):
_reader = lambda obj: getattr(obj, varname)
def _writer(obj, value):
if set_validation is not None:
if set_validation(value):
setattr(obj, varname, value)
super(accessor, self).__init__(_reader, _writer)
#example
class MyClass(object):
def __init__(self):
self._attr = None
attr = reader('_attr')