Python中的"property"和"attribute"有什么区别?

227
我对“属性”和“特性”之间的区别感到困惑,并且找不到一个很好的资源来简明扼要地详细说明这些区别。
8个回答

244

属性是一种特殊的属性。基本上,当Python遇到以下代码时:

spam = SomeObject()
print(spam.eggs)

它在 spam 中查找 eggs,然后检查 eggs 是否具有 __get____set____delete__ 方法 —— 如果有,则是属性。如果它是一个属性,它不会像其他属性一样只返回 eggs 对象,而是调用了 __get__ 方法(因为我们正在查找),并返回该方法返回的任何内容。
有关 Python 数据模型和描述符 的更多信息。

听起来好像属性需要额外的处理才能访问目标值...你知道这有多显著/慢吗? - martineau
1
@martineau:嗯,多了一个函数调用,但是额外的工作和时间很可能取决于属性所做的工作量(一般建议:不要使用属性来隐藏I/O)。 - Ethan Furman

101

通过使用属性,您完全控制其getter、setter和deleter方法,而如果不使用警告,则无法做到这一点。

class A(object):
    _x = 0
    '''A._x is an attribute'''

    @property
    def x(self):
        '''
        A.x is a property
        This is the getter method
        '''
        return self._x

    @x.setter
    def x(self, value):
        """
        This is the setter method
        where I can check it's not assigned a value < 0
        """
        if value < 0:
            raise ValueError("Must be >= 0")
        self._x = value

>>> a = A()
>>> a._x = -1
>>> a.x = -1
Traceback (most recent call last):
  File "ex.py", line 15, in <module>
    a.x = -1
  File "ex.py", line 9, in x
    raise ValueError("Must be >= 0")
ValueError: Must be >= 0

26
我喜欢这个回答提供了一个现实和有用的例子。我觉得这个网站上太多的回答不必要地解释了后端是如何工作的,而没有澄清用户应该如何与它们交互。如果一个人不理解何时/为什么使用某些功能,知道它在幕后如何运作就没有意义了。 - Tom
1
@TinLuu - 只有一种方法可以设置x的值。同样,只有一种方法可以设置_x的值。它们是相同的事情这个事实只是一个实现细节。这个类的明智用户不使用_x。 - lit
考虑变量_x,a.x=1或a._x=1都会改变_x的值,这难道不是改变_x值的多种方式吗? - Tin Luu
2
@TinLuu - 我认为我们从不同的角度表达了同样的意思。类的用户应该只看到 x。这是一种方式。如果类的用户发现了 _x,他们将自行承担使用它的风险。 - lit
@TinLuu,考虑到_x,a.x=-1或a._x=-1它们的行为是不同的,这就是拥有getter和setter的意义所在。否则,只需使用属性即可。 - neurino
显示剩余2条评论

25

一般来说,属性(property)和特性(attribute)是相同的东西。然而,在Python中有一个属性修饰符可以提供对属性(或其他数据)的getter/setter访问。

class MyObject(object):
    # This is a normal attribute
    foo = 1

    @property
    def bar(self):
        return self.foo

    @bar.setter
    def bar(self, value):
        self.foo = value


obj = MyObject()
assert obj.foo == 1
assert obj.bar == obj.foo
obj.bar = 2
assert obj.foo == 2
assert obj.bar == obj.foo

1
请问您能否提及一下这段代码的预期结果? - QuestionEverything
2
什么意思?不是在代码底部吗? - six8

21

这个属性允许你像使用普通的属性一样获取和设置值,但实际上在底层会调用一个方法来将其转换为getter和setter。这只是一个方便的方式,可以减少调用getter和setter时的模板代码。

假设你有一个类来保存某些物体的x和y坐标。为了设置它们,你可能想要这样做:

myObj.x = 5
myObj.y = 10

这比写下以下代码要容易得多:

myObj.setX(5)
myObj.setY(10)
问题在于,如果有一天你的类发生了变化,需要将x和y偏移一些值怎么办?现在,您需要进入并更改类定义及所有调用它的代码,这可能非常耗时且容易出错。但是,使用property属性可以让您使用以前的语法同时提供灵活性。
在Python中,您可以使用property函数定义Getter、Setter和Delete方法。如果您只想要读取属性,则还可以在方法上方添加@property装饰器。 http://docs.python.org/library/functions.html#property

只有这个答案是有实际意义的! - Dude156

18

我从Bernd Klein的网站上学到了两个不同之处,总结如下:

1. 属性是实现数据封装更方便的一种方式。

例如,假设您有一个公共属性 length。后来,您的项目需要将其封装,即将其更改为私有,并提供getter和setter => 您必须更改以前编写的代码:

# Old code
obj1.length = obj1.length + obj2.length
# New code (using private attributes and getter and setter)
obj1.set_length(obj1.get_length() + obj2.get_length()) # => this is ugly
如果你使用@property@length.setter,那么你不需要改变那个旧代码。
2. 一个属性可以封装多个属性。
class Person:
  def __init__(self, name, physic_health, mental_health):
    self.name = name
    self.__physic_health = physic_health 
    self.__mental_health = mental_health 

  @property
  def condition(self):
    health = self.__physic_health + self.__mental_health
    if(health < 5.0):
      return "I feel bad!"
    elif health < 8.0:
      return "I am ok!"
    else:
      return "Great!"
在这个例子中,__physic_health__mental_health是私有的,不能直接从外部访问。

10

还有一个不明显的区别,就是我经常用来缓存或刷新数据的方式,通常我们会将函数连接到类属性。例如,我需要读取文件一次,并将内容分配给属性,以便值被缓存:

class Misc():
        def __init__(self):
            self.test = self.test_func()

        def test_func(self):
            print 'func running'
            return 'func value'

cl = Misc()
print cl.test
print cl.test

输出:

func running
func value
func value

我们访问了属性两次,但我们的函数只被触发了一次。将上面的例子改为使用属性将导致每次访问它时刷新属性的值:
class Misc():

    @property
    def test(self):
        print 'func running'
        return 'func value'

cl = Misc()
print cl.test
print cl.test

输出:

func running
func value
func running
func value

10

我认为,如果您想为属性设置限制,应该使用属性。

虽然所有属性都是公共的,但程序员通常会使用下划线(_)来区分公共和私有属性。考虑以下类:

class A:
    def __init__(self):
        self.b = 3    # To show public
        self._c = 4   # To show private

这里,b 属性旨在从类 A 的外部访问。但是,该类的读者可能会想知道,b 属性能否从类 A 的外部设置?

如果我们不打算从外部设置 b,我们可以使用 @property 来表明这一意图。

class A:
    def __init__(self):
        self._c = 4   # To show private
   
    @property
    def b(self):
        return 3

现在,b无法设置。

a = A()
print(a.b)   # prints 3
a.b = 7      # Raises AttributeError

或者,如果您只想设置某些值,

class A:
    @property 
    def b(self):
        return self._b
    
    @b.setter
    def b(self, val):
        if val < 0:
            raise ValueError("b can't be negative")
        self._b = val

a = A()
a.b = 6     # OK
a.b = -5    # Raises ValueError

0

1
目前你的回答不够清晰,请编辑并添加更多细节,以帮助其他人理解它如何回答问题。你可以在帮助中心找到有关如何编写好答案的更多信息。 - Community

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接