Python 从attribute到property详解
作者:Daniel2333 发布时间:2022-03-15 18:52:40
字面意思上的区别
Attribute与property, 都可翻译成属性. 虽然无论是在中文中还是英文中 它们的意思都几乎一样, 但仍有些许差别. Google了好几下, 找到了一个看起来比较靠谱的解释:
According to Webster, a property is a characteristic that belongs to a thing's essential nature and may be used to describe a type or species.
An attribute is a modifier word that serves to limit, identify, particularize, describe, or supplement the meaning of the word it modifies.
简单来说, property是类的本质属性, 可用于定义和描述一个类别或物种; attribute则是用于详细说明它所描述的物体, 是物体的具体属性.
例如: 人都有嘴巴. 有的人嘴巴很大, 嘴巴是人的property之一, 而大嘴巴只能说是部分人的attribute.
从这个意义上讲, property是attribute的子集.
Python里的attribute与property
回到Python.
Attribute与property在Java中不作区分, 但在Python中有所不同. 下面是Fluent Python(Chapter 19)给出的(非正式)定义:
接下来分别解释.
attribute
所有的数据属性(data attribute)与方法(method)都是attribute. 根据attribute的所有者, 可分为class attribute与instance attribute. class或instance的所有attribute都存储在各自的__dict__属性中.
例如:
# Python3
class Foo():
name = 'Foo class attribute'
def fn(self):
pass
print('class attribute:', Foo.__dict__)
print()
foo = Foo()
foo.name = 'foo instance attribute'
print('instance attribute:', foo.__dict__)
输出:
class attribute: {'fn': <function Foo.fn at 0x7fd135ec8ea0>, ... , 'name': 'Foo class attribute'}
instance attribute: {'name': 'foo instance attribute'}
property
property是出于安全考虑用setter/getter方法替代data attribute, 例如, 只读属性与属性值合法性验证.
只读属性
例如:
class Foo():
def __init__(self, name):
self.name = name
foo = Foo('I do not want to be changed')
print('foo.name = ', foo.name)
foo.name = 'Unluckily, I can be changed'
print('foo.name = ', foo.name)
输出:
foo.name = I do not want to be changed
foo.name = Unluckily, I can be changed
在上面的代码中, 假如我们只想将foo的name属性暴露给外部读取, 但并不想它被修改, 我们该怎么办? 之前在Python 定义只读属性中列出了两种解决方案. 第一种方案:”通过私有属性”, 其实就是用property替代attribute.
将上面的foo.name改写成property:
class Foo():
def __init__(self, name):
self.__name = name
@property
def name(self):
return self.__name
foo = Foo('I do not want to be changed')
print('foo.name = ', foo.name)
foo.name = 'Luckily, I really can not be changed'
输出:
foo.name = I do not want to be changed
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-69-101c96ba497e> in <module>()
9 foo = Foo('I do not want to be changed')
10 print('foo.name = ', foo.name)
---> 11 foo.name = 'Luckily, I really can not be changed'
AttributeError: can't set attribute
有两点需要注意:
foo.name确实已经不能通过foo.name = ...来修改了, 即, foo.name已经是只读属性.
将foo.name从attribute变成property之后, 它的访问方式并没有改变. 也就是说, 对外接口没有改变. 这个优点可以让我们从容的写代码, 不用在一开始就纠结于是使用property还是attribute, 因为可以都使用attribute, 如果有需要, 以后可以在不影响外部代码的前提下随时修改. 而在Java里要做到这一点很难(如果可以做到的话).
属性值合法性验证
在上面的例子中, foo.name只有getter方法, 是只读的, 但其实property也是可修改的, 只需要为它添加一个setter方法就行了. 那么问题就来了, 如果property也是可读可改, 那为何要费事将attribute改写成property呢?
想象一个简单的购物相关的业务场景. 一个Item代表用户购买的一样东西, 主要有类别, 价格和数量属性:
class Item():
def __init__(self, category, count, price):
self.cat = category
self.count = count
self.price = price
正常的调用是类似于这样的, 价格与数量都是正数:
item = Item('Bread', 1, 10)
可是, 若价格或数量设置为负数也不会报错:
item.price = -10
item.count = -1
invalid_item1 = Item('Bread', -1, 10)
invalid_item2 = Item('Bread', 1, -10)
从语法上看, 这些语句都是合法的, 但从业务上看, 它们都是不合法的. 那么, 怎样才能防止这种非法赋值呢? 一种解决方案是按照Java风格, 实现一个Java式的setter方法, 通过item.set_price(price)设置price属性, 然后在set_price方法里写验证代码. 这样是可行的, 但不够Pythonic. Python的风格是读与写都通过属性名进行:
print(item.price)
item.price = -10
这样做的好处之前提到过: 将attribute改写成property时不会改变对外接口. 那么, 如何在执行item.price = -10时检验-10的合法性呢? 最直白的方法是在__setattr__方法里设置拦截, 但很麻烦, 特别是当需要验证的属性很多时.(不信的话可以参照Python 定义只读属性的方案二试试).
Python提供的最佳方案是通过property的setter方法:
class Item():
def __init__(self, category, count, price):
self.__cat = category # attribute
self.count = count # property
self.price = price # property
@property
def cat(self):
return self.__cat
@property
def count(self):
return self.__dict__['count']
@count.setter
def count(self, value):
if value < 0:
raise ValueError('count can not be minus: %r'%(value))
self.__dict__['count'] = value
@property
def price(self):
return self.__dict__['price']
@price.setter
def price(self, value):
if value < 0:
raise ValueError('price can not be minus: %r'%(value))
self.__dict__['price'] = value
之前合法的语句现在仍然可以正常运行:
item = Item('Bread', 1, 10)
item.price = 20
item.count = 2
print(item.price)
但下面的语句执行时便会报错了:
item = Item('Bread', 1, -10)
# or
item.price = -10
会报出同一个错误:
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-93-4fcbd1284b2d> in <module>()
----> 1 item.price = -10
<ipython-input-91-7546240b5469> in price(self, value)
27 def price(self, value):
28 if value < 0:
---> 29 raise ValueError('price can not be minus: %r'%(value))
30 self.__dict__['price'] = value
ValueError: price can not be minus: -10
定义property的其他方式
@property中的property虽可被当作修饰器来使用, 但它其实是一个class(具体API请参考文档), 所以上面的代码还可以改写为:
class Item():
def __init__(self, category, count, price):
self.__cat = category # attribute
self.count = count # property
self.price = price # property
def get_cat(self):
return self.__cat
def get_count(self):
return self.__dict__['count']
def set_count(self, value):
if value < 0:
raise ValueError('count can not be minus: %r'%(value))
self.__dict__['count'] = value
def get_price(self):
return self.__dict__['price']
def set_price(self, value):
if value < 0:
raise ValueError('price can not be minus: %r'%(value))
self.__dict__['price'] = value
bill = property(get_bill)
cat = property(get_cat)
count = property(get_count, set_count)
price = property(get_price, set_price)
功能上达到要求了, 可代码本身看起来很冗长, 比Java中的getter/setter风格还要长. 这时可以通过property factory来简化代码:
先定义可共用的property factory函数:
def readonly_prop(storage_name):
def getter(instance):
return instance.__dict__[storage_name]
return property(getter)
def positive_mutable_prop(storage_name):
def getter(instance):
return instance.__dict__[storage_name]
def setter(instance, value):
if value < 0:
raise ValueError('%s can not be minus: %r'%(storage_name, value))
instance.__dict__[storage_name] = value
return property(getter, setter)
然后, 之前的示例代码可以简化为:
class Item():
def __init__(self, category, count, price):
self.__cat = category # attribute
self.count = count # property
self.price = price # property
cat = readonly_prop('__cat')
count = positive_mutable_prop('count')
price = positive_mutable_prop('price')
这样一来, 在保证代码简洁的前提下实现了访问控制和合法性验证.
property不会被instance attribute覆盖
之前在Python对象的属性访问过程一文中展示了attribute的解析过程, 从中知道class attribute可以被instance attribute覆盖:
class Foo():
name = 'Foo'
foo = Foo()
foo.name = 'foo'
codes = ['Foo.name', 'foo.name']
for code in codes:
print(code, '=', eval(code))
输出为:
Foo.name = Foo
foo.name = foo
但在property身上不会发生这种事情:
class Foo():
@property
def name(self):
return 'Foo'
foo = Foo()
foo.__dict__['name'] = 'foo'# 已经不能通过foo.name赋值了
codes = ['Foo.name', 'foo.name']
for code in codes:
print(code, '=', eval(code))
输出:
Foo.name = <property object at 0x7fd135e7ecc8>
foo.name = Foo
至少可以看出两点:
1. 通过class Foo访问Foo.name得到的是property对象, 而非property值.
2. 访问 foo.name时返回的是Foo.name的property值. 究其原因, 是因为在属性解析过程中, property的优先级是最高的.
总结
1.Python的attribute与property不同:
attribute: data attribute + method
property: replace attribute with access control methods like getter/setter, for security reasons.
2.可以通过多种方式定义property:
@property
property(getter, setter)
property factory
3.property在属性解析时的优先级最高, 不会被instance attribute覆盖.
来源:https://blog.csdn.net/weixin_35653315/article/details/78164204
猜你喜欢
- 假设有一个可迭代对象,现在想要对它内部的元素进行排序,我们一般会使用内置函数 sorted,举个例子:data = (3
- 装饰器对与Python新手以至于熟悉Python的人都是一个难理解, 难写的东西. 那么今天就分享一下我对Python 装饰器的理解所谓装饰
- 给明文密码加密的流程:import base64pwd_after_encrypt = base64.b64encode(b'thi
- python3中,list有个reverse函数,用来反转列表元素,但是如果想要反转部分元素呢?a = [1,2,3,4,5]a[0:3].
- 最近做了一个系统由于部分接口需要进行耗时操作,因而不希望用户进行频繁访问,需要进行访问频率限制。如果要自己实现一个访问限制功能相对来说也不会
- 这篇文章主要介绍了python plotly画柱状图代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需
- 写在前面 众所周知python拥有众多的第三方库,据不完全统计python有1w多个第三方库(为什么是不完全统计,因为我也记不清了☺),
- 录入身份证信息是一件繁琐的工作,如果可以自动识别并且录入系统,那可真是太好了。今天我们就来学习一下,如何自动识别身份证信息并且录入系统~识别
- 最近研究了一下并行读入数据的方式,现在将自己的理解整理如下,理解比较浅,仅供参考。并行读入数据主要分1. 创建文件名列表2. 创建文件名队列
- 内容简介展示如何给图像叠加不同等级的椒盐噪声和高斯噪声的代码,相应的叠加噪声的已编为对应的类,可实例化使用。以下主要展示自己编写的:加噪声的
- 列表的添加1)+ 添加2)append 追加一次只能添加一个元素到列表中,适合用于循环里3)extend 拉伸可一次添加多个元素到列表中4)
- 有些时候(如开发聊天程序),我们需要将将滚动条(scrollbar)保持在最底部,比如聊天窗口,最新发出和收到的信息要显示在最下方,如果要看
- 在html中关于select元素的问题在很多地方都提出过,而在前段时间的项目中,刚好遇到了关于select元素的两个小问题,这里进行一下总结
- 最近对 Range 和 Selection 比较感兴趣。基本非 IE 的浏览器都支持 DOM Level2 中的 Range,而 IE 中仅
- 本文实例讲述了Python mutiprocessing多线程池pool操作。分享给大家供大家参考,具体如下:python — mutipr
- 通过一条命令用Npm安装gulp-htmlmin:npm install gulp-htmlmin --save-dev安装完毕后,打开gu
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN&
- sort() 函数用于对数组单元从低到高进行排序。rsort() 函数用于对数组单元从高到低进行排序。asort() 函数用于对数组单元从低
- 安装cuda更新nvidia驱动打开GeForce Game Ready Driver或在GeForce Experience中下载符合自己
- 长期以来一直以为iframe跟div一样都是块级元素,直到今天在一个群中看到一位朋友问到iframe怎么居中的时候,测试了下发现原来我一直对