问题:关于如何在python中使用属性功能的真实示例?
我对如何@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
我不知道通过包装_x
用属性装饰器填充的内容可以获得什么好处。为什么不只是实现为:
class C(object):
def __init__(self):
self.x = None
我认为,属性功能在某些情况下可能很有用。但当?有人可以给我一些真实的例子吗?
谢谢。
回答 0
其他示例是对设置的属性进行验证/过滤(强制将它们限制或接受)以及对复杂或快速变化的术语进行惰性评估。
隐藏在属性后面的复杂计算:
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
一个简单的用例是设置一个只读实例属性,因为您知道_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
回答 2
看一下这篇文章,将其用于非常实际的用途。简而言之,它解释了在Python中通常如何放弃显式的getter / setter方法,因为如果在某个阶段需要它们,则可以使用它property
来实现无缝实现。
回答 3
我使用它的一件事是缓存存储在数据库中的查找缓慢但不变的值。这会泛化到您的属性需要计算或您只想按需执行的其他长时间操作(例如数据库检查,网络通信)的任何情况。
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
通读答案和评论,主题似乎是答案似乎缺少一个简单但有用的示例。我在这里包括了一个非常简单的示例,演示了@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
回答 5
属性只是字段的抽象,它使您可以更好地控制特定字段的操作方式和中间件计算。想到的用法很少是验证,事先初始化和访问限制
@property
def x(self):
"""I'm the 'x' property."""
if self._x is None:
self._x = Foo()
return self._x
回答 6
是的,对于发布的原始示例,该属性的作用与仅具有实例变量“ x”的作用完全相同。
这是关于python属性的最好的事情。从外部看,它们的工作方式完全类似于实例变量!这允许您从类外部使用实例变量。
这意味着您的第一个示例实际上可以使用实例变量。如果情况发生了变化,然后您决定更改实现,并且一个属性很有用,则该类的代码与该类外部的代码的接口将相同。 从实例变量更改为属性不会影响类外部的代码。
许多其他语言和编程类将指示程序员不要公开实例变量,而应使用“ getters”和“ setters”从类外部访问任何值,即使是问题中引用的简单情况也是如此。
类外的代码使用多种语言(例如Java)
object.get_i()
#and
object.set_i(value)
#in place of (with python)
object.i
#and
object.i = value
在实现该类时,有许多“ getter”和“ setter”的作用与您的第一个示例完全相同:复制一个简单的实例变量。这些获取器和设置器是必需的,因为如果类实现更改,则该类外部的所有代码都需要更改。但是python属性允许类外的代码与实例变量相同。因此,如果添加属性或具有简单的实例变量,则无需更改类外部的代码。因此,与大多数面向对象的语言不同,对于您的简单示例,您可以使用实例变量代替真正不需要的“ getters”和“ setters”,因此请确保在将来更改为属性时,可以使用您的Class无需更改。
这意味着仅当行为复杂时才需要创建属性,对于非常普遍的简单情况(如问题中所述),仅需要一个简单的实例变量,您只需使用实例变量即可。
回答 7
与使用setter和getters相比,属性的另一个不错的功能是,它们允许您继续在属性上使用OP =运算符(例如,+ =,-=,* =等),同时仍保留任何验证,访问控制,缓存等设置者和获取者将提供。
例如,如果您Person
使用setter setage(newage)
和getter 编写了类,getage()
则要增加年龄,您必须编写:
bob = Person('Robert', 25)
bob.setage(bob.getage() + 1)
但是如果您age
有财产,您可以写得更加简洁:
bob.age += 1
回答 8
这个问题的简短答案是,在您的示例中,没有任何好处。您可能应该使用不包含属性的表格。
属性存在的原因是,如果您的代码在将来发生更改,并且您突然需要对数据做更多的事情:缓存值,保护访问,查询一些外部资源…等等,您可以轻松地修改类以添加getter和数据的设置器,而无需更改接口,因此您不必在代码中的任何地方找到要访问该数据的地方,也不必进行更改。
回答 9
起初许多人没有注意到的事情是您可以创建自己的属性子类。我发现这对于公开只读对象属性或可以读写但不能删除的属性非常有用。这也是包装诸如跟踪对对象字段的修改之类的功能的绝佳方法。
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')