与经典的getter+setter相比,@property表示法有什么优点?在哪些特定的情况下,程序员应该选择使用其中一种而不是另一种?

属性:

class MyClass(object):
    @property
    def my_attr(self):
        return self._my_attr

    @my_attr.setter
    def my_attr(self, value):
        self._my_attr = value

没有属性:

class MyClass(object):
    def get_my_attr(self):
        return self._my_attr

    def set_my_attr(self, value):
        self._my_attr = value

当前回答

我觉得属性是为了让您在真正需要getter和setter时才编写它们。

Java编程文化强烈建议永远不要授予对属性的访问权,相反,只访问实际需要的getter和setter。 总是编写这些显而易见的代码段有点啰嗦,请注意,70%的情况下,它们从未被一些重要的逻辑所取代。

在Python中,人们实际上关心这种开销,因此您可以采用以下实践:

不使用getter和setter在第一次,如果他们不需要 使用@property来实现它们,而不改变其余代码的语法。

其他回答

在Python中,你不会仅仅为了好玩而使用getter或setter或属性。首先只使用属性,然后在需要时,最终迁移到属性,而不必使用类更改代码。

确实有很多带有.py扩展名的代码在任何地方都使用getter和setter、继承和无意义的类,例如一个简单的元组就可以了,但这是人们使用Python用c++或Java编写的代码。

这不是Python代码。

我很惊讶没有人提到属性是描述符类的绑定方法,Adam Donohue和NeilenMarais在他们的帖子中得到了这个想法——getter和setter是函数,可以用于:

验证 改变数据 鸭子类型(强迫类型到另一种类型)

这提供了一种聪明的方法来隐藏实现细节和代码cruft,如正则表达式,类型转换,尝试..块、断言或计算值除外。

一般来说,在对象上执行CRUD可能相当简单,但请考虑将持久化到关系数据库的数据示例。ORM可以在属性类中定义的绑定到fget, fset, fdel的方法中隐藏特定SQL白话的实现细节,该属性类将管理可怕的if ..elif . .在OO代码中是如此丑陋的梯子——暴露了简单而优雅的自我。variable = something,为使用ORM的开发人员消除细节。

如果有人认为属性只是束缚和纪律语言(即Java)的一些沉闷的残余,那么他们就没有理解描述符的意义。

简单的答案是:properties轻松获胜。总是这样。

有时需要getter和setter,但即使这样,我也会将它们“隐藏”到外部世界。在Python中有很多方法可以做到这一点(getattr, setattr, __getattribute__,等等…,但最简洁明了的是:

def set_email(self, value):
    if '@' not in value:
        raise Exception("This doesn't look like an email address.")
    self._email = value

def get_email(self):
    return self._email

email = property(get_email, set_email)

下面是一篇简短的文章,介绍Python中的getter和setter主题。

以下是摘自《有效的Python: 90种具体方法来编写更好的Python》(一本令人惊叹的书)的节选。我强烈推荐)。

Things to Remember ✦ Define new class interfaces using simple public attributes and avoid defining setter and getter methods. ✦ Use @property to define special behavior when attributes are accessed on your objects, if necessary. ✦ Follow the rule of least surprise and avoid odd side effects in your @property methods. ✦ Ensure that @property methods are fast; for slow or complex work—especially involving I/O or causing side effects—use normal methods instead. One advanced but common use of @property is transitioning what was once a simple numerical attribute into an on-the-fly calculation. This is extremely helpful because it lets you migrate all existing usage of a class to have new behaviors without requiring any of the call sites to be rewritten (which is especially important if there’s calling code that you don’t control). @property also provides an important stopgap for improving interfaces over time. I especially like @property because it lets you make incremental progress toward a better data model over time. @property is a tool to help you address problems you’ll come across in real-world code. Don’t overuse it. When you find yourself repeatedly extending @property methods, it’s probably time to refactor your class instead of further paving over your code’s poor design. ✦ Use @property to give existing instance attributes new functionality. ✦ Make incremental progress toward better data models by using @property. ✦ Consider refactoring a class and all call sites when you find yourself using @property too heavily.

我认为两者都有各自的地位。使用@property的一个问题是,很难在子类中使用标准的类机制扩展getter或setter的行为。问题是实际的getter/setter函数隐藏在属性中。

你可以掌握这些函数,比如

class C(object):
    _p = 1
    @property
    def p(self):
        return self._p
    @p.setter
    def p(self, val):
        self._p = val

你可以像C.p.fset和C.p.fset一样访问getter和setter函数,但是你不能很容易地使用普通的方法继承(例如super)工具来扩展它们。在深入研究了super的复杂性之后,你确实可以这样使用super:

# Using super():
class D(C):
    # Cannot use super(D,D) here to define the property
    # since D is not yet defined in this scope.
    @property
    def p(self):
        return super(D,D).p.fget(self)

    @p.setter
    def p(self, val):
        print 'Implement extra functionality here for D'
        super(D,D).p.fset(self, val)

# Using a direct reference to C
class E(C):
    p = C.p

    @p.setter
    def p(self, val):
        print 'Implement extra functionality here for E'
        C.p.fset(self, val)

然而,使用super()是相当笨拙的,因为必须重新定义属性,并且必须使用稍微违反直觉的super(cls,cls)机制来获得p的未绑定副本。