python的魔法方法是python之所以强大之所以灵活的灵魂。魔法方法的魔力是他们总会在适当的时候被调用。
魔法方法总是被双下划线包围,例如用于初始化的构造方法init()
因为魔法方法太多了,我们简要学习几个最基础最简单的魔法方法。
构造方法
init(self,[,,,])构造方法,类在实例化对象,即a =A()过程中会执行,用来
初始化对象。注意init方法的返回值一定是None!
>>> class A:
def __init__(self,a):
self.a = a
print('设置的初始值为:%d'%self.a)
>>> new =A(3)
设置的初始值为:3
因为在实例化对象过程中,必须会调用init()方法,所以init()中的打印语句会在实例化对象完成后执行。
但是实例化对象最先执行的方法不是_init_()
而是一个叫new(cls[, …])的方法。这个方法以一个实例对象作为返回值。当继承不可变类型如str,又必须要修改的时候,因为init()返回值为none,所以不能返回更改后的实例对象,所以需要用new()方法。
- new 是在一个对象实例化的时候所调用的第一个方法
- 它的第一个参数是这个类,其他的参数是用来直接传递给 init 方法
- new 决定是否要使用该 init 方法,因为 new 可以调用其他类的构造方法或者直接返回别的实例对象来作为本类的实例,如果 new 没有返回实例对象,则 init 不会被调用
- new 主要是用于继承一个不可变的类型比如一个 tuple 或者 string
如下例:
>>> class Capstr(str):
def __new__(self,string):
string =string.upper()
return string
>>> capstr = Capstr(' I love you')
>>> capstr
' I LOVE YOU'
这个new()方法的作用。就是对待实例化的对象,进行一步加工处理,再返回一个新的实例化对象。就比如上例中,将实例化对象字符串’I love you ‘作全部转换为大写的操作,再将’I LOVE YOU ‘作为新的实例化对象返回。
析构方法
>>> class C:
def __init__(self):
print('我是__init__方法,我被调用了...')
def __del__(self):
print('我是__del__方法,我被调用了.....')
>>> c1 =C()
我是__init__方法,我被调用了...
>>> c2 =c1
>>> c3 =c2
>>> del c3
>>> del c2
>>> del c1
我是__del__方法,我被调用了.....
上面代码表述的意思是,只有一个对象地址贴上的所有标签都删除的时候,才会启动析构方法。就比如上例中,c1,c2,c3都指向一个变量空间地址,但只有三个标签都撕下来的时候,析构方法才启动。
算术运算符的魔法方法
python中用工厂函数代替其他函数中的数据类型,所谓的工厂函数如list,int,float,都是类对象。
通过使用算术运算符的魔法方法,python可以自定义数值处理。
下例中,定一个类继承int类,然后重写父类中的add()和sub()方法。当识别到+号,要执行加法操作,就会启动add()方法。同样道理,遇到减法操作,就会执行sub()方法。
>>> class Nint(int):
def __add__(self,other):
return int.__sub__(self,other)
def __sub__(self,other):
return int.__add__(self,other)
>>> a = Nint(5)
>>> b = Nint(7)
>>> a +b
-2
>>> a -b
12
属性魔法方法
__getattr__(self,name)定义当用户试图获取一个不存在的属性时的行为
__getattribute__(self,name)定义当该类的属性被访问时的行为
__setattr__(self,name,value)定义当一个属性被设置时的行为
__delattr__(self,name)定义一个属性被删除时的行为
>>> class Rectrange:
def __init__(self,width =0 ,height =0):
self.width =width
self.height =height
def __setattr__(self,name,value):
if name =='square':
self.width =value
self.height =value
else:
#self.name =value,这种方式会导致陷入死循环,因为,这里的赋值又会执行__setattr__()
super().__setattr__(name,value)#调用父类object中的未修改的__setattr__()
#对象有一个特殊的属性dict,用于以字典的形式输出所有属性和对应的值
#或者self.__dict__[name] = value
def getArea(self):
return self.width *self.height
>>> rec = Rectrange()
>>> rec.square =10
>>> rec.getArea()
100
定制序列
定制序列,首先要讲一下协议
协议与其他编程语言中的接口很相似,它规定你哪些方法必须要定义
然而,在python中的协议就显得不那么正式。事实上,在python中,
协议更像是一种指南。
容器类型的协议
如果你希望定制的容器是不可变的话,你只需要定义
len()和getitem()方法。
如果你希望定制的容器是可变的话,除了定义
len()和getitem()方法,你还需要定义
setitem()和delitem()两个方法。
练习:
编写一个不可改变的自定义列表,要求记录列表中每个元素被访问的次数
class zdyseq:
def __init__(self,*args):
self.values = [ x for x in args]
self.count = {}.fromkeys(range(len(self.values)),0)
def __len__():
return len(self.values)
def __getitem__(self,key):
self.count[key]+= 1
return self.values[key]
c1 =zdyseq(1,2,35,4)
c2 =zdyseq(354,42,1,2)
print(c1[1])
print(c2[1])
print(c1[1]+c2[1])
print(c1.count)
迭代器
每一次迭代的的结果会作为下一次迭代的初始值。
提供迭代方法的容器我们称为迭代器。如序列、字典
关于迭代操作python提供了两个BIF
iter()对于一个容器对象调用iter()就得到一个对应的迭代器
next()next(迭代器)返回下一次迭代的值,如果下一个值没有,就会抛出异常StopIteration
迭代器的魔法方法有两个:
iter()返回本身
next()决定迭代的规则
string = 'i love you '
it =iter(string)
for each in range(11):
print(next(it))
#这里可以看出for语句的运行原理
string2 = 'i miss you '
it2 = iter(string2)
while True:
try:
each =next(it2)
print(each)
except StopIteration:
break
#构建一个斐波那契迭代器
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)