python面对对象编程-------5:获取属性的四种办法:@property, __setattr__(__getattr__) ,descriptor
一:最基本的属性操作
class Generic:
pass g= Generic() >>> g.attribute= "value" #创建属性并赋值
>>> g.attribute
'value'
>>> g.unset
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Generic' object has no attribute 'unset'
>>> del g.attribute #注意,此地时直接删除了这个属性
>>> g.attribute
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Generic' object has no attribute 'attribute'
基本的属性操作
二:@property
被@property修饰的是一个方法,但此方法名可以像属性一样被获取,设置,删除
需要注意的是,属性的外部添加是十分简单的,但property的外部添加不是,所以其与属性还是有区别的
有两种方法创建property:
1:用@property修饰的函数
2:用property()方法
有两种设计模式:
1:懒惰计算模式:被调用时才执行
2:主动计算模式:实例化时就执行 懒惰模式:计算手牌总和
class Hand_Lazy(Hand):
def __init__( self, dealer_card, *cards ):
self.dealer_card= dealer_card
self._cards= list(cards)
@property
def total( self ):
delta_soft = max(c.soft-c.hard for c in self._cards)
hard_total = sum(c.hard for c in self._cards)
if hard_total+delta_soft <= 21:
return hard_total+delta_soft
return hard_total
@property
def card( self ):
return self._cards
@card.setter
def card( self, aCard ):
self._cards.append( aCard )
@card.deleter
def card( self ):
self._cards.pop(-1) d= Deck()
h= Hand_Lazy( d.pop(), d.pop(), d.pop() )
h.total #被调用时才执行计算手头的牌之和
#
h.card = d.pop() #注意,可以将@property看作@property.getter,而此地可以看作两步,左边为@property获取属性,=号调用@property.setter并且将右边的d.pop()当作参数传入。
h.total
#
懒惰模式
主动计算模式
# 将计算嵌入到@card.setter中,每新添加一张手牌就立马更新手牌总和
class Hand_Eager(Hand):
def __init__( self, dealer_card, *cards ):
self.dealer_card= dealer_card
self.total= 0
self._delta_soft= 0
self._hard_total= 0
self._cards= list()
for c in cards:
self.card = c
@property
def card( self ):
return self._cards
@card.setter
def card( self, aCard ):
self._cards.append(aCard)
self._delta_soft = max(aCard.soft-aCard.hard,self._delta_soft)
self._hard_total += aCard.hard
self._set_total()
@card.deleter
def card( self ):
removed= self._cards.pop(-1)
self._hard_total -= removed.hard
# Issue: was this the only ace?
self._delta_soft = max( c.soft-c.hard for c in self._cards)
self._set_total() def _set_total( self ):
if self._hard_total+self._delta_soft <= 21:
self.total= self._hard_total+self._delta_soft
else:
self.total= self._hard_total d= Deck()
h1= Hand_Lazy( d.pop(), d.pop(), d.pop() )
print( h1.total )
h2= Hand_Eager( d.pop(), d.pop(), d.pop() )
print( h2.total )
主动计算模式
其实@property已经模糊了数据和行为了,那么到底什么时候我们需要使用@property呢?
1:需要使用类中其他属性计算得到【也就是上面的情况】
2:对于难以查找或者计算的东西,将这个值以私有属性的形式缓存到本地,而后再次访问就快捷很多:
from urllib.request import urlopen
class WebPage:
def __init__(self,url):
self.url = url
self._content = None @property
def content(self):
if not self._content:
print("retriving new page")
self._content = urlopen(self.url).read() return self._content import time
webpage = WebPage("http://ccphillips.net/")
now = time.time()
content1 = webpage.content
print(time.time()-now)
now = time.time()
content2 = webpage.content
print(time.time()-now) 输出:
retriving new page
14.51249384880066
0.0 #!!!!
用于缓存内容
补充:廖雪峰的关于@property片段的代码
class Student:
def get_score(self):
return self._score def set_score(self,value):
if not isinstance(value,int):
raise ValueError('must be integer')
if value < 0 or value > 100:
raise ValueError('0~100')
self._score = value s=Student()
s.set_score(60)
s.get_score()
# # 用@property优化:
# 注意,可以把@property看作getter,而setter与deletter都是基于getter的
class Student:
@property
def score(self):
return self._score @score.setter
def score(self,value):
if not isinstance(value,int):
raise ValueError('must be integer')
if value < 0 or value > 100:
raise ValueError('0~100')
self._score = value s=Student()
s.score = 60
s.score
#
廖雪峰@property
三:属性获取的特殊方法
__getattr__(), __setattr__(), and __delattr__(),__dir__(),__getattribute__()
__setattr__(): 创建属性并赋值
__getattr__(): 首先:如果此属性已有值,不会用到__getattr__(),直接返回值就是了。
其次:如果此属性没有值,此时调用__getattr__()并且返回其中设定的返回值。
最后:如果压根没有这属性,报 AttributeError 错误。
__delattr__():删除一个属性
__dir__(): 返回包含属性的list
__getattribute__():更加底层的属性获取方法,他默认从__dict__(或__slots__)中获取值,如果没有找到,调用__getattr__()作为反馈。如果发现此值是一个dexcriptor,就调用descriptor,否者就直接返回值。
__getattr__()方法只当某个属性没有值时才起作用。
1:创建immutable object
什么是immutable object:不能够在外部直接赋值一个已有属性的值,不能创建新属性
immutable object的一个特点是__hash__()能够返回固定的值
版本一:用__slots__创建immutable object:
class BlackJackCard:
"""Abstract Superclass"""
__slots__ = ( 'rank', 'suit', 'hard', 'soft' ) #__slots__限定了只有这些属性可用
def __init__( self, rank, suit, hard, soft ):
super().__setattr__( 'rank', rank )
super().__setattr__( 'suit', suit )
super().__setattr__( 'hard', hard )
super().__setattr__( 'soft', soft )
def __str__( self ):
return "{0.rank}{0.suit}".format( self )
def __setattr__( self, name, value ):
raise AttributeError( "'{__class__.__name__}' has no attribute '{name}'".format( __class__= self.__class__, name= name ) ) # We defined __setattr__() to raise an exception rather than do anything useful.
# __init__() use the superclass version of __setattr__() so that values can be properly set in spite of the absence of a working __setattr__() method in this class.
# 我们知道,python并不阻止人干坏事,所以可以通过 object.__setattr__(c, 'bad', 5) 来绕过immutable机制
__slots__创建immutable object
版本2: 我们还可以通过继承 tuple 并且覆盖__getattr__()来写immutable object。
class BlackJackCard2( tuple ):
def __new__( cls, rank, suit, hard, soft ): # tuple(iterable) -> tuple initialized from iterable's items
return super().__new__( cls, (rank, suit, hard, soft) ) def __getattr__( self, name ): #translate __getattr__(name) requests to self[index] requests
return self[{'rank':0, 'suit':1, 'hard':2 , 'soft':3}[name]] def __setattr__( self, name, value ):
raise AttributeError >>> d = BlackJackCard2( 'A', '?', 1, 11 )
>>> d.rank
'A'
>>> d.suit
'?'
>>> d.bad= 2 #不能改变属性值了
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in __setattr__AttributeError
继承tuple实现immutable object
# 注意上面两个版本是有区别的,在版本2中可以通过d.__dict__来增加属性
# 而版本1中用了__slots__后就会关闭__dict__
2:创建一个一旦给定速度与时间就自动更新距离的类,让其继承自dict,好处是用format函数特别方便
class RateTimeDistance( dict ):
def __init__( self, *args, **kw ):
super().__init__( *args, **kw )
self._solve()
def __getattr__( self, name ):
return self.get(name,None) #对应字典的get方法
def __setattr__( self, name, value ):
self[name]= value #对应字典的赋值方法
self._solve() #在__setattr__中调用方法既是一旦赋值就能能够完成计算
def __dir__( self ):
return list(self.keys())
def _solve(self):
if self.rate is not None and self.time is not None:
self['distance'] = self.rate*self.time
elif self.rate is not None and self.distance is not None:
self['time'] = self.distance / self.rate
elif self.time is not None and self.distance is not None:
self['rate'] = self.distance / self.time >>> rtd= RateTimeDistance( rate=6.3, time=8.25, distance=None )
>>> print( "Rate={rate}, Time={time}, Distance={distance}".format(**rtd ) )
Rate=6.3, Time=8.25, Distance=51.975
# It's also important to note that once all three values are set, this object can't be changed to provide new solutions easily.
# 上面有个bug在于,一旦我们想改变时间,这时发现速度与距离至少其一一定会变,按代码顺序是改变了距离,而如果我们不想改变距离而是改变速度就不行了
# 或者是两个都不想改变,唯一的办法不改变其中一个就是先把一个值设为None # 解决办法:design a model that tracked the order that the variables were set in
# this model could save us from having to clear one variable before setting another to recompute a related result.
综合__settattr__,__getattr__,__dir__以及主动计算
3:The __getattribute__() method
总的来说,几乎没必要用__getattribute__(),其默认的方法已近够强大了,况且几乎所有我们需要的都能够通过__getattr__()实现。
class BlackJackCard3:
"""Abstract Superclass"""
def __init__( self, rank, suit, hard, soft ):
super().__setattr__( 'rank', rank )
super().__setattr__( 'suit', suit )
super().__setattr__( 'hard', hard )
super().__setattr__( 'soft', soft )
def __setattr__( self, name, value ):
if name in self.__dict__:
raise AttributeError( "Cannot set {name}".format(name=name) )
raise AttributeError( "'{__class__.__name__}' has no attribute'{name}'".format( __class__= self.__class__, name= name ) )
def __getattribute__( self, name ):
if name.startswith('_'):
raise AttributeError
return object.__getattribute__( self, name ) >>> c = BlackJackCard3( 'A', '?', 1, 11 )
>>> c.rank= 12
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 9, in __setattr__
File "<stdin>", line 13, in __getattribute__
AttributeError
>>> c.__dict__['rank']= 12
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 13, in __getattribute__
AttributeError
__getattribute__
四:descriptors
Descriptor.__get__( self, instance, owner ),Descriptor.__set__( self, instance, value ),Descriptor.__delete__( self, instance )
instance: the self variable of the object being accessed
owner : the owning class object
value : the new value that the descriptor needs to be set to. 描述符是一个类:在达到属性前处理,可用于get,set,delete
其本身在类定义时创建,并不是在__init__中创建,它是类的一部分,不同于方法以及属性
用其来实现(不)可变对象:
无数据描述符:实现__set__or__delete__ or both,若是immutable对象,只用实现__set__并返回AttributeError
数据描述符: 至少实现__get__,通常实现__get__与__set__来创建个可变对象。
1:无数据描述符
class UnitValue_1:
"""Measure and Unit combined."""
def __init__( self, unit ):
self.value= None
self.unit= unit
self.default_format= "5.2f"
def __set__( self, instance, value ):
self.value= value
def __str__( self ):
return "{value:{spec}} {unit}".format( spec=self.default_format, **self.__dict__)
def __format__( self, spec="5.2f" ):
#print( "formatting", spec )
if spec == "": spec= self.default_format
return "{value:{spec}} {unit}".format( spec=spec,**self.__dict__) # The following is a class that does rate-time-distance calculations eagerly:
class RTD_1:
rate= UnitValue_1( "kt" )
time= UnitValue_1( "hr" )
distance= UnitValue_1( "nm" )
def __init__( self, rate=None, time=None, distance=None ):
if rate is None:
self.time = time
self.distance = distance
self.rate = distance / time
if time is None:
self.rate = rate
self.distance = distance
self.time = distance / rate
if distance is None:
self.rate = rate
self.time = time
self.distance = rate * time
def __str__( self ):
return "rate: {0.rate} time: {0.time} distance:{0.distance}".format(self) # As soon as the object is created and the attributes loaded, the missing value is computed.
# Once computed, the descriptor can be examined to get the value or the unit's name.
# Additionally, the descriptor has a handy response to str() and formatting requests >>> m1 = RTD_1( rate=5.8, distance=12 )
>>> str(m1)
'rate: 5.80 kt time: 2.07 hr distance: 12.00 nm'
>>> print( "Time:", m1.time.value, m1.time.unit )
Time: 2.0689655172413794 hr
无数据描述符的例子
2:数据描述符,转换单位后自动更新
class Unit:
conversion= 1.0
def __get__( self, instance, owner ):
return instance.kph * self.conversion #kph:千米每小时
def __set__( self, instance, value ):
instance.kph= value / self.conversion # The following are the two conversion descriptors:
class Knots( Unit ):
conversion= 0.5399568
class MPH( Unit ):
conversion= 0.62137119
# The following is a unit descriptor for a standard unit, kilometers per hour:
class KPH( Unit ):
def __get__( self, instance, owner ):
return instance._kph
def __set__( self, instance, value ):
instance._kph= value class Measurement:
kph= KPH()
knots= Knots()
mph= MPH()
def __init__( self, kph=None, mph=None, knots=None ):
if kph:
self.kph= kph
elif mph:
self.mph= mph
elif knots:
self.knots= knots
else:
raise TypeError
def __str__( self ):
return "rate: {0.kph} kph = {0.mph} mph = {0.knots}knots".format(self) # 在不同进制下自动完成转换
>>> m2 = Measurement( knots=5.9 )
>>> str(m2)
'rate: 10.92680006993152 kph = 6.789598762345432 mph = 5.9 knots'
>>> m2.kph
10.92680006993152
>>> m2.mph
6.789598762345432
数据描述符例子
五:一些补充:
Internally, Python uses descriptors to implement features such as method functions,
static method functions, and properties. Many of the cool use cases for descriptors
are already first-class features of the language
In Python, it's considerably simpler to treat all attributes as public. This means the following:
They should be well documented.
They should properly reflect the state of the object; they shouldn't be temporary or transient values.
In the rare case of an attribute that has a potentially confusing (or brittle)
value, a single leading underscore character (_) marks the name as "not part
of the defined interface." It's not really private.
一般来说,外部能够改变属性值并不是严重的事,但是当一个属性值改变后会影响到另一个时,我们需要考虑用函数或者property进行一些设置。
注意区别property的两种设计方式(eager calcilation & lazy calculation) descriptor是非常高级的python用法,一般用于连接 python 与 non-python 的处理,比如python与SQL,python做网络服务器,
在我们的程序里,关于attributes我们尽量用property来实现,如果发现property需要写的太复杂,那么我们转向descriptor。
python面对对象编程-------5:获取属性的四种办法:@property, __setattr__(__getattr__) ,descriptor的更多相关文章
- python面对对象编程----2:__init__
面对对象编程估计我们最早接触到的就是__init__了,也就是实例的初始化处理过程: 1:来看看最基础的__init__ class Card(object): #抽象类Card,并不用于实例化 de ...
- python面对对象编程------4:类基本的特殊方法__str__,__repr__,__hash__,__new__,__bool__,6大比较方法
一:string相关:__str__(),__repr__(),__format__() str方法更面向人类阅读,print()使用的就是str repr方法更面对python,目标是希望生成一个放 ...
- js面对对象编程(二):属性和闭包
上篇博客中解说了一些js对象的基本概念和使用方法.这篇博客解说一下js属性方面的:公有属性.私有属性,特权方法. 假设学过java.公有属性.私有属性,特权方法(即能够訪问和设置私有属性的方法)一定非 ...
- python面对对象编程---------6:抽象基类
抽象基本类的几大特点: 1:要定义但是并不完整的实现所有方法 2:基本的意思是作为父类 3:父类需要明确表示出那些方法的特征,这样在写子类时更加简单明白 用抽象基本类的地方: 1:用作父类 2:用作检 ...
- python面对对象编程中会用到的装饰器
1.property 用途:用来将对像的某个方法伪装成属性来提高代码的统一性. class Goods: #商品类 discount = 0.8 #商品折扣 def __init__(self,nam ...
- python面对对象编程----------7:callable(类调用)与context(上下文)
一:callables callables使类实例能够像函数一样被调用 如果类需要一个函数型接口这时用callable,最好继承自abc.Callable,这样有些检查机制并且一看就知道此类的目的是c ...
- python面对对象编程----1:BlackJack(21点)
昨天读完了<Mastering Object-oriented Python>的第一部分,做一些总结. 首先,第一部分总过八章,名字叫Pythonic Classes via Specia ...
- python面对对象编程------3:写集合类的三种方法
写一个集合类的三种方法:wrap,extend,invent 一:包装一个集合类 class Deck: def __init__( self ): self._cards = [card6(r+1, ...
- Python学习6——再谈抽象(面对对象编程)
1.对象魔法 在面对对象编程中,术语对象大致意味着一系列数据(属性)以及一套访问和操作这些数据的方法. 使用对象而非全局变量以及函数的原因有多个,而最重要的好处不过以下几点: 多态:可对不同类型的对象 ...
随机推荐
- crf 分词(待)
http://blog.csdn.net/marising/article/details/5769653
- input placeholder文字垂直居中(Mobile & PC)
Html5输入框支持placeholder,但是在定义文本框中定义placeholder存在兼容问题 <input type="text" placeholder=" ...
- BZOJ 3107 二进制a+b
Description 输入三个整数\(a, b, c\),把它们写成无前导\(0\)的二进制整数.比如\(a=7, b=6, c=9\),写成二进制为\(a=111, b=110, c=1001\) ...
- oracle 中使用触发器自动生成UUID
create or replace trigger tri_test before insert on test for each row declare begin if :new.uuid is ...
- 总结iOS 8和Xcode 6的各种坑
模拟器的路径从之前的~/Library/Application Support/iPhone Simulator移动到了~/Library/Developer/CoreSimulator/Device ...
- js plugin
http://site518.net/javascript-date-handle/ http://developer.51cto.com/art/201212/374902.htm http://e ...
- phpMyAdmin 完整路径泄露漏洞
漏洞名称: phpMyAdmin 完整路径泄露漏洞 CNNVD编号: CNNVD-201307-650 发布时间: 2013-08-09 更新时间: 2013-08-09 危害等级: 中危 漏洞类 ...
- 【动态规划】Vijos P1313 金明的预算方案(NOIP2006提高组第二题)
题目链接: https://vijos.org/p/1313 题目大意: m(m<=32000)金钱,n(n<=60)个物品,花费vi,价值vi*ci,每个物品可能有不超过2个附件,附件没 ...
- 使用ChineseLunisolarCalendar 对象由年份获得生肖名,Datetime.now.tostring获得星期几
一:使用ChineseLunisolarCalendar 对象由年份获得生肖名,截图 二:代码 using System; using System.Collections.Generic; usin ...
- 部署ASP.Net项目 遇到总是启用目录浏览或者报HTTP 错误 403.14 - Forbidden 的原因
部署Asp.Net 网站总是报下面的问题 原因: 没有为网站指定默认文档,增加默认文档 1.选中“默认文档” 2.点击右边“打开功能” 点击右边“添加”按钮,把你想作为的默认页面添加就可以了,重启服务 ...