Python“属性”和“特性”之间有什么区别?
- 2025-04-15 09:19:00
- admin 原创
- 27
问题描述:
我通常对“属性”和“特性”之间的区别感到困惑,并且我找不到很好的资源来简明扼要地详细说明这些区别。
解决方案 1:
属性是一种特殊的属性。基本上,当 Python 遇到以下代码时:
spam = SomeObject()
print(spam.eggs)
它会在1eggs
中查找,然后检查它是否具有、或方法——如果有,则它是一个属性,Python 会调用该方法(因为我们执行的是查找操作),并返回该方法的返回值。如果它不是属性,则会在 中查找,并返回在那里找到的任何值。SomeObject
`eggs__get__
set__delete__
geteggs
spam`
有关Python 的数据模型和描述符的更多信息。
1非常感谢 Robert Seimer 对查找顺序的修正。
解决方案 2:
通过属性 (property),您可以完全控制其 getter、setter 和 deleter 方法,而使用属性 (attribute) 则无法做到这一点(如果不使用警告)。
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
解决方案 3:
一般来说,属性 (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
解决方案 4:
属性 (property) 允许您像普通属性 (attribute) 一样获取和设置值,但其底层调用了一个方法,将其转换为 getter 和 setter。这实际上只是为了减少调用 getter 和 setter 的样板代码。
举个例子,你有一个类,里面保存着你需要的 x 和 y 坐标。要设置它们,你可能需要执行以下操作:
myObj.x = 5
myObj.y = 10
这比写作更容易看和思考:
myObj.setX(5)
myObj.setY(10)
问题是,如果有一天你的类发生了变化,需要将 x 和 y 偏移某个值,该怎么办?现在你需要修改类定义以及所有调用它的代码,这非常耗时且容易出错。该属性允许你使用前一种语法,同时赋予你修改后一种语法的灵活性。
在 Python 中,你可以使用 property 函数定义 getter、setter 和 delete 方法。如果你只需要读取属性,也可以在方法上方添加 @property 装饰器。
http://docs.python.org/library/functions.html#property
解决方案 5:
我从 Bernd Klein 的网站上了解到了 2 个不同之处,总结如下:
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
and @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
是私有的,不能从外部直接访问。
解决方案 6:
我用来缓存或刷新数据还有一个不太明显的区别,我们经常会用一个函数连接到类属性。例如,我需要读取一次文件,并将内容赋给属性,这样值就被缓存了:
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
我们访问了该属性两次,但我们的函数只被触发了一次。将上面的示例更改为使用 property ,将导致每次访问该属性时其值都会刷新:
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
解决方案 7:
我喜欢这样想,如果您想为属性设置限制,请使用属性。
虽然所有属性都是公共的,但程序员通常用下划线(_
)来区分公共属性和私有属性。考虑以下类,
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
解决方案 8:
属性实际上存在于对象中。
属性通过代理传递。(其值可能会被动态计算。)
参见
https://docs.python.org/dev/library/functions.html#getattr:内置函数
https://docs.python.org/dev/library/functions.html#property:内置装饰器
解决方案 9:
对于一个用例,我使用@property
按需加载数据和缓存
class Foo:
def __init__(self):
self._bar = None
@property
def bar(self):
if _bar is None:
_bar = self.load_bar_dataset_from_database()
return _bar
当我的字段包含用于分析的数据框时,它们会在第一次访问时加载,并且我不会通过一次性从数据库加载数据框来初始化整个对象,但也可以缓存以用于进一步的分析。
还可以防止“设置”一致性。
扫码咨询,免费领取项目管理大礼包!