Python魔术方法
分类:
1.创建,初始化与销毁 __init__.__del__
类实例化调用__init__;当运行结束,.del 标识符,引用计数为0的时候调用__del__
2.hash
1.内建函数hash()调用的返回值,返回一个整数,如果定义这个方法该类的实例可hash; 2.hash是为了提供set,dict的key,hash可以冲突,冲突的时候用__eq__比较内容,实现去重 3.类一般都是可以hash,由于继承基类的属性,但是没有事项__hash__,我们认为不可以hash 4.list bytearray 内部用 __hash__ = None 实现不可以hash
class Point: def __init__(self,x,y): self.x = x self.y = y def __hash__(self): #return 就是一个元祖,想要返回对象 Point(self.x,self,y) return hash((self.x,self.y)) def __eq__(self,other): #运算符重载 p1 == p2,判断对象是否相等,返回bool return (self.x,self.y) == (other.x,other.y)
# return self.x ==other.x and self.y == other.y def __repr__(self): return 'Point.x={},Point.y={}'.format(self.x,self.y)
3.bool
1.内建函数bool(),或者对象放在逻辑表达式的位置,调用这个函数返回bool,没有定义__bool__(),就找__len__()的长度,非0为真,如果__len__()也没有定义,所有实例都返回真
class A: def __len__(self): return 100 __hash__ = None if A(): print("real a") bool(A) bool(A())
4.可视化
1.__repr__内建函数repr()对一个对象获取字符串表达,调用魔术方法返回字符串表达,如果没有定义,就直接返回object的定义就式显示内存的地址信息 2.__str__,str()函数,内建函数format(),print()函数调用,需要返回对象的字符串表达,如果没有定义,就去调用__repr__方法返回字符串表达,如果__repr__也没有定义,就直接返回对象的内存地址信息
3.__bytes__,bytes()函数调用,返回一个对象的bytes表达,返回bytes对象
5.运算符重载
1.比较运算符:lt,le,eq,gt,ge,ne -->[<, <=, == ,> ,>= ,!=] 2.算数运算符:add,sub,mul,truediv,mod,floordiv,pow,divmod -->[+-*/% // ** divmod] 3.iadd,isub,imul,itruediv,imod,ifloordiv,ipow --> [+=,-=,*=,/=,%=,//=,**=]
class Point: def __init__(self,x,y): self.x = x self.y = y def __sub__(self,other): return Point(self.x-other.x,self.y-other.y) def __add__(self,other): return Point(self.x+other.x,self.y+other.y) def __repr__(self): return '<Point.x {},Point.y>'.format(self.x,self.y)
6.容器和大小
1.__len__:内建函数len(),返回对象长度,
2.__iter__:迭代容器的时候,调用,返回一个新的迭代对象(没有这个方法是instance(cart,Iterable)返回false,当for i in cart:调用__iter__)
3.__contains__:in成员运算符,没有实现就掉用__iter__方法遍历
4.__getitem__:实现self[key]访问,序列对象,key接受整数位索引,或者切片,对于set,dict,key为hashable,key不存在引发保存,索引访问
5.__setitem__:是设置值的方法,索引赋值
6.__missing__:字典或其子类使用__getitem__调用时,key不存在执行该方法
class Cart: def __init__(self): self.items = [] def add(self,value): self.items.append(value) def __len__(self): return len(self.items) def __iter__(self): return iter(self.items)
#yield from self.items def __getitem__(self, item): return self.items[item] def __setitem__(self, key, value): self.items[key] = value def __str__(self): return str(self.items) def __add__(self, other): self.items.append(other) return self cart = Cart() print(cart) cart.add(1) cart.add(2) cart.add(3) print(cart+4+5+6) #链式变成实现加法
print(cart.__add__(17).__add__(18)
7.可调用对象
1.Pyhon一切皆对象,函数也不例外 2.__call__:类中定义一个该方法,实例就可以像函数一样调用 3.可调用对象:定义一个类,并实例化得到其实例,将实例像函数一样调用 4.要实现类实例的函数调用可以把函数调用写在__call__()内 5.可调用对象可以实现类装饰器; 见下面例子
class Point: def __init__(self,x,y): self.x = x self.y = y def __call__(self,*args,**kwargs): return "<Point {}:{}>".format(self.x,self.y) p = Point(4,5) p()
class Adder: def __call__(self,*args): ret = 0 for x in args: ret+=x self.ret = ret return ret p =Adder() p(10,10) p.ret
斐波数列用可调用方式并包装成容器实现
class Fib: def __init__(self): self.item=[0,1,1] def __call__(self,num): if num<len(self.item): return self.item[num] for i in range(len(self.item),num+1): self.item.append(self.item[i-1]+self.item[i-2]) return self.item def __iter__(self): return iter(self.item) def __len__(self): return len(self.item) def __getitem__(self,key): return self.item[key] fib = Fib() for i in fib: print(i) len(fib) fib[2]
class Fib: def __init__(self): self.item=[0,1,1] def __call__(self,num):#num为形参,fib()先调用call,return在调用getitem魔术方法 return self[num] def __iter__(self): return iter(self.item) def __len__(self): return len(self.item) def __getitem__(self,num): #这个num为实参,后调用getitem if num<len(self.item): return self.item[num] for i in range(len(self.item),num+1): self.item.append(self.item[i-1]+self.item[i-2]) # return self.item[num] def __str__(self): return str(self.item) __repr__=__str__ fib = Fib() fib(5) print(fib(5))
import time from datetime import datetime from functools import wraps,update_wrapper class Timeie: """this is Timeit""" def __init__(self,fn): self.fn = fn #update_wrapper(self,fn) wraps(fn)(self) # wrapper = wraps(fn)(wrapper) def __call__(self,x,y): self.start=datetime.now() ret = self.fn(x,y) delta = (datetime.now()-self.start).total_seconds() print(delta) return ret @Timeie #add = Timeit(add) def add(x,y): """this is add""" time.sleep(2) return x+y add(4,5)
8.上下文管理
1.当一个对象同时实现了__enter__()和__exit__()方法,它就属于上下文管理的对象 2.__enter__:进入与次相关的上下文,如果存在该方法,with语法就会把该方法返回 __enter__方法返回值就是上下文中使用的对象,with语法会把他的返回值赋值给as子句的变量 3.__exit__:退出与此对象相关的上下文;方法返回一个等效的True值,压制异常,否则继续抛出异常 4.应用场景:增强功能,资源管理,权限验证(在执行代码前,做权限验证,在__enter__中处理) 5.contextlib库,@contextlib.contextmanager ;用来装饰函数,函数必须返回生成器,只有一个yield值;增加try保证exit执行 try: yield 5 #yield之前当作enter,之后当作exit,;yield值当作enter返回值 finally: print('exit')
class Point: def __init__(self): print('init') def __enter__(self): print('enter')
return self def __exit__(self, exc_type, exc_val, exc_tb): print('exit') with Point() as f: #output init-->enter-->f-->exit print('f') 实例化对象的时候,并不会调用enter,进入with语句块的时候调用_enter__方法,然后执行语句体,
最后离开with语句快的时候调用__exit__方法,如果with语句块发生异常,enter,exit照样执行,上下文管理最安全
class Point: def __init__(self): print('init') def __enter__(self): print('enter') return self def __exit__(self, exc_type, exc_val, exc_tb): print('exit') print(exc_type,exc_val,exc_tb) return 0 p = Point() with p as f: #output init-->enter-->f-->exit raise Exception("new erroe") print('f')
import time from datetime import datetime def add(x,y): time.sleep(2) return x+y class Timeit: def __init__(self,fn): self.fn = fn
def __enter__(self): self.start=datetime.now() return self def __exit__(self, exc_type, exc_val, exc_tb): delta = (datetime.now()-self.start).total_seconds() print(delta) def __call__(self,x,y): return self.fn(x,y) p = Timeit(add)
print(p) #<__main__.Timeit object at 0x05100810>
print(Timeit(add)) #<__main__.Timeit object at 0x05219750>
with p as f: #with Timeit(add) as f ;print-->false print(p==f) f(5,6)
9.反射:动态增加属性
1.getattr(object,name,default) 通过name返回object的属性值,当属性值不存在的时候,将使用default返回,如果没有default,抛出异常,name必须为字符串 2.setattr(object,name,value) object的属性存在,则覆盖,不存在新增 3.hasattr(object,name) 判断对象是否有这个名字的属性,name必须为字符串
class Dispatcher: def __init__(self): self.run() def cmd1(self): print('i am cmd1') def cmd2(self): print("i am cmd2") def run(self): while True: cmd = input('please input a command').strip() if cmd == 'quit': break getattr(self,cmd,lambda:print('unkown{}'.format(cmd)))()
1.__getattr__():一般属性会按照继承关系找,如果找不到,就会执行__getattr__()方法,没有抛错 2.__setattr__():实例通过.设置属性,如同self.x = x 就会调用__setattr__(),属性要加到__dict__就要自己完成 3.__delattr__():可以阻止通过实例删除属性的操作,但是通过类依然可以删除属性 4.__getattribute__():实例的所有属性访问都会调用__getattribute方法,它阻止了属性的查找,该方法应该返回计算后的值,或者抛出个异常;return的值作为属性的查找结果,如果抛错,调用__getattr__表示属性没有找到
class Base: n = 0 class Point(Base): z = 6 def __init__(self,x,y): print('init') self.x = x print('init2') self.y = y def show(self): print(self.x,self.y) def __getattr__(self,item): setattr(self,item,0) # getattr(self,item)产生递归 # return 'missing {}'.format(item) def __setattr__(self,key,value): #self.x 就会调用这个函数,如果不操作__dict__,就是空字典 print('setattr') print("setattr {}={}".format(key,value)) self.__dict__[key] = value # setattr(self,key,value)产生递归 # getattr(self,key,value)产生递归 def __delattr__(self,item): print('can not del {}'.format(item)) p1 = Point(3,4) p1.__dict__ p1.aa=100 #aa:100 p1.t #t:0 del p1.x #output can not del x del Point.z#删除类的属性
class Base: n = 0 class Point(Base): z = 6 def __init__(self,x,y): print('init') self.x = x self.y = y def __getattr__(self,item): return 'missing{}'.format(item) def __getattribute__(self,item):#所有实例的随性都从调用这个函数开始 print('getattribute') return object.__getattribute__(self,item)
属性实例查找顺序:
实例调用__getattribute__()-->instance.__dict__-->instance.__class__.__dict__-->继承德祖先类直到object-->调用__getattr__()
10.描述器
本文为原创文章,转载请标明出处