python魔术方法一
实例化
方法 |
意义 |
__new__ |
实例化一个对象,该方法需要返回一个值,如果该值不是cls 的实例,则不会调用__ini__ ,该方法永远都是一个静态方法 |
class A:
# __new__ 有两个作用: 1. 实例化, 2.元类的构造过程
def __new__(cls, *args, **kwargs): # 1 构建实例,从无到有
print(cls)
print(args)
print(kwargs)
# return object.__new__(cls)
return super().__new__(cls)
def __init__(self,x,y):
print('init ~~~')
self.x = x
self.y = y
t = A(4,5) # 1. A.__new__(1) --> object.__new__(A) --> A instance 2. instance.__init__(4,5) # 实际的调用过程
print(t.x)
可视化
方法 |
意义 |
__str__ |
str() 函数、format() 函数、print() 函数调用,需要返回对象的字符串表达。如果没有定义,就去调用__repr__ 方法返回字符串表达,如果__repr__ 没有定义,就直接返回对象的内在地址信息 |
__repr__ |
内建函数repr() 对一个对象获取字符串表达。调用__repr__ 方法返回字符串表达,如果__repr__ 也没有定义,就直接返回object 的定义就是显示内存地址信息 |
__bytes__ |
bytes() 函数调用,返回一个对象的bytes 表达,即返回bytes对象 |
class Point:
def __init__(self,x,y):
self.x = x
self.y = y
def __str__(self): # 重写了__str__方法,返回一个字符串,主要是为了在面向对象的时候,打印对象的时候,能够打印出有意义的信息
return 'Point({},{})'.format(self.x,self.y)
t = Point(4,5)
print(type(t)) # <class '__main__.Point'>
print(t) # Point(4,5) 这个方法等价于 print(str(t))
print(str(t)) # Point(4,5)
class Point:
def __init__(self,x,y):
self.x = x
self.y = y
def __str__(self): # 重写了__str__方法,返回一个字符串,主要是为了在面向对象的时候,打印对象的时候,能够打印出有意义的信息
return 'Point({},{})'.format(self.x,self.y)
def __repr__(self):
return 'Point({},{})'.format(self.x,self.y)
t = Point(4,5)
print(type(t)) # <class '__main__.Point'>
print(t) # Point(4,5) 这个方法等价于 print(str(t))
print(str(t)) # Point(4,5)
print([t,str(t)],"{}".format(t)) # 到这里是没有_reper_方法的,所以会调用__str__方法 ,打印结果 [<__main__.Point object at 0x102b778e0>, 'Point(4,5)']
print(repr(t), [t, str(t)]) #
bool
方法 |
意义 |
__bool__ |
内建函数bool() ,或者对象放在逻辑表达式的位置,调用这个函数返回布尔值。没有定义__bool__() ,就找__len__() 返回长度、非0为真。如果__len__() 也没有定义,那么所有实例都返回真 |
class B:
def __bool__(self):
# return False
# return True
return bool(0)
# return bool(1)
print(bool(B())) # False
class A: pass
print(bool(A())) # True 一个实例的对象,默认是True
class B:
def __len__(self): # 容器类的实例,用长度来判断True or False
return 0
# return 1
print(bool(B())) # False
class B:
def __bool__(self):
return bool(len(self)) # 实际上等价于 len(self) => self.__len__()
# 再者bool方法和len方法同时存在,那么bool方法会优先调用
def __len__(self): # 容器类的实例,用长度来判断True or False
return 0
# return 1
print(bool(B())) # False
class B:
def __bool__(self):
print('bool...')
return False
def __len__(self):
print('len...')
return 100
t = B()
print(bool(t)) # False bool优先调用bool方法,如果没有bool方法,再调用len方法,len方法也没有,恒为True
print(len(t)) # len优先调用len方法
运算符重载
operator
模块提供以下的特殊方法,可以将类的实例使用下面的操作符来操作
运算符 |
特殊方法 |
含义 |
< , <= , == , > , >= , != |
__lt__ , __le__ , __eq__ , __gt__ , __ge__ , __ne__ |
比较运算符 |
* , / , % , ** , divmod |
__mul__ , __truediv__ , __mod__ , __pow__ , __divmod__ |
算数运算符 |
+= , -= , *= , /= , %= , **= |
__iadd__ , __isub__ , __imul__ , __itruediv__ , __imod__ , __ifloordiv__ , __ipow__ |
赋值运算符 |
class Student:
def __init__(self,name,age=20):
self.name = name
self.age = age
def __gt__(self,other):
print(self, other)
return self.age > other.age
def __ge__(self,other):
return self.age >= other.age
def __lt__(self,other):
return self.age < other.age
def __eq__(self,other):
return self.age == other.age and self.name == other.name
def __str__(self):
print('-' * 30)
return "<Student {} {}>".format(self.name,self.age)
t1 = Student('Tom',22)
t2 = Student('Jerry')
print(t1 > t2) # True # t1.__gt__(t2)
print(t1 < t2) # False # t1.__lt__(t2)
print(t1 >= t2) # True # t1.__ge__(t2)
print(t1 <= t2) # False # t1.__le__(t2)
print(t2 >= t1) # False # t2.__ge__(t1)
t3 = Student('Tom',22)
print(t1 == t3) # True # t1.__eq__(t3)
# 计算两同学的分数差
class Student:
def __init__(self,name,score):
self.name = name
self.score = score
def __repr__(self):
return "<Student {} {}>".format(self.name,self.score)
def __sub__(self,other):
print(self)
print(other)
return self.score - other.score
def __isub__(self,other):
print('isub...')
self.score -= other.score
return self
t1 = Student('Tom',100)
t2 = Student('Jerry',90)
print(t1 - t2) # 这里用的是__sub__方法
tom = Student('Tom',100)
jerry = Student('Jerry',90)
tom -= jerry # <Student Tom 10> # 这里用的是__isub__方法
print(type(tom))
print(tom)
class Student:
def __init__(self,name,score):
self.name = name
self.score = score
def __repr__(self):
return "<Student {} {}>".format(self.name,self.score)
def __sub__(self,other):
print(self)
print(other)
return self.score - other.score
def __isub__(self,other):
print('isub...')
self.score -= other.score
return self
t1 = Student('Tom',100)
t2 = Student('Jerry',90)
print(t1 - t2) # 这里用的是__sub__方法
tom = Student('Tom',100)
jerry = Student('Jerry',90)
tom -= jerry # <Student Tom 10> # 这里用的是__isub__方法
print(type(tom))
print(tom)
# 思考: list 的+ 和 += 有什么区别
# list 的+ 是生成一个新的list
# list 的+= 是在原来的list上进行操作,即就地覆盖
# 思考: tuple 的+ 和 += 有什么区别
# t1 + t2 => t3
# t1 += t2 => t1 = t1 + t2 => t1 = t4
容器相关的魔术方法
方法 |
意义 |
__len__ |
内建函数 len() ,返回对象的长度(>=0的整数)。如果把对象当做容器类型看,就如同list或者dict。 |
__bool__ |
当使用 bool() 函数调用的时候,如果没有 __bool__() 方法,则会看 __len__() 方法是否存在,存在返回非0为真。 |
__iter__ |
迭代容器时调用,返回一个新的迭代器对象。 |
__contains__ |
in 成员运算符,没有实现,就调用 __iter__ 方法遍历。 |
__getitem__ |
实现 self[key] 访问。对于序列对象,key 接受整数为索引,或者切片。对于set和dict,key 为可哈希的。key 不存在引发 KeyError 异常。 |
__setitem__ |
和 __getitem__ 的访问类似,是设置值的方法。 |
__missing__ |
字典或其子类使用__getitem__() 调用时,key 不存在执行该方法 |
class A(dict):
def __missing__(self,key):
print(key)
return 0
t = A(a=1,b='abc')
print(t,type(t),isinstance(t,dict)) # {'a': 1, 'b': 'abc'} <class '__main__.A'> True
print(t['k']) # k 0
class Cart:
def __init__(self):
self.__items = []
def additem(self,item):
self.__items.append(item)
return self
# return self + item
def __len__(self):
return len(self.__items)
def __repr__(self):
return str(self.__items)
def __iter__(self): # 实例是可迭代对象
#return iter(self.__items)
yield from self.__items
def __getitem__(self,index):
print(index, '+++')
return self.__items[index]
def __setitem__(self,index,value):
self.__items[index] = value
def __add__(self,other):
# self.__items.append(other)
return self.additem(other)
cart = Cart()
cart.additem('apple')
cart.additem('banana')
cart.additem(3)
print(len(cart)) # 3
print(cart) # ['apple', 'banana', 3]
for i in cart:
print(i)
# apple
# banana
# 3
print(3 in cart) # True
print(2 in cart) # False
print(cart[1]) # banana
cart[1] = 'banana2'
print(cart) # ['apple', 'banana2', 3]
cart.additem('orange').additem('pear')
print(cart) # ['apple', 'banana2', 3, 'orange', 'pear']
print(cart + 300 + 200 ) # ['apple', 'banana2', 3, 'orange', 'pear', 300, 200]
方法 |
意义 |
__call__ |
类中定义一个该方法,实例 就可以像函数一样使用 |
class Fib:
def __init__(self):
self.items = [0,1,1]
def __str__(self):
return str(self.items)
def __len__(self):
return len(self.items)
def __iter__(self):
return iter(self.items)
def __getitem__(self,index):
if index < 0:
raise IndexError("Index out of range")
for i in range(len(self.items),index+1):
self.items.append(self.items[i-1] + self.items[i-2])
return self.items[index]
# def __call__(self,index):
# return self[index]
# __call__ = __getitem__
# def __call__(self,index): # f(3) ==> f.__call__(3)
# return self[index] # req turn self.__getitem__(index)
__call__ = __getitem__
f = Fib()
print(f[10]) # 55
print(str(f)) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
for i in f:
print(i)
print(len(f))
print('---')
print(f[11]) # 89
print(f(11)) # 89
上下文管理
当一个对象同时实现了__enter__()
和__exit__()
方法,它就属于上下文管理的对象
方法 |
意义 |
__enter__ |
进入此对象下关的上下文。如果存在该方法,with 语法会把该方法的返回值作为绑定到as 子句中指定的变量上 |
__exit__ |
退出与此对象的上下文 |
with open('test') as f: # open的作用是打开文件,返回一个文件对象,with是对文件对象的一个上下文管理
pass
那么对with
执行顺序的描述
AOP
面向切面编程
class A:
def __init__(self):
print(1,'init start ----')
def __enter__(self):
print(2,'enter start ----')
def __exit__(self, exc_type, exc_val, exc_tb):
print(3,'exit start ----')
with A():
print('4, with start ----')
import time
time.sleep(3)
print('5, with end ----')
# 1 init start ----
# 2 enter start ----
# 4, with start ----
# 5, with end ----
# 3 exit start ----
class A:
def __init__(self):
print(1,'init start ----')
def __enter__(self):
return self
print(2,'enter start ----')
def __exit__(self, exc_type, exc_val, exc_tb):
print(3,'exit start ----')
t1 = A()
with t1 as t2: # t2 = t1.__enter__() --> t2 = t1
print(t1," ", t2)
print( t1 == t2)
print( t1 is t2)
import time
import datetime
def add(x ,y):
time.sleep(3)
return x +y
class A:
def __init__(self,fn):
print(1,'init start ----')
self.__fn = fn
def __enter__(self):
print(2,'enter start ----')
self.start = datetime.datetime.now()
return self.__fn
def __exit__(self, exc_type, exc_val, exc_tb):
print(3,'exit start ----')
delta = (datetime.datetime.now() - self.start).total_seconds()
print('{} took {}s'.format(self.__fn.__name__,delta))
with A(add) as t:
print(t(100,200))
- 增强功能
- 在代码执行的前后增加代码,以增强其功能。类似装饰器的功能。
- 资源管理
- 打开了资源需要关闭,例如文件对象、网络连接、数据库连接等。
- 权限验证
- 在执行代码之前,做权限的验证,在
__enter__中处理
from contextlib import contextmanager
import time
import datetime
def add(x, y):
time.sleep(3)
return x + y
@contextmanager
def timeit(fn):
print('之前的调用')
start = datetime.datetime.now()
try:
yield fn
finally:
print('之后的调用')
delta = (datetime.datetime.now() - start).total_seconds()
print('{} took {}s'.format(fn.__name__,delta))
with timeit(add) as t:
print(t(100,200))
# 之前的调用
# 300
# 之后的调用
# add took 3.000754s
利用简单的yeild
一次的生成器管理