关于如何在python中使用属性function的真实世界的例子?
我感兴趣的是如何在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
我认为,在某些情况下,属性function可能会有用。 但当? 有人能给我一些真实的例子吗?
谢谢。
其他的例子是集合属性的validation/过滤(迫使它们处于边界或可接受的范围内)以及对复杂或快速变化的术语的惰性评估。
隐藏在属性后面的复杂计算:
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
validation:
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
一个简单的用例就是设置一个只读实例属性,就像你知道在python中用一个下划线_x
引用一个variables名通常意味着它是私有的 (内部使用),但是有时我们希望能够读取实例属性而不是写它,所以我们可以使用这个property
:
>>> class C(object): def __init__(self, x): self._x = x @property def x(self): return self._x >>> c = C(1) >>> cx 1 >>> cx = 2 AttributeError Traceback (most recent call last) AttributeError: can't set attribute
看看这篇文章是非常实用的。 简而言之,它解释了在Python中,通常可以使用明确的getter / setter方法,因为如果在某个阶段需要它们,可以使用property
来实现无缝实现。
有一件事我用它caching缓慢查找,但不变,存储在数据库中的值。 这可以概括为任何需要计算或其他长时间操作(例如数据库检查,networking通信)的情况。
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应用程序中,任何给定的页面视图可能只需要一个这样的特定属性,但底层对象本身可能有几个这样的属性 – 初始化他们所有的build设将是浪费,属性让我灵活属性是懒惰的,哪些不是。
属性只是一个领域的抽象,它使您可以更好地控制特定领域的操作方式和中间件计算。 很less有用户想到的是validation和事先初始化和访问限制
@property def x(self): """I'm the 'x' property.""" if self._x is None: self._x = Foo() return self._x
你的问题的简短答案是,在你的例子中,没有任何好处。 您应该使用不涉及属性的表单。
属性存在的原因是,如果你的代码将来会改变,你突然需要做更多的事情:caching值,保护访问权限,查询一些外部资源…无论如何,你可以很容易地修改你的类来添加getters和设置数据而不改变接口,所以你不必在你的代码中find访问数据的地方,也可以改变它。
是的,对于最初的示例,该属性将与仅具有实例variables“x”的工作完全相同。
这是关于python属性的最好的东西。 从外部看,它们的工作方式与实例variables完全相同! 它允许你使用类之外的实例variables。
这意味着你的第一个例子可以实际使用一个实例variables。 如果事情发生了变化,然后你决定改变你的实现,并且属性是有用的,那么属性的接口仍然会和类之外的代码相同。 从实例variables到属性的更改对类外的代码没有影响。
许多其他语言和编程课程将指示程序员不应该公开实例variables,而是使用“getters”和“setter”来从课程外部访问任何值,即使是在问题中引用的简单情况。
使用多种语言(例如Java)使用类之外的代码
object.get_i() #and object.set_i(value) #in place of (with python) object.i #and object.i = value
而当实现这个类时,有许多“getters”和“setter”就像你的第一个例子一样:复制一个简单的实例variables。 这些getter和setter是必需的,因为如果类实现改变了,那么这个类之外的所有代码都需要改变。 但python属性允许类外的代码与实例variables相同。 所以,如果你添加一个属性或者有一个简单的实例variables,那么类外的代码就不需要改变。 因此,与大多数面向对象的语言不同的是,对于您的简单示例,您可以使用实例variables而不是实际不需要的“getters”和“setters”,因为知道如果将来更改属性,你的课不需要改变。
这意味着如果存在复杂的行为,则只需要创build属性;对于常见的简单情况,如问题中所述,只需要一个简单的实例variables,就可以使用实例variables。
起初没有注意到的是,你可以制造属于自己的子类。 这我发现非常有用的暴露只读对象的属性或属性,你可以读写,但不能删除。 这也是一个很好的方式来包装跟踪修改对象字段的function。
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')
另一个使用setter和getters的属性,它允许你继续使用OP =操作符(例如+ =, – =,* = etc),同时仍然保留任何validation,访问控制,caching等制定者和获得者将提供。
例如,如果您使用setter setage(newage)
和getter getage()
编写了Person
类,那么要增加您需要编写的年龄:
bob = Person('Robert', 25) bob.setage(bob.getage() + 1)
但是如果你把age
变成财产,你可以写出更清洁的东西:
bob.age += 1
阅读答案和评论,主题似乎是答案似乎缺less一个简单而有用的例子。 我在这里包含了一个非常简单的@property
,演示了@property
装饰器的简单使用。 这是一个允许用户使用各种不同的单位(即in_feet
或in_metres
指定和获取距离测量值的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