与经典的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

当前回答

@property和传统的getter和setter都有各自的优点。这取决于您的用例。

@property的优点

You don't have to change the interface while changing the implementation of data access. When your project is small, you probably want to use direct attribute access to access a class member. For example, let's say you have an object foo of type Foo, which has a member num. Then you can simply get this member with num = foo.num. As your project grows, you may feel like there needs to be some checks or debugs on the simple attribute access. Then you can do that with a @property within the class. The data access interface remains the same so that there is no need to modify client code. Cited from PEP-8: For simple public data attributes, it is best to expose just the attribute name, without complicated accessor/mutator methods. Keep in mind that Python provides an easy path to future enhancement, should you find that a simple data attribute needs to grow functional behavior. In that case, use properties to hide functional implementation behind simple data attribute access syntax. Using @property for data access in Python is regarded as Pythonic: It can strengthen your self-identification as a Python (not Java) programmer. It can help your job interview if your interviewer thinks Java-style getters and setters are anti-patterns.

传统getter和setter的优点

Traditional getters and setters allow for more complicated data access than simple attribute access. For example, when you are setting a class member, sometimes you need a flag indicating where you would like to force this operation even if something doesn't look perfect. While it is not obvious how to augment a direct member access like foo.num = num, You can easily augment your traditional setter with an additional force parameter: def Foo: def set_num(self, num, force=False): ... Traditional getters and setters make it explicit that a class member access is through a method. This means: What you get as the result may not be the same as what is exactly stored within that class. Even if the access looks like a simple attribute access, the performance can vary greatly from that. Unless your class users expect a @property hiding behind every attribute access statement, making such things explicit can help minimize your class users surprises. As mentioned by @NeilenMarais and in this post, extending traditional getters and setters in subclasses is easier than extending properties. Traditional getters and setters have been widely used for a long time in different languages. If you have people from different backgrounds in your team, they look more familiar than @property. Also, as your project grows, if you may need to migrate from Python to another language that doesn't have @property, using traditional getters and setters would make the migration smoother.

警告

Neither @property nor traditional getters and setters makes the class member private, even if you use double underscore before its name: class Foo: def __init__(self): self.__num = 0 @property def num(self): return self.__num @num.setter def num(self, num): self.__num = num def get_num(self): return self.__num def set_num(self, num): self.__num = num foo = Foo() print(foo.num) # output: 0 print(foo.get_num()) # output: 0 print(foo._Foo__num) # output: 0

其他回答

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

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

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

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

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

对我来说,使用属性更直观,更适合大多数代码。

比较

o.x = 5
ox = o.x

vs.

o.setX(5)
ox = o.getX()

对我来说很明显,更容易理解。此外,属性允许私有变量更容易。

我认为两者都有各自的地位。使用@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的未绑定副本。

使用属性可以让您从普通的属性访问开始,然后在必要时使用getter和setter对其进行备份。

@property和传统的getter和setter都有各自的优点。这取决于您的用例。

@property的优点

You don't have to change the interface while changing the implementation of data access. When your project is small, you probably want to use direct attribute access to access a class member. For example, let's say you have an object foo of type Foo, which has a member num. Then you can simply get this member with num = foo.num. As your project grows, you may feel like there needs to be some checks or debugs on the simple attribute access. Then you can do that with a @property within the class. The data access interface remains the same so that there is no need to modify client code. Cited from PEP-8: For simple public data attributes, it is best to expose just the attribute name, without complicated accessor/mutator methods. Keep in mind that Python provides an easy path to future enhancement, should you find that a simple data attribute needs to grow functional behavior. In that case, use properties to hide functional implementation behind simple data attribute access syntax. Using @property for data access in Python is regarded as Pythonic: It can strengthen your self-identification as a Python (not Java) programmer. It can help your job interview if your interviewer thinks Java-style getters and setters are anti-patterns.

传统getter和setter的优点

Traditional getters and setters allow for more complicated data access than simple attribute access. For example, when you are setting a class member, sometimes you need a flag indicating where you would like to force this operation even if something doesn't look perfect. While it is not obvious how to augment a direct member access like foo.num = num, You can easily augment your traditional setter with an additional force parameter: def Foo: def set_num(self, num, force=False): ... Traditional getters and setters make it explicit that a class member access is through a method. This means: What you get as the result may not be the same as what is exactly stored within that class. Even if the access looks like a simple attribute access, the performance can vary greatly from that. Unless your class users expect a @property hiding behind every attribute access statement, making such things explicit can help minimize your class users surprises. As mentioned by @NeilenMarais and in this post, extending traditional getters and setters in subclasses is easier than extending properties. Traditional getters and setters have been widely used for a long time in different languages. If you have people from different backgrounds in your team, they look more familiar than @property. Also, as your project grows, if you may need to migrate from Python to another language that doesn't have @property, using traditional getters and setters would make the migration smoother.

警告

Neither @property nor traditional getters and setters makes the class member private, even if you use double underscore before its name: class Foo: def __init__(self): self.__num = 0 @property def num(self): return self.__num @num.setter def num(self, num): self.__num = num def get_num(self): return self.__num def set_num(self, num): self.__num = num foo = Foo() print(foo.num) # output: 0 print(foo.get_num()) # output: 0 print(foo._Foo__num) # output: 0