我对“属性”和“特性”之间的区别感到困惑,并且找不到一个很好的资源来简明扼要地详细说明这些区别。
属性是一种特殊的属性。基本上,当Python遇到以下代码时:
spam = SomeObject()
print(spam.eggs)
spam
中查找 eggs
,然后检查 eggs
是否具有 __get__
、__set__
或 __delete__
方法 —— 如果有,则是属性。如果它是一个属性,它不会像其他属性一样只返回 eggs
对象,而是调用了 __get__
方法(因为我们正在查找),并返回该方法返回的任何内容。通过使用属性,您完全控制其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
x
。这是一种方式。如果类的用户发现了 _x,他们将自行承担使用它的风险。 - lit一般来说,属性(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
这个属性允许你像使用普通的属性一样获取和设置值,但实际上在底层会调用一个方法来将其转换为getter和setter。这只是一个方便的方式,可以减少调用getter和setter时的模板代码。
假设你有一个类来保存某些物体的x和y坐标。为了设置它们,你可能想要这样做:
myObj.x = 5
myObj.y = 10
这比写下以下代码要容易得多:
myObj.setX(5)
myObj.setY(10)
问题在于,如果有一天你的类发生了变化,需要将x和y偏移一些值怎么办?现在,您需要进入并更改类定义及所有调用它的代码,这可能非常耗时且容易出错。但是,使用property属性可以让您使用以前的语法同时提供灵活性。我从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
,那么你不需要改变那个旧代码。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
是私有的,不能直接从外部访问。还有一个不明显的区别,就是我经常用来缓存或刷新数据的方式,通常我们会将函数连接到类属性。例如,我需要读取文件一次,并将内容分配给属性,以便值被缓存:
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
我认为,如果您想为属性设置限制,应该使用属性。
虽然所有属性都是公共的,但程序员通常会使用下划线(_
)来区分公共和私有属性。考虑以下类:
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
属性实际上在对象中。
属性通过代理传播。(它的值可以动态计算。)
另请参阅