70.魔法方法:http://bbs.fishc.com/forum.php?mod=viewthread&tid=48793&highlight=%C4%A7%B7%A8%B7%BD%B7%A8
魔法方法总是被双下划线包围,例如__init__
魔法方法的‘魔力’体现再他们总能再适当的时候被自动调用
1)__init__(self[,...])类在实例化对象的时候,首先会调用的一个方法
有时候在类定义是写__init__方法,有时候却没有,这是为什么呢?
>>> class Rectangle:
def __init__(self,x,y):
self.x=x
self.y=y
def getPeri(self):
return (self.x+self.y)*2
def getArea(self):
return self.x*self.y
>>> rect=Rectangle(3,4)
>>> rect.getPeri()
14
>>> rect.getArea()
12
#__init__不能有返回值
>>> class A:
def __init__(self):
return 'haha'
>>> a=A()
Traceback (most recent call last):
File "", line 1, in
a=A()
TypeError: __init__() should return None, not 'str'
2)__new__(cls[,...]) 实际上在实例化的时候先自动调用了这个魔法方法,之后才自动调用了__init__魔法方法
该魔法方法的参数是这个类
这个方法的返回值是一个实例对象,通常返回cls这个类的实例对象,也可以返回其他类的对象
这个new方法一般很少去重写,一般让python用默认的方法去执行就行
当继承一个不可变类型但需要进行修改需要重写这个方法
>>> class CapStr(str):
def __new__(cls,string):
string=string.upper()
return str.__new__(cls,string)
>>> a=CapStr('hahah') #由于CapStr这个类是继承了str类,str是不可改变的类,那么就不能修改str的__init__方法进行修改,所以应该在重写__new__,增加了将字符串转化成大写然后再调用父类str的__new__的方法
>>> a
'HAHAH'
3)__del__(self)析构方法,当对象被销毁时候自动调用(当垃圾回收机制自动销毁的时候:实例化对象都没有引用了的时候)
>>> class C:
def __init__(self):
print('我是__init__方法,我被调用了')
def __del__(self):
print('我是__del__方法,我被调用了')
>>> c=C()
我是__init__方法,我被调用了
>>> c1=c
>>> c2=c1
>>> c3=c2
>>> c4=c3
>>> del c1
>>> del c
>>> del c2
>>> del c3
>>> del c4
我是__del__方法,我被调用了
4)算数魔法方法当你的对象进行了算数操作的时候,以下魔法方法就会自动被调用,这些方法可以被重写
__add__(self,other):定义加法行为+
__sub__(self,other):定义减法行为-
>>> class New_int(int):
def __add__(self,other):
return int.__sub__(self,other)
def __sub__(self,other):
return int.__add__(self,other)
>>> a=New_int(3)
>>> b=New_int(5)
>>> a+b
-2
__mul__(self,other):定义乘法行为*
__truediv__(self,other):定义真除法行为/
__floordiv__(self,other):定义整数除法行为://
__mod__(self,other):定义取模算法的行为:%
__divmod__(self,other):定义当被divmod()调用时的行为
__pow__(self,other):定义当被power()调用或**运算时的行为
__lshift__(self,other):定义按位左移位的行为:<<
__rshift__(self,other):定义按右移位的行为>>
__and__(self,other):定义按位与操作的行为&
__xor__(self,other):定义按位异或操作的行为^
__or__(self,other):定义按位或操作的行为|
【反运算魔法方法(上面的所有魔法方法前面加上r)】当a+b,a是一个数值,那么不能利用a的魔法方法,只用利用b的r魔法方法,反魔法方法也可以改写
>>> class Nint(int):
def __radd__(self,other):
return int.__sub__(self,other)
>>> a=Nint(5)
>>> b=Nint(3)
>>> a+b
2
>>> 1+b
2
【增量运算符】a+=1 上面所有的魔法方法前面家i
【一元操作符】-a
5)案例练习
定制一个计时器的类
start和stop方法代表启动计时和停止计时
假设计时器对象t1,print(t1)和直接调用t1均显示结果
当计时器未启动或已经停止计时,调用stop方法会给予温馨提示
两个计时器对象可以进行相加:t1+t2
只能使用提供的有限资源完成
资源如下:
使用time模块的localtime方法获取当前时间
time.localtime返回struct_time的时间格式(time模块详解http://bbs.fishc.com/forum.php?mod=viewthread&tid=51326&highlight=time%2B%C4%A3%BF%E9%CF%EA%BD%E2
表现你的类:__str__和__repr__:由于要求输入t1回车显示结果(__repr__)和print(t1)(__str__)显示结果果则需要重写这俩魔法函数
__str__
>>> class A:
def __str__(self):
return 'hahah'
>>> a=A()
>>> print(a) #__str__就是当要print出来一个实例化对象的时候要自动调用的魔法方法
hahah
__repr__
>>> class B:
def __repr__(self):
return 'haha'
>>> b=B()
>>> b #__repr__就是当要对实例化对象直接敲回车的时候要自动调用的魔法方法
haha
[1]编写代码模块MyTime.py
import time as t
class MyTimer():
def __init__(self):
self.unit=['年','月','日','时','分','秒']
self.prompt= '未开始计时'
self.begin=0
self.end=0
self.lasted=[]
def __str__(self):
return self.prompt
__repr__=__str__
def __add__(self,other):
prompt='总共运行了' #局部变量
result=[]
for index in range(6):
result.append(self.lasted[index]+other.lasted[index])
if result[index]:
prompt+=(str(result[index])+self.unit[index])
return prompt
#开始计时
def start(self):
self.begin=t.localtime()
self.prompt='请先stop'
print('计时开始。。')
#停止计时
def stop(self):
if not self.begin:
print('请先start')
else:
self.end=t.localtime()
self._calc()
print('计时结束')
#内部方法,计算运行时间
def _calc(self):
self.lasted=[]
self.prompt= '总过运行了'
for index in range(6):
self.lasted.append(self.end[index]-self.begin[index])
if self.lasted[index]:
self.prompt+=(str(self.lasted[index])+self.unit[index])
#为下一轮初始化变量
self.begin=0
self.end=0
[2]运行代码模块
>>>
===================== RESTART: D:/python/3.5.1/Mytime.py =====================
>>> t1=MyTimer()
>>> t1
未开始计时
>>> t1.stop()
请先start
>>> t1.start()
计时开始。。
>>> t1.start()
计时开始。。
>>> t1
请先stop
>>> t1.stop()
计时结束
>>> t1
总过运行了15秒
>>> t2=MyTimer()
>>> t2.start()
计时开始。。
>>> t2.stop()
计时结束
>>> t2
总过运行了5秒
>>> t1+t2
'总共运行了20秒'
>>>
6)魔法方法对于属性访问的用用
__getattr__(self,name):定义当用户试图获取一个不存在的属性时候会被自动调用的方法
__getattribute__(self,name):定义当该类的属性被访问的时候会自动调用的方法
__setatter__(self,name,value):定义当一个属性被设置时候,会自动调用的方法
__delattr__(self,name):定义当一个属性被删除时候会自动调用的方法
>>> class C:
def __getattribute__(self,name):
print('getattribute')
return super().__getattribute(name)
def __getattr__(self,name):
print('getattr')
def __setattr__(self,name,value):
print('setattr')
super().__setattr__(name,value)
def __delattr__(self,name):
print('delattr')
super().__delattr__(name)
>>> c=C()
>>> c.x
getattribute
getattr
>>> c.x=1
setattr
>>> c.x
getattribute
getattr
>>> del c.x
delattr
#以下例子会造成改造魔法方法后的死循环
class Rectangle:
def __init__(self,width=0,height=0):
self.width=width #既然发生赋值操作就会自动除法__setattr__但是这个魔法方法被改造后里头又有一个赋值,所以会死循环
self.height=height
def __setattr__(self,name,value):
if name=='square':
self.width=value
self.height=value
else:
self.name=value
def getAre(self):
return self.width*self.height
7)描述符:将某种特殊类型的类的实例指派给另一个类的属性
特殊类型是实现以下三个方法一个或者多个的类就是特殊类型的类
__get__(self,instance,owner)用于访问属性,它返回属性的值
__set__(self,instance,value)将再属性分配操作中调用,不返回任何内容
__delete__(self,instance)控制删除操作,不返回任何内容
>>> class MyDecriptor: #这个类含有上述三个方法
def __get__(self,instance,owner):
print('getting...',self,instance,owner)
def __set__(self,instance,value):
print('setting...',self,instance,value)
def __delete__(self,instance):
print('deleting...',self,instance)
>>> class Test:
x=MyDecriptor() #把MyDecriptor这个类的实例MyDecriptor() 赋值给 Test类的属性x,那么这个MyDecriptor类就是描述符类
>>> test=Test()
>>> test.x #调用MyDecriptor的__get__方法
getting... <__main__.MyDecriptor object at 0x0000012E3FF4D748> <__main__.Test object at 0x0000012E3FF4D898>
>>> test
<__main__.Test object at 0x0000012E3FF4D898>
>>> Test
>>> test.x='x-man' #调用MyDecriptor的__set__方法
setting... <__main__.MyDecriptor object at 0x0000012E3FF4D748> <__main__.Test object at 0x0000012E3FF4D898> x-man
>>> del test.x #调用MyDecriptor的__del__方法
deleting... <__main__.MyDecriptor object at 0x0000012E3FF4D748> <__main__.Test object at 0x0000012E3FF4D898>
以下是编写自己的property类
>>> class Myproperty:
def __init__(self,fget=None,fset=None,fdel=None):
self.fget=fget
self.fset=fset
self.fdel=fdel
def __get__(self,instance,owner):
return self.fget(instance)
def __set__(self,instance,value):
self.fset(instance,value)
def __del__(self,instance):
self.fdel(instance)
>>> class C:
def __init__(self):
self._x=None
def getx(self):
return self._x
def setx(self,value):
self._x=value
def delx(self):
del self._x
x=Myproperty(getx,setx,delx)
>>> c=C()
>>> c.x='haha'
>>> c.x
'haha'
>>> x._x
Traceback (most recent call last):
File "", line 1, in
x._x
NameError: name 'x' is not defined
>>> c,_x
Traceback (most recent call last):
File "", line 1, in
c,_x
NameError: name '_x' is not defined
>>> c._x
'haha'
#以下是一个温度计数值转化的练习
先定义一个温度类,然后定义两个描述符类哟关于描述摄氏度和华氏度两个属性
要求两个属性会自动进行转换,也就是说你可以给摄氏度这个属性赋值,然后打印的华氏度属性是自动转换后的结果
class Celsius:
def __init__(self,value=26.0):
self.value=float(value)
def __get__(self,instance,owner):
return self.value
def __set__(self,instance,value):
self.value=float(value)
class Fahrenheit:
def __get__(self,instance,owner):
return instance.cel*1.8+32
def __set__(self,instance,value):
instance.cel=(float(value)-32)/1.8
class Temperature:
cel=Celsius()
fah=Fahrenheit()
调用后
>>>
====================== RESTART: D:/python/3.5.1/temp.py ======================
>>> temp=Temperature()#实例化对象此时调用了__init__方法,使得temp的value=26.0
>>> temp.cel #这个操作会触发Celsius描述符的__get__方法,返回对象temp的value
26.0
>>> temp.cel=30 #这个操作会触发Celsius描述符的__set__方法,将对象的value修改成30
>>> temp.fah #这个操作会触发Fahrenheit描述符的__get__方法
86.0
>>> temp.fah=100 #这个操作会触发Fahrenheit描述符的__set__方法
>>> temp.cel
37.77777777777778
>>>
8)定制序列
协议与其他编程语言中的接口很相似,他对丁你哪些方法必须要定义
容器类型的协议
如果说希望定制的容器是不可变的话,你只需要定义__len__()和__getitem__()方法
如果说你希望定制的容器是可变的话,除了__len__()和__getitem__()方法,你还需要定义__setitem__()和__delitem__()两个方法
编写一个不可改变的自定义列表,要求记录列表中每个元素被访问的次数
class Countlist:
def __init__(self,*args):
self.values=[x for x in args]
self.count={}.fromkeys(range(len(self.values)),0)
def __len__(self):
return len(self.values)
def __getitem__(self,key):
self.count[key]+=1
return self.values[key]
运行脚本
>>>
=================== RESTART: D:/python/3.5.1/Countlist.py ===================
>>> c1=Countlist(1,3,5,7,9) #调用__init__方法
>>> c2=Countlist(2,4,6,8,10)
>>> c1[1] #调用__getitem__方法
3
>>> c2[1]
4
>>> c1[1]+c2[1]
7
>>> c1.count
{0: 0, 1: 2, 2: 0, 3: 0, 4: 0}
9)迭代器 http://bbs.fishc.com/forum.php?mod=viewthread&tid=56023&highlight=yield
iter() 对一个容器对象调用该bif可以得到一个迭代器
next() 对一个迭代器调用该big可以返回一个值
当迭代器没有值返回系统会返回报错
>>> string='fishc'
>>> it=iter(string)
>>> next(it)
'f'
>>> next(it)
'i'
>>> next(it)
's'
>>> next(it)
'h'
>>> next(it)
'c'
>>> next(it)
Traceback (most recent call last):
File "", line 1, in
next(it)
StopIteration
用这里的两个bif实现for的作用
>>> string='fishc'
>>> it=iter(string)
>>> while True:
try:
each=next(it)
except StopIteration:
break
print(each)
f
i
s
h
c
#利用__iter__() 和 __next__()方法
>>> class Fibs:
def __init__(self,n=10):
self.a=0
self.b=1
self.n=n
def __iter__(self):
return self
def __next__(self):
self.a,self.b=self.b,self.a+self.b
if self.a>self.n:
raise StopIteration
return self.a
>>> fibs=Fibs()
>>> for each in fibs:
print(each)
1
1
2
3
5
8