魔法方法
一. 基本的魔法方法
二.算术运算符
三.反算术运算符
四.增量赋值运算符
五一元运算符
六.属性访问
七. 描述符
八.定制序列
九.迭代器
十.习题
十一.总结
一.基本的魔法对象
1.1__init__
__init__(self[, ...])
在上一章类与对象中,有对__init__
的应用,其主要作用是在类的实例化时,执行一些初始化程序。
class Person:
def __init__(self,name,height,weight):
self.name=name
self.height=height
self.weight=weight
def intro(self):
print('我叫%s,我身高%dcm,体重%dkg'%(self.name,self.height,self.weight))
who=Person('小王',180,75)
who.intro() #我叫小王,我身高180cm,体重75kg
在创建实例who的时候,__init__
就会被调用,接收传进来的参数。值得注意的是,__init__
不能有返回值,它的返回值只能是none,如果return别的东西会报错。
1.2__new__
本来以为初始化方法__init__
是第一个执行的,没想到还有比它更早的,那就是__new__
,全拼__new__(cls[, ...])
。
__new__
只有第一个参数cls
是自己的,其他的都要传给__init__
。但是__new__
不一定会进入__init__
,只有它返回当前cls
时才会进入,因为__new__
可以调用其他类的构造方法或者直接返回别的对象来作为本类的实例.
class One(object):
def __init__(self, value):
self.value = value
def __new__(cls, *args, **kwargs):
return Two(*args, **kwargs)
#因为这个return的存在,本来应该执行__init__的,结果跑去执行Two()了
def ok(self): #One里的方法
print('%s,are you OK?' % (self.value))
class Two(object):
def __init__(self, value):
self.value = value
def ok(self): #Two里的方法
print('%s,how are you?' % (self.value))
who=One('小王')
who.ok() #小王,how are you?
上面这个例子中,__new__
返回的是__Two()__
,不是当前的cls
,因此就没有执行__init__
。
1.2__del__
__del__
是python中用来回收对象的方法,一个对象被几个变量引用就会计数为几,如果为0时可以用__del__
回收。
class C(object):
def __init__(self):
print('into C __init__')
def __del__(self):
print('into C __del__')
c1 = C()
# into C __init__
c2 = c1
c3 = c2
del c3
del c2
del c1
# into C __del__
然后神奇的一幕发生了,用jupyter notebook和pycharm竟然出现了不同的结果!!!
1.3__str__和__repr__
-
__str__(self)
:- 当你打印一个对象的时候,触发
__str__
- 当你使用
%s
格式化的时候,触发__str__
str
强转数据类型的时候,触发__str__
- 当你打印一个对象的时候,触发
-
__repr__(self)
:repr
是str
的备胎- 有
__str__
的时候执行__str__
,没有实现__str__
的时候,执行__repr__
repr(obj)
内置函数对应的结果是__repr__
的返回值- 当你使用
%r
格式化的时候 触发__repr__
class Cat:
"""定义一个猫类"""
def __init__(self, new_name, new_age):
"""在创建完对象之后 会自动调用, 它完成对象的初始化的功能"""
self.name = new_name
self.age = new_age
def __str__(self):
"""返回一个对象的描述信息"""
return "名字是:%s , 年龄是:%d" % (self.name, self.age)
def __repr__(self):
"""返回一个对象的描述信息"""
return "Cat:(%s,%d)" % (self.name, self.age)
def eat(self):
print("%s在吃鱼...." % self.name)
def drink(self):
print("%s在喝可乐..." % self.name)
def introduce(self):
print("名字是:%s, 年龄是:%d" % (self.name, self.age))
# 创建了一个对象
tom = Cat("汤姆", 30)
print(tom) # 名字是:汤姆 , 年龄是:30
print(str(tom)) # 名字是:汤姆 , 年龄是:30
print(repr(tom)) # Cat:(汤姆,30)
tom.eat() # 汤姆在吃鱼....
tom.introduce() # 名字是:汤姆, 年龄是:30
二.算术运算符
算术运算符 | 描述 |
---|---|
__add__(self, other) | 定义加法的行为:+ |
__sub__(self, other) | 定义减法的行为:- |
__mul__(self, other) | 定义乘法的行为:* |
__truediv__(self, other) | 定义真除法的行为:/ |
__floordiv__(self, other) | 定义整数除法的行为:// |
__mod__(self, other) | 定义取模算法的行为:% |
__divmod__(self, other) | 定义当被 divmod() 调用时的行为 |
divmod(a, b) | 把除数和余数运算结果结合起来, 返回一个包含商和余数的元组 (a // b, a % b) 。 |
__pow__(self, other[, module]) | 定义当被 power() 调用或 ** 运算时的行为 |
__lshift__(self, other) | 定义按位左移位的行为:<< |
__rshift__(self, other) | 定义按位右移位的行为:>> |
__and__(self, other) | 定义按位与操作的行为:& |
__xor__(self, other) | 定义按位异或操作的行为:^ |
__or__(self, other) | 定义按位或操作的行为:| |
三.反算术运算符
算术运算符 | 描述 |
---|---|
__radd__(self, other) | 定义加法的行为:+ |
__rsub__(self, other) | 定义减法的行为:- |
__rmul__(self, other) | 定义乘法的行为:* |
__rtruediv__(self, other) | 定义真除法的行为:/ |
__rfloordiv__(self, other) | 定义整数除法的行为:// |
__rmod__(self, other) | 定义取模算法的行为:% |
__rdivmod__(self, other) | 定义当被 divmod() 调用时的行为 |
__rpow__(self, other[, module]) | 定义当被 power() 调用或 ** 运算时的行为 |
__rlshift__(self, other) | 定义按位左移位的行为:<< |
__rrshift__(self, other) | 定义按位右移位的行为:>> |
__rand__(self, other) | 定义按位与操作的行为:& |
__rxor__(self, other) | 定义按位异或操作的行为:^ |
__ror__(self, other) | 定义按位或操作的行为:| |
class Nint(int):
def __radd__(self, other):
return int.__sub__(other, self) # 注意 self 在后面
a = Nint(5)
b = Nint(3)
print(a + b) # 8
print(1 + b) # -2
四.增量赋值运算符
算术运算符 | 描述 |
---|---|
__iadd__(self, other) | 定义赋值加法的行为:+= |
__isub__(self, other) | 定义赋值减法的行为:-= |
__imul__(self, other) | 定义赋值乘法的行为:*= |
__itruediv__(self, other) | 定义赋值真除法的行为:/= |
__ifloordiv__(self, other) | 定义赋值整数除法的行为://= |
__imod__(self, other) | 定义赋值取模算法的行为:%= |
__ipow__(self, other[, modulo]) | 定义赋值幂运算的行为:**= |
__ilshift__(self, other) | 定义赋值按位左移位的行为:<<= |
__irshift__(self, other) | 定义赋值按位右移位的行为:>>= |
__iand__(self, other) | 定义赋值按位与操作的行为:&= |
__ixor__(self, other) | 定义赋值按位异或操作的行为:^= |
__ior__(self, other) | 定义赋值按位或操作的行为:|= |
五.一元运算符
算术运算符 | 描述 |
---|---|
__neg__(self) | 定义正号的行为:+x |
__pos__(self) | 定义负号的行为:-x |
__abs__(self) | 定义当被abs() 调用时的行为 |
__invert__(self) | 定义按位求反的行为:~x |
六.属性访问
算术运算符 | 描述 |
---|---|
__getattr__(self, name) | 定义当用户试图获取一个不存在的属性时的行为。 |
__getattribute__(self, name) | 定义当该类的属性被访问时的行为 (先调用该方法,查看是否存在该属性,若不存在,接着去调用__getattr__`)。 |
__setattr__(self, name, value) | 定义当一个属性被设置时的行为。 |
__delattr__(self, name) | 定义当一个属性被删除时的行为。 |
七. 描述符
算术运算符 | 描述 |
---|---|
__get__(self, instance, owner) | 用于访问属性,它返回属性的值。 |
__set__(self, instance, value) | 将在属性分配操作中调用,不返回任何内容。 |
__del__(self, instance) | 控制删除操作,不返回任何内容。 |
八.定制序列
协议(Protocols)与其它编程语言中的接口很相似,它规定你哪些方法必须要定义。然而,在 Python 中的协议就显得不那么正式。事实上,在 Python 中,协议更像是一种指南。
容器类型的协议
- 如果说你希望定制的容器是不可变的话,你只需要定义
__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, item):
self.count[item] += 1
return self.values[item]
c1 = CountList(1, 3, 5, 7, 9)
c2 = CountList(2, 4, 6, 8, 10)
print(c1[1]) # 3
print(c2[2]) # 6
print(c1[1] + c2[1]) # 7
print(c1.count)
# {0: 0, 1: 2, 2: 0, 3: 0, 4: 0}
print(c2.count)
九.迭代器
迭代器是访问集合元素的一种方式,能够记住遍历的位置的对象,从第一个元素开始访问,直到所有的元素访问完毕,只能往前不能后退。
很常用的迭代器是for in
a = [1,2,3,4,5]
it=iter(a)
print(next(it)) #1
print(next(it)) #2
print(next(it)) #3
print(next(it)) #4
print(next(it)) #5
print(next(it)) #StopIteration
把一个类作为一个迭代器使用需要在类中实现两个魔法方法 __iter__()
与 __next__()
。
__iter__(self)
定义当迭代容器中的元素的行为,返回一个特殊的迭代器对象, 这个迭代器对象实现了__next__()
方法并通过StopIteration
异常标识迭代的完成。__next__()
返回下一个迭代器对象。StopIteration
异常用于标识迭代的完成,防止出现无限循环的情况,在__next__()
方法中我们可以设置在完成指定循环次数后触发StopIteration
异常来结束迭代。
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(100)
for each in fibs:
print(each, end=' ')
# 1 1 2 3 5 8 13 21 34 55 89
十.生成器
在python中,使用yield
的函数被称为生成器,生成器生成的函数只能用于迭代操作,在调用生成器运行的过程中,每次遇到 yield
时函数会暂停并保存当前所有的运行信息,返回 yield
的值, 并在下一次执行 next()
方法时从当前位置继续运行。
def myGen():
print('生成器执行!')
yield 1
yield 2
myG = myGen()
print(next(myG))
# 生成器执行!
# 1
print(next(myG)) # 2
print(next(myG)) # StopIteration
myG = myGen()
for each in myG:
print(each)
'''
生成器执行!
1
2
'''
【例子】用生成器实现斐波那契数列。
def libs(n):
a = 0
b = 1
while True:
a, b = b, a + b
if a > n:
return
yield a
for each in libs(100):
print(each, end=' ')
# 1 1 2 3 5 8 13 21 34 55 89
十一.习题
1、上面提到了许多魔法方法,如__new__
,__init__
, __str__
,__rstr__
,__getitem__
,__setitem__
等等,请总结它们各自的使用方法。
__new__
:类实例化时调用的第一个方法,方法的参数将由它传入__init__
,并且它可以决定调不调用__init__
方法。__init__
:函数初始化时执行的方法。__str__
:在使用print()时会自动触发__str__
。__rstr__
:生成随机字符串,但是也需要传入参数。__getitem__
:一般会在索引的时候进行试用。__setitem__
该方法应该按一定的方式存储和key相关的value。
2、利用python做一个简单的定时器类
要求:
- 定制一个计时器的类。
start
和stop
方法代表启动计时和停止计时。- 假设计时器对象
t1
,print(t1)
和直接调用t1
均显示结果。 - 当计时器未启动或已经停止计时时,调用
stop
方法会给予温馨的提示。 - 两个计时器对象可以进行相加:
t1+t2
。 - 只能使用提供的有限资源完成。
import time
class Timer():
def start(self):
global a #定义全局变量a
global b #定义全局变量a
a=time.time() #获取当前时间
print('启动计时器')
while(1):
i=input() #输入指令
if i == 'print(t1)' : #只有这三个指令能够使时钟停止
b = time.time() - a
print(round(b,2))
break
elif i=='a.stop()':#只有这三个指令能够使时钟停止
b = time.time() - a
print(round(b,2))
break
elif i=='t1': #只有这三个指令能够使时钟停止
b = time.time() - a
print(round(b,2))
break
else:
print('请输入正确的指令')
t1=Timer() #将Timer()实例化
t1.start() #启动定时器1
t1b=b #记录t1的时间
t2=Timer() #将Timer()实例化
t2.start() #启动定时器2
t2b=b #记录t2的时间
t=t1b+t2b #t1和t2的时间和
print(round(t,2))
能完成题目的要求,但是代码不是题目想要的那种哈哈哈哈,纯粹偷换概念写的。
十二.总结
这是带有魔法的一章,据说非常强大,学得有点云里雾里,知识点越来越难了。加油打工人!