关于如何在python中使用属性功能的真实示例?

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)

  • 可能,从专业角度来看,这些都是非常好的例子.但是,作为一个noobie,我发现这个例子非常无效.我的错 ... :( (2认同)

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)

  • 如果用户想要,仍然可以设置`c._x`.Python实际上并不是真正的私有属性. (5认同)

Eli*_*sky 20

看看这篇文章是非常实用的.简而言之,它解释了如何在Python中放弃显式的getter/setter方法,因为如果你在某个阶段需要它们,你可以使用它们property进行无缝实现.


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应用程序中,任何给定的页面视图可能只需要这种类型的一个特定属性,但底层对象本身可能有几个这样的属性 - 在构造中初始化它们将是浪费,并且属性允许我灵活属性是懒惰的,哪些不是.

  • 有趣.我认为它是在这个答案之后首次发布的,但任何阅读此内容的人都应该使用它. (2认同)
  • `cached_property` 现在通过 [functools](https://docs.python.org/3/library/functools.html#functools.cached_property) 位于标准库中 (2认同)

Sta*_*eev 7

属性只是一个字段的抽象,它可以让您更好地控制特定字段的操作方式和中间件计算.想到的几个用法是验证和先前的初始化和访问限制

@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)


inn*_*ov8 6

是的,对于发布的原始示例,该属性将与仅具有实例变量"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",知道如果您将来更改为属性,则使用的代码是安全的你的班级不需要改变.

这意味着如果存在复杂的行为,您只需要创建属性,对于非常常见的简单情况,如问题中所述,只需要一个简单的实例变量,您就可以使用实例变量.


qui*_*dog 6

与使用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)


Mik*_*ike 6

阅读答案和评论,主题似乎是答案似乎缺少一个简单但有用的例子.我在这里包含了一个非常简单的例子,演示了@property装饰器的简单使用.这是一个允许用户使用各种不同单位指定和获得距离测量的类,即in_feetin_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)


Spo*_*ser 5

对你的问题的简短回答是,在你的例子中,没有任何好处.您应该使用不涉及属性的表单.

属性存在的原因是,如果您的代码将来发生更改,并且您突然需要对数据执行更多操作:缓存值,保护访问权限,查询某些外部资源......无论如何,您可以轻松修改您的类以添加getter数据的setter和setter,无需更改接口,因此您不必在代码中的任何位置找到访问该数据的位置,也可以更改它.