魔法方法
1.魔法方法总是被双下划线包围,例如__init__
2.魔法方法是面向对象的Python的一切,如果不知道魔法方法,说明还没能意识到面向对象的Python的强大
3.魔法方法的魔力体现在它们总能在适当的时候被自动调用
一、构造和析构
1.__init__(self[,...])
相当于其他语言的构造方法,类在实例化的时候首先会被调用的方法
2.__new__(cls[,...])
这个是类实例化第一个调用的魔法方法,在init之前
3.__del__(self)
python析构方法,当类没有任何引用时,就会自动删除掉,ex:
>>> class C:
def __init__(self):
print('我是__init__,我被调用了')
def __del__(self):
print('我是__del__,我被调用了')
>>> c1=C()
我是__init__,我被调用了
>>> c2=c1
>>> c3=c1
>>> del c3
>>> del c2
>>> del c1
我是__del__,我被调用了
>>>
二、算术运算
python的魔法方法还支持自定义类的数值处理,通过对魔法方法的重写,可以自定义任何对象之间的算数运算。算数运算魔法方法如下:
1.__add__(self,other)--->定义加法的行为:+
2.__sub__(self,other)--->定义减法的行为:-
3.__mul__(self,other)--->定义乘法的行为:*
4.__truediv__(self,other)--->定义真除法的行为:/
5.__floordiv__(self,other)--->定义整数除法的行为://
6.__mod__(self,other)--->定义取模算法的行为:%
7.__divmod__(self,other)--->定义当被divmod()调用时的行为
8.__pow__(self,other[,modulo])--->定义当被power()调用或**运算的时的行为
9.__lshift__(self,other)--->定义按位左移位的行为:<<
10.__rshift__(self,other)--->定义按位右移位的行为:>>
11.__and__(self,other)--->定义按位与操作的行为:
&
12.__xor__(self,other)--->定义按位异或操作的行为:^
13.__or__(self,other)--->定义按位或操作的方法:|
14.
ex:
>>> class NewAdd(int):
def __add__(self,other):
return int.__sub__(self,other)
def __sub__(self,other):
return int.__add__(self,other)
>>> a=NewAdd(3)
>>> b=NewAdd(7)
>>> a+b
-4
>>> a-b
10
>>>
这里一定要搞清对象之间的关系!如果直接加是什么情况,你可以试试看,不是老师说,我还真没想到!,ex:
>>> class NewAdd(int):
def __add__(self,other):
return self+other
def __sub__(self,other):
return self-other
>>> a=NewAdd(3)
>>> b=NewAdd(7)
>>> a+b
Exception ignored in: <function NewAdd.__del__ at 0x00000201DA228950>
TypeError: __del__() missing 1 required positional argument: 'other'
Exception ignored in: <function NewAdd.__del__ at 0x00000201DA228950>
TypeError: __del__() missing 1 required positional argument: 'other'
Traceback (most recent call last):
File "<pyshell#103>", line 1, in <module>
a+b
File "<pyshell#100>", line 3, in __add__
return self+other
File "<pyshell#100>", line 3, in __add__
return self+other
File "<pyshell#100>", line 3, in __add__
return self+other
[Previous line repeated 990 more times]
RecursionError: maximum recursion depth exceeded
return a+b中的+,会再调用类的__add__
,然后又调用+,无限循环~~
还有一种情况:
>>> class int(int):
def __add__(self,other):
return int.__sub__(self,other)
>>> a=int('5')
>>> a
5
>>> b=int(3)
>>> a+b
2
这里默默把int替换成我们自定义的int类
反运算
__radd__(self,other):与上方相同,当左操作不支持相应的操作时被调用
....
当左边没有定义可操作的方法时,就会调用右侧的方法,ex:
>>> class Nint(int):
def __radd__(self,other):
return int.__sub__(self,other)
>>> a=Nint(5)
>>> b=Nint(3)
>>> a+b
8
>>> 1+a
4
>>>
>>>> a+1
6
>>> 1-a
-4
>>> a-1
4
这里a和b都定义了方法,执行正常的加法运算,而1+a中,1没有定义方法,a定义了,就会调用a的加法,也就是减法,即1+a其实就等价于与a-1,其他的都是这个道理了
简单定制
基本要求:
–定制一个计时器的类
–start和stop方法代表启动计时和停止计时
–假设计时器对象t1,print(t1)和直接调用t1
均显示结果
–当计时器未启动或已经停止计时,调用stop方法会给予温馨的提示
–两个计时器对象可以进行相加:t1+t2
–只能使用提供的有限资源完成
提供的资源:
使用time模块的localtime方法获取时间
–扩展阅读:time模块详解(时间获取和转换)
time.localtime犯规struct_time的时间格式
表现你的类:__str__和__repr__
time.localtime([ sec ])
sec – 转换为time.struct_time类型的对象的秒数。
ex:
import time
print( "time.localtime() : %s" % time.localtime())
输出结果:
time.localtime() : time.struct_time(tm_year=2016, tm_mon=11, tm_mday=27, tm_hour=10, tm_min=26, tm_sec=5, tm_wday=6, tm_yday=332, tm_isdst=0)
int tm_sec; /* 秒 – 取值区间为[0,59] */
int tm_min; /* 分 - 取值区间为[0,59] */
int tm_hour; /* 时 - 取值区间为[0,23] */
int tm_mday; /* 一个月中的日期 - 取值区间为[1,31] */
int tm_mon; /* 月份(从一月开始,0代表一月) - 取值区间为[0,11] */
int tm_year; /* 年份,其值等于实际年份减去1900 */
int tm_wday; /* 星期 – 取值区间为[0,6],其中0代表星期天,1代表星期一,以此类推 */
int tm_yday; /* 从每年的1月1日开始的天数 – 取值区间为[0,365],其中0代表1月1日,1代表1月2日,以此类推 */
int tm_isdst; /* 夏令时标识符,实行夏令时的时候,tm_isdst为正。不实行夏令时的时候,tm_isdst为0;不了解情况时,tm_isdst()为负。
介绍两个魔法方法:
__str__和__repr__
>>> class A():
def __str__(self):
return 'What a Fuck?'
>>> a=A()
>>> a
<__main__.A object at 0x000002327D2A3630>
>>> print(a)
What a Fuck?
>>> class B():
def __repr__(self):
return 'God!'
>>> b=B()
>>> print(b)
God!
>>>
__repr__和__str__这两个方法都是用于显示的,__str__是面向用户的,而__repr__面向程序员。
class Me(object):
def __init__(self, name, hobby):
self.name = name
self.hobby = hobby
def __str__(self):
return (' __str__(): '+self.name + ' ' + self.hobby)
def __repr__(self):
return ('__repr__(): '+self.name + ' ' + self.hobby)
me = Me('frank', 'swiming')
print(me)
----->
__str__(): frank swiming
计时器最终代码:
import time as t
class myTimer():
def __init__(self):
self.unit=['年','月','日','小时','分钟','秒']
self.prompt='未开始计时!'
self.lasted=[]
self.begin=0
self.end=0
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()
#为了防止不调用stop就直接str的
self.prompt='请先调用stop()方法停止计时!'
print('计时开始...')
#结束计时
def stop(self):
if self.begin:
self.end=t.localtime()
#调用自身方法,一定不要忘了self...
self._calc()
print('计时结束!')
else:
print('请先调用start开始计时!')
#内部方法,计算运行时间
def _calc(self):
self.lasted=[]
self.prompt='总共运行了:'
#6个队列存储6个时间分段
for index in range(6):
self.lasted.append(self.end[index]-self.begin[index])
#为0的时候就是False,否则为True
if self.lasted[index]:
self.prompt+=str(self.lasted[index])+self.unit[index]
#为下一次计时初始化
self.begin=0
self.end=0
魔法方法:属性访问
非魔法方法:
1.类名.属性名
2.getattr(类名,‘属性名’,[默认值])
>>> class A:
def __init__(self):
self.x='Bingo!'
>>> a=A()
>>> a.x
'Bingo!'
>>> getattr(a,'x','zgd')
'Bingo!'
>>> getattr(a,'y','zgd')
'zgd'
>>>
属性访问相关魔法方法:
1.__getattr__(self,name)
定义当用户视图获取一个不存在的属性时的行为
2.__getattribute__(self,name)
定义该类的属性被访问时的行为
3.__setattr__(self,name,value)
定义当一个属性被设置时的行为
4.__delattr__(self,name)
定义当一个属性被删除时的行为
魔法方法:描述符 (Property的原理)
描述符就是将某种类型的类的实例指派给另一个类的属性(由一个人的儿子变成另一个人的孙子?)
__get__(self,instance,owner)
用于访问属性,它返回属性的值
__set__(self,instance,value)
将在属性分配操作中调用,不返回任何内容
__delete__(self,instance)
控制删除操作,不返回任何内容
魔法方法:定制序列
在Python中的协议没有那么正式,更像是一种指南。
一、容器类型的协议
如果说定制的容器是不可变的话,只需要定义__len__()
和__getitem__()
方法,如果是可变的话,除了上面两个,还需要好定义__setitem__()
和__delitem__()
方法。
容器类型:
__len__(self)
:定义当被len()调用时的行为(返回容器中元素的个数)
__getitem__(self,key)
:定义获取容器中指定元素的行为,相当于self[key]
__setitem__(self,key,value)
:定义设置容器中指定元素的行为,相当于self[key]=value
__delitem__(self,key)
:定义删除容器中指定元素的行为,相当于del self[key]
__iter__(self)
:定义当迭代容器中的元素的行为
__reversed__(self)
:定义当被reversed()调用时的行为
__contains__(self,item)
:定义当使用成员测试运算符(in
or not in)时的行为
先学习一下两个知识点:
一.列表推导式
列表推导式提供了一种可以快速创建list的简便方法,应用列表穿件列表时,列表中的元素来源于其他序列、可迭代对象或创建的一个满足一定条件的序列。
1.使用[]生成list
基本格式:
[表达式 for 变量 in 列表]或者[表达式 for 变量 in 列表 if 条件
]
example:
简单点的:
>>> list1=['Bob','Alive','Death','Aid']
>>> [item.upper() for item in list1]
['BOB', 'ALIVE', 'DEATH', 'AID']
复杂点的:
求(x,y)其中x是0-5之间的偶数,y是0-5之间的奇数组成的元组列表。
>>> [(x,y) for x in range(0,5) if x%2==0 for y in range(0,5) if y%2==1]
[(0, 1), (0, 3), (2, 1), (2, 3), (4, 1), (4, 3)]
这里顺带一提,python3和python2对于range()函数的返回值是不一样的,2中返回的是一个列表,可以直接打印出来,3中返回的是一个对象,得通过循环打印出来:
>>> for item in range(10):
print(item)
0
1
2
3
4
5
6
7
8
9
二、字典(dict)的fromkeys方法
fromkeys()函数用于创建一个新字典,以序列seq中元素做字典的键,value为字典所有键对应的初始值,语法为:
dict.formkeys(seq[,value])
返回值是字典。
ex:
seq=['name','age','sex']
dict11=dict.fromkeys(seq)
print('新的字典为:%s' % str(dict11))
-->
新的字典为 : {'age': None, 'name': None, 'sex': None}
这里dict可以替换为空字典:{}
来个练习:
编写一个不可改变的自定义列表,要求记录列表中每个元素被访问的次数。
这里,我们可以将每个元素所在的下标作为key值,利用字典(dict)【可以理解成Java中的HashMap】存储起来。
>>> class Ryelist:
def __init__(self,*args):
print(args)
self.values=[x for x in args]
self.dicts={}.fromkeys(range(len(self.values)),0)
def __len__(self):
return len(self.values)
def __getitem__(self,key):
self.dicts[key]+=1
return self.dicts[key]
>>> test=Ryelist(1,3,5,7)
(1, 3, 5, 7)
>>> test[1]
1
>>> test.dicts
{0: 0, 1: 1, 2: 0, 3: 0}
>>>
魔法方法:迭代器
python的迭代器提供了两个BIF:
next()、iter()
调用对象的iter()返回它的迭代器
调用next()返回下一个值
当没有下一个对象的时候抛出异常。
ex:
>>> list1=['Rye','Catcher','MySelf']
>>>> item=iter(list1)
>>> next(item)
'Rye'
>>> next(item)
'Catcher'
>>>
这两个BIF对应的魔法方法为:
__iter__()、__next__()
ex:
定义一个斐波那契数列:
>>> class Fib:
def __init__(self):
self.a=0
self.b=1
def __iter__(self):
return self
def __next__(self):
self.a,self.b=self.b,self.a+self.b
return self.a
>>> fibs=Fib()
>>> for each in fibs:
if each<20:
print(each)
else :
break;
1
1
2
3
5
8
13
>>>
这里加了限制20以内的,不然会一直输出,会报异常的。。
可以再定义的时候就限制一下大小,如下:
>>> class Fib:
def __init__(self,num=10):
self.a=0
self.b=1
self.num=num
def __iter__(self):
return self
def __next__(self):
self.a,self.b=self.b,self.a+self.b
if self.a<self.num:
return self.a
else :
raise StopIteration
>>> fib=Fib(100)
>>> for each in fib:
print(each)
1
1
2
3
5
8
13
21
34
55
89
>>>
生成器
python的生成器是一个返回可以迭代对象的函数。
生成器函数与一般函数的不同点:
1.生成器函数包含一个或多个yield
2.当调用生成器函数时,函数将返回一个对象,但是不会立刻向下执行
3.想iter和next方法等都是自动实现的,所以我们可以通过next方法对对象进行迭代
4.一旦函数被yield,函数会暂停,控制权返回调用者
5.局部变量和他们的状态会被保存,直到下一次调用
6.函数终止的时候, StopIteration会被自动抛出
ex:
>>> def myGen():
print('调用生成器')
yield 1
yield 2
>>> gen=myGen()
>>> next(gen)
调用生成器
1
>>> next(gen)
2
>>> next(gen)
Traceback (most recent call last):
File "<pyshell#8>", line 1, in <module>
next(gen)
StopIteration
>>>
这里可以将yield当成return,只不过区别是return 下面的语句不会执行,而yield底下的语句会在下次调用的时候继续执行,直到没有yield的时候抛出StopIteration异常。
使用yield实现斐波那契数列:
>>> def libs():
a=0
b=1
while True:
a,b=b,a+b
yield a
>>> for each in libs():
if each >100:
break
print(each,end=' ')
1 1 2 3 5 8 13 21 34 55 89
yield每次暂停,print中加一个end=’ ',表示已空白符作为分割符,这样就不会一列一列输出了。
除了列表推导式外还有字典推导式
列表推导式例子:
>>> a=[i for i in range(100) if not(i%2) and i%3]
>>> a
[2, 4, 8, 10, 14, 16, 20, 22, 26, 28, 32, 34, 38, 40, 44, 46, 50, 52, 56, 58, 62, 64, 68, 70, 74, 76, 80, 82, 86, 88, 92, 94, 98]
>>>
``字典推导式:
字典和集合推导式是列表推导式思想的延续,语法差不多,只不过产生的是字典和集合而已,字典推导式如下:
{key_expr:value_expr for value in collection if condition}
ex:
b={i:i % 2==0 for i in range(10)}
b
{0: True, 1: False, 2: True, 3: False, 4: True, 5: False, 6: True, 7: False, 8: True, 9: False}
集合(set)推导式:
a={i for i in {1,2,3,4,3,2,1}}
a
{1, 2, 3, 4}
因为集合没有重复元素嘛,所以这样。列表,字典,集合推导式原理都是一样的。
模块就是程序
容器–>数据的封装
函数–>语句的封装
类----->方法和属性的封装
模块–>模块就是程序
实际上,将代码保存为一个py文件,就可以当做一个模块,即一个程序。为了能保证这个模块能被我们启动的idle调用,需要将两者放在同一文件夹下。
因为有命名空间的限制,所以引用模块里的方法变量的时候,前面一定要先加上模块的名字。
导入模块的三种方法:
第一种:import 模块名
第二种:from 模块名 import 函数名【导入模块某个函数】
这个第二种主要是应对一种情况,就是模块名比较长,每次调用前还要输入模块名,这样有时候也就有点麻烦了;采用这种方式后,可以直接将模块里面的函数导入,这样我们就可以不用加模块名而直接引用了。【不过这样很容易导致命名混乱,强烈不推荐使用】
第三种:from 模块名 as 新名字
这种最好了,估计用这种的最多。
至此,初学部分结束,下面开启看书总结。