xia*_*o 啸 133 python oop properties python-decorators
我对如何@property在Python中使用感兴趣.我已经阅读了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
Run Code Online (Sandbox Code Playgroud)
我不知道从包装_x填充属性装饰器可以获得什么好处.为什么不实施为:
class C(object):
def __init__(self):
self.x = None
Run Code Online (Sandbox Code Playgroud)
我认为,属性功能在某些情况下可能会有用.但当?有人可以给我一些现实世界的例子吗?
谢谢.
小智 89
其他示例将是设置属性的验证/过滤(强制它们处于边界或可接受的范围内)以及对复杂或快速变化的术语的惰性评估.
隐藏在属性后面的复杂计算:
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
Run Code Online (Sandbox Code Playgroud)
验证:
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
Run Code Online (Sandbox Code Playgroud)
mou*_*uad 74
一个简单的用例是设置一个只读实例属性,因为你知道_x在python中使用一个下划线引导变量名通常意味着它是私有的(内部使用)但有时我们希望能够读取实例属性而不是写它所以我们可以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
Run Code Online (Sandbox Code Playgroud)
det*_*tly 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)
Run Code Online (Sandbox Code Playgroud)
这是在一个Web应用程序中,任何给定的页面视图可能只需要这种类型的一个特定属性,但底层对象本身可能有几个这样的属性 - 在构造中初始化它们将是浪费,并且属性允许我灵活属性是懒惰的,哪些不是.
属性只是一个字段的抽象,它可以让您更好地控制特定字段的操作方式和中间件计算.想到的几个用法是验证和先前的初始化和访问限制
@property
def x(self):
"""I'm the 'x' property."""
if self._x is None:
self._x = Foo()
return self._x
Run Code Online (Sandbox Code Playgroud)
是的,对于发布的原始示例,该属性将与仅具有实例变量"x"完全相同.
这是关于python属性的最好的事情.从外部看,它们的工作方式与实例变量完全相同!这允许您使用来自课外的实例变量.
这意味着您的第一个示例实际上可以使用实例变量.如果事情发生了变化,然后你决定改变你的实现并且属性很有用,那么属性的接口仍然与类外的代码相同. 从实例变量到属性的更改不会影响类外的代码.
许多其他语言和编程课程将指示程序员不应该暴露实例变量,而是使用"getters"和"setter"来从类外部访问任何值,甚至是问题中引用的简单情况.
使用多种语言(例如Java)的类外的代码
object.get_i()
#and
object.set_i(value)
#in place of (with python)
object.i
#and
object.i = value
Run Code Online (Sandbox Code Playgroud)
在实现类时,有许多"getter"和"setter"与第一个示例完全相同:复制一个简单的实例变量.这些getter和setter是必需的,因为如果类实现发生更改,则类外的所有代码都需要更改.但是python属性允许类外部的代码与实例变量相同.因此,如果添加属性或具有简单的实例变量,则不需要更改类外部的代码.因此,与大多数面向对象语言不同,对于您的简单示例,您可以使用实例变量而不是真正不需要的"getters"和"setter",知道如果您将来更改为属性,则使用的代码是安全的你的班级不需要改变.
这意味着如果存在复杂的行为,您只需要创建属性,对于非常常见的简单情况,如问题中所述,只需要一个简单的实例变量,您就可以使用实例变量.
与使用setter和getters相比,属性的另一个不错的功能是,它们允许您继续对属性使用OP =运算符(例如+ =,-=,* =等),同时仍保留任何验证,访问控制,缓存等设置者和获取者将提供。
例如,如果您Person使用setter setage(newage)和getter 编写了类,getage()则要增加年龄,您必须编写:
bob = Person('Robert', 25)
bob.setage(bob.getage() + 1)
Run Code Online (Sandbox Code Playgroud)
但是如果您age有财产,您可以写得更加简洁:
bob.age += 1
Run Code Online (Sandbox Code Playgroud)
阅读答案和评论,主题似乎是答案似乎缺少一个简单但有用的例子.我在这里包含了一个非常简单的例子,演示了@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")
Run Code Online (Sandbox Code Playgroud)
用法:
>>> distance = Distance()
>>> distance.in_metres = 1000.0
>>> distance.in_metres
1000.0
>>> distance.in_feet
3280.8399
>>> distance.in_parsecs
3.24078e-14
Run Code Online (Sandbox Code Playgroud)
对你的问题的简短回答是,在你的例子中,没有任何好处.您应该使用不涉及属性的表单.
属性存在的原因是,如果您的代码将来发生更改,并且您突然需要对数据执行更多操作:缓存值,保护访问权限,查询某些外部资源......无论如何,您可以轻松修改您的类以添加getter数据的setter和setter,无需更改接口,因此您不必在代码中的任何位置找到访问该数据的位置,也可以更改它.
| 归档时间: |
|
| 查看次数: |
68310 次 |
| 最近记录: |