白云岛资源网 Design By www.pvray.com

字面意思上的区别

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)给出的(非正式)定义:

Python 从attribute到property详解

接下来分别解释.

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属性暴露给外部读取, 但并不想它被修改, 我们该怎么办"_blank" href="https://www.jb51.net/article/181955.htm">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呢"htmlcode">

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)

从语法上看, 这些语句都是合法的, 但从业务上看, 它们都是不合法的. 那么, 怎样才能防止这种非法赋值呢"_blank" href="https://www.jb51.net/article/181955.htm">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覆盖.

以上这篇Python 从attribute到property详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。

白云岛资源网 Design By www.pvray.com
广告合作:本站广告合作请联系QQ:858582 申请时备注:广告合作(否则不回)
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
白云岛资源网 Design By www.pvray.com

《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线

暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。

艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。

《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。