1、属性
属性 |
含义 |
__name__ |
类、函数、方法等的名字 __dir__ |
__module__ |
类定义所在的模块名 |
__class__ |
对象或类所属的类 只是返回基类 |
__bases__ |
返回自己到object的类,类的基类元组,顺序为在基类列表中出现的顺序。 |
__doc__ |
类,函数的文档字符串,如果没有定义则为None。 |
__mro__ |
类的不是实例的。类的mro,class.mro()返回的结果保存在__mro__中。 |
__dict__ |
类或实例属性,可写的字典。 |
标识符和名称两码回事。。
2、查看属性
方法 |
意义 |
__dir__(只是影响实例)*** |
返回类或者对象的所有成员名称列表,dir()函数就是调用__dir__(),如果提供__dir__(),则返回属性的列表,否则会尽量从__dict__属性中收集信息。 收集很多信息 |
如果dir([object])参数obj包含方法__dir__(),该方法则被调用,如果参数obj中不包含__dir__(),该方法将最大限度的收集参数信息。
dir ()对不同类型的对象具有不同的行为。
如果对象是木块对象,返回的列表包含模块的属性名。
如果对象是类型或者类对象,返回的列表包含的属性名,以及他的基类的属性名。
否则,返回列表包含对象的属性名,他的类的属性名和类的基类的属性名。
import animal from animal import Animal class Cat(Animal): x = ‘cat‘ y =‘abcd‘ class Dog(Animal): def __dir__(self): return [‘dog‘] print(‘-------‘) print(1,‘current modules \‘s names = {}‘.format(dir())) #模块名词空间内的属性。 print(2,‘anmial modules \‘s names = {}‘.format(dir(animal))) #指定模块内的属性。 print(3,"object ‘s __dict__ = {}".format(sorted(object.__dict__.keys()))) #object 的字典。 print(4,"Animal‘s dir()={}".format(dir(Animal))) #类Animal的dir() print(5,"Cat‘s dir() = {}".format(dir(Cat))) #类Cat的dir() print(‘++++++++++++++++‘) tom = Cat(‘tom‘) print(6,sorted(dir(tom))) #tom 的属性,cat类以及祖先类的属性。 print(7,sorted(tom.__dir__())) #和上面一致, print(8,sorted(set(tom.__dict__.keys())|set(Cat.__dict__.keys())|set(object.__dict__.keys()))) print(9,"Dog‘s dir = {}".format(dir(Dog))) dog = Dog(‘snoppy‘) print(10,dir(dog)) print(11,dog.__dict__)
animal Module‘s names = [‘Animal‘, ‘__builtins__‘, ‘__cached__‘, ‘__doc__‘, ‘__file__‘, ‘__loader__‘, ‘__name__‘, ‘__package__‘, ‘__spec__‘]
-------
1 current modules ‘s names = [‘Animal‘, ‘Cat‘, ‘Dog‘, ‘__builtins__‘, ‘__cached__‘, ‘__doc__‘, ‘__file__‘, ‘__loader__‘, ‘__name__‘, ‘__package__‘, ‘__spec__‘, ‘animal‘]
2 anmial modules ‘s names = [‘Animal‘, ‘__builtins__‘, ‘__cached__‘, ‘__doc__‘, ‘__file__‘, ‘__loader__‘, ‘__name__‘, ‘__package__‘, ‘__spec__‘]
3 object ‘s __dict__ = [‘__class__‘, ‘__delattr__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__gt__‘, ‘__hash__‘, ‘__init__‘, ‘__le__‘, ‘__lt__‘, ‘__ne__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘]
4 Animal‘s dir()=[‘__class__‘, ‘__delattr__‘, ‘__dict__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__gt__‘, ‘__hash__‘, ‘__init__‘, ‘__le__‘, ‘__lt__‘, ‘__module__‘, ‘__ne__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘__weakref__‘, ‘x‘]
5 Cat‘s dir() = [‘__class__‘, ‘__delattr__‘, ‘__dict__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__gt__‘, ‘__hash__‘, ‘__init__‘, ‘__le__‘, ‘__lt__‘, ‘__module__‘, ‘__ne__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘__weakref__‘, ‘x‘, ‘y‘]
++++++++++++++++
6 [‘_Animal__age‘, ‘__class__‘, ‘__delattr__‘, ‘__dict__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__gt__‘, ‘__hash__‘, ‘__init__‘, ‘__le__‘, ‘__lt__‘, ‘__module__‘, ‘__ne__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘__weakref__‘, ‘name‘, ‘weight‘, ‘x‘, ‘y‘]
7 [‘_Animal__age‘, ‘__class__‘, ‘__delattr__‘, ‘__dict__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__gt__‘, ‘__hash__‘, ‘__init__‘, ‘__le__‘, ‘__lt__‘, ‘__module__‘, ‘__ne__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘__weakref__‘, ‘name‘, ‘weight‘, ‘x‘, ‘y‘]
8 [‘_Animal__age‘, ‘__class__‘, ‘__delattr__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__gt__‘, ‘__hash__‘, ‘__init__‘, ‘__le__‘, ‘__lt__‘, ‘__module__‘, ‘__ne__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘name‘, ‘weight‘, ‘x‘, ‘y‘]
9 Dog‘s dir = [‘__class__‘, ‘__delattr__‘, ‘__dict__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__gt__‘, ‘__hash__‘, ‘__init__‘, ‘__le__‘, ‘__lt__‘, ‘__module__‘, ‘__ne__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘__weakref__‘, ‘x‘]
10 [‘dog‘]
11 {‘name‘: ‘snoppy‘, ‘_Animal__age‘: 10, ‘weight‘: 20}
3、魔术方法***
分类:
创建、初始化与销毁
__init__ 初始化时候调用。(做资源申请,属性的建立等,存放数据等。)
没有返回值。返回值是new函数做的。为实例编辑属性。属性在实例自己的dict的字典里面。
__del__ 引用计数为0的时候。占用资源清理工作。不清理实例本身,只是清理连接等,(网络连接,直接联系的是打开和关闭资源等。)
hash: (__eq__方法等等,hash函数,把数据散列,把原数据散列,输入一定,输出也是一定的。) 幂等性。不过hash多少次,hash只是作为门牌号码,值怎么放hash不管。去重的话用到了__eq__函数。
bool : (做等效处理,如果没有__bool__方法,会找取__len__方法,)
可视化:print() __repr__ __str__ __bytes__
Str调用内建函数str,print ,format会调用魔术方法str。
没有提供str会使用repr,没有提供repr的会采用祖先类的。
运算符重载:极其有作用。
容器大小:可迭代等由其完成。In操作。 geitem 和元素有关。
可调用对象:
上下文管理:with as
反射:
描述器:
其他杂项:
4、hash
在实例调用的时候起作用
__hash__ |
内建函数hash()调用的返回值,返回一个整数。如果定了这个方法该类的实例就可hash。 |
class A: def __init__(self,name,age=18): self.name =name def __hash__(self): return 1 def __repr__(self): return self.name print(hash(A(‘tom‘))) # 1 print((A(‘tom‘),A(‘tom‘))) #(tom, tom) print([A(‘tom‘),A(‘tom‘)]) #[tom, tom] s = {A(‘tom‘),A(‘tom‘)} #{tom, tom} print(s) print({tuple(‘t‘),tuple(‘t‘)}) # {(‘t‘,)} print({(‘tom,‘,),(‘tom‘,)}) #{(‘tom‘,), (‘tom,‘,)} print({‘tom‘,‘tom‘}) #{‘tom‘}
........ class A: def __init__(self,name,age=18): self.name =name def __hash__(self): return 1 def __eq__(self, other): 等等函数作用 return self.name == other.name def __repr__(self): return self.name print(hash(A(‘tom‘))) # 1 print((A(‘tom‘),A(‘tom‘))) #(tom, tom) print([A(‘tom‘),A(‘tom‘)]) #[tom, tom] s = {A(‘tom‘),A(‘tom‘)} #{tom} print(s) print({tuple(‘t‘),tuple(‘t‘)}) # {(‘t‘,)} print({(‘tom‘,),(‘tom‘,)}) #{(‘tom‘,)} print({‘tom‘,‘tom‘}) #{‘tom‘}
...................................................
什么是hash:解决能否hash的数据,通过hash函数散列成一个范围内的某个值。
散列。把散列值作为存储数据的位置。整数例外,特殊。 y=hash(x)
特点是:值发生一点变化,都会发生散列值的巨大的变化。
方法 |
意义 |
__eq__ |
对应==操作符,判断两个对象是够相等,返回bool值 |
__hash__ = None
不可hash 就是以上的设置。
__hash__方法只是返回一个hash值作为set的key,但是去重还是需要__eq__来判断两个对象是否相等。
hash值相等,只是hash冲突,不能说明两个值是相等的
因此,一般来说提供__hash__方法只是为了作为set或者dict的key,所以去重要求同时提供__eq__方法。
hash 只是解决了数据放在哪里的问题(数据存放问题)。不解决两个值一样的处理问题。(不不比较值相等的问题。)
(只是门牌号码,存放多少个值就不一样。)
不可hash对象isinstance(p1,collections.hashable)一定为False。
去重需要提供__eq__方法。
练习:设计二维坐标系,使其成为可hash类型,比比较两个坐标的实例是否相等。
class Point: def __init__(self,x,y): self.x = x self.y = y # def finish(self): # return self.x,self.y # def __hash__(self): return hash((self.x,self.y)) def __eq__(self, other): return self.x == other.x and self.y == other.y def __repr__(self): return ‘{}{}‘.format(self.x,self.y) p1 = Point(4,5) p2 = Point(4,5)
为什么list类实例不可以hash呢。
因为源码中有一句__hash__ =None ,所以如果调用了__hash__就相当于调用了None,一定报错,如果一个类不能hash就把__hash__设置为None。
.....................................................................................
5、bool
方法 |
意义 |
__bool__ |
内建函数bool(),或者对象放在逻辑表达式的位置,调用这个函数返回布尔值,没有定义__bool__(),就找__len__()返回长度,非0为真,如果 __len__()也没有定义,那么实例就为真。 |
class A:pass print(bool(A())) if A(): print(‘real A‘) class B: def __bool__(self): return False print(bool(B)) print(bool(B())) False 因为实例化定义了为flase。 if B: print(‘real B‘) class C: def __len__(self): return 0 print(bool(C)) if C: print(‘real c‘)
..................................................................................
可视化:
方法 |
意义 |
__repr__ |
内建函数repr()对一个对象获取字符串表达,调用__repr__返回字符串表达,如果__repr__也没有定义,就直接返回object的定义就显示内存地址信息。 |
__str__ |
Str()函数,内建函数format(),print()函数调用,需要返回对象的字符串表达,如果没有定义,就去调用__repr__方法返回字符串表达,如果__repr__没有定义,就直接返回对象的内存地址信息。 |
__bytes__ |
bytes()函数调用,返回一个对象的bytes表达,即返回bytes对象。 |
Print format str首先调用str方法。没有的话会找到__repr__。
class A: def __init__(self,name,age=1): self.name = name self.age = age def __repr__(self): return ‘repr:{},{}‘.format(self.name,self.age) def __str__(self): return ‘str:{},{}‘.format(self.name,self.age) def __bytes__(self): return ‘{}is{}‘.format(self.name,self.age).encode() print(1,A(‘tom‘)) #1 str:tom,1 print(2,[A(‘tom‘)]) # 2 [repr:tom,1] print(3,([str(A(‘tom‘))])) #3 [‘str:tom,1‘] print(4,bytes(A(‘tom‘))) #4 b‘tomis1‘ print(‘str:a,1‘)
执行bytes 和list时候会报错,因为是在构建函数,后面的不是可迭代对象。
.....................................................................
6、运算符重载
Operator模块提供以下的特殊方法,可以将类的实例使用下面的操作符来操作。
运算符 |
特殊方法 |
含义 |
<,<=,==,>,>=,!= |
__lt__,__le__,__eq__,__gt__,__ge__,__ne__ |
比较运算符 |
+,-,*,/,%,//,**,divmod |
__add__,__sub__,__mul__,__truediv__,__mod__,__floordiv__,__pow__,__divmod__ |
算数运算符,移位,位运算也有对应的方法。 |
+=,-=,*=,/=,%=,//=,**= |
__iadd__,__isub__,__imul__,__itruediv__,__imod__,__ifloordiv__,__ipow__ |
class A: def __init__(self,name,age=15): self.name = name self.age = age def __sub__(self, other): return self.age - other.age def __isub__(self, other): return A(self.name,self - other) tom = A(‘tom‘) jerry = A(‘jerry‘,16) print(tom - jerry) #-1 print(tom - jerry,jerry.__sub__(tom)) #-1 1
6.1练习,完成point类设计,实现判断点相等的方法,并完成向量的加法。
class Point: def __init__(self,x,y): self.x = x self.y = y def __eq__(self, other): return self.x == other.x and self.y == other.y def __add__(self, other): return Point(self.x+other.x,self.y+other.y) def __str__(self): return ‘{},{}‘.format(self.x,self.y) p1 = Point(1,1) p2 = Point(2,2) points = (p1,p2) print(points[0]+points[1])
7、运算符重载应用场景
往往是面向对象实现的类,需要做大量的运算。Int类实现了所有操作符。
From functools import total_ordering
8、@functools.total_ordering 装饰器
__lt__, __le__ ,__eq__, __gt__ ,__ge__ 是比较大小必须实现的方法啊,利用@functools.total_ordering 装饰器就可以大大简化代码。
使用装饰器的时候__eq__必须实现其他方法,等等,大于小于等实现其一就可以。
from functools import total_ordering @total_ordering class Person: def __init__(self,name,age): self.name = name self.age = age def __eq__(self, other): return self.age == other.age def __gt__(self, other): return self.age > other.age tom = Person(‘tom‘,20) jerry = Person(‘jerry‘,18) print(tom>jerry) #True print(tom<jerry) ##False print(tom==jerry) # False print(tom>=jerry) #True print(tom<=jerry) #False
装饰器虽然大大简化代码。但是会带来性能问题,所以需要什么方法自己去创建就可以了。
一共六种比较,只是需要创建三样就可以了。
class Person: def __init__(self,name,age): self.name = name self.age = age def __eq__(self, other): return self.age == other.age def __gt__(self, other): return self.age > other.age def __ge__(self, other): return self.age >= other.age tom = Person(‘tom‘,20) jerry = Person(‘jerry‘,18) print(tom>jerry) #True print(tom<jerry) ##False print(tom==jerry) #False print(tom>=jerry) #True print(tom<=jerry) #False print(tom!=jerry) #True
9、容器相关方法
方法 |
意义 |
__len__ |
内建函数len(),返回对象的长度(>=0)的整数,如果把对象当做是容器类看,就是如同list和dict。Bool()函数调用的时候,如果没有__bool__()方法,则会看__len__()方法是否存在,存在返回非0为真。 |
__iter__ |
迭代容器时候,调用,返回一个新的迭代器对象 |
__contains__ |
In 成员运算符,没有实现,就调用__iter__方法遍历 |
__getitem__ |
实现self[key]访问,序列对象,key接受整数位索引,或者切片。对于set和dict,key为hashable,key不存在引发keyerror异常。 |
__setitem__ |
和__getitem__的访问类似,是设置值得方法。 |
__missing__ |
字典和其子类使用__getitem__() 调用时,key不存在执行该方法。 |
class A(dict): def __missing__(self, key): print(‘missing key:‘,key) return 0 a = A() print(a[‘k‘]) #missing key: k 0
9.1 为什么空字典,空字符串,空元组,空集合,空列表可以等效为False。
因为空的这些采用内建函数len,长度为0,所以等效为False。
9.2练习 :将购物车改造成方便操作的容器类
class Cart: def __init__(self): self.items = [] def __len__(self): return len(self.items) def __iter__(self): return iter(self.items) def additem(self,item): self.items.append(item) def __add__(self, other): self.items.append(other) return self def __getitem__(self,index): return self.items[index] def __setitem__(self, key, value): self.items[key] = value def __str__(self): return str(self.items) cart = Cart() cart.additem(1) cart.additem(‘abc‘) print(len(cart)) # 2 print(bool(cart)) # True for x in cart: print(x) # 1 abc print(3 in cart) # False print(cart[1]) # abc print(cart + 4 + 5 + 6) # [1, ‘abc‘, 4, 5, 6] print(cart.__add__(12).__add__(13)) # [1, ‘abc‘, 4, 5, 6, 12, 13]
__getitem__ 列表和字典都是通过key访问。
必须记住;
__missing__是和字典相关的。
.............................................完美分割线...................................................
10、可调用对象
__closure__闭包,
Callable可调用对象。
a()相当于是a.__call__()调用。
函数即对象,对象A加上()就是调用对象的__call__()方法。
方法 |
意义 |
__call__ |
类中定义一个该方法,实例就可以像函数一样调用 |
class Point: def __init__(self,x,y): self.x = x self.y = y def __call__(self, *args, **kwargs): return ‘{}:{}‘.format(self.x,self.y) p = Point(4,5) print(p) # <__main__.Point object at 0x000000B420CB2898> print(p()) #4:5
class Adder: def __call__(self, *args, **kwargs): ret = 0 for x in args: ret += x self.ret = ret return ret adder = Adder() print(adder(4,5,6)) #15 print(adder.ret) #15
10.1斐波那契数列。
class Fib: def __init__(self): self.items = [0,1,1] def __call__(self,n): l = len(self.items) if n <= 0: raise IndexError elif n < len(self.items): return self.items[n] for i in range(3,n+1): x = self.items[i-1] + self.items[i-2] self.items.append(x) return x fib = Fib() print(fib(10))
class Fib: def __init__(self): self.lst = [0,1,1] def __call__(self, index): return self[index] def __len__(self): return len(self.lst) def __iter__(self): return iter(self.lst) def __getitem__(self, index): if index <0: raise IndexError if index < len(self.lst): return self.lst[index] for i in range(len(self),index+1): self.lst.append(self.lst[i-1]+self.lst[i-2]) return self.lst[index] def __str__(self): return str(self.lst) fib = Fib() print(fib(10))
斐波那契数列
........................................................完美分割线...................................................................
enter的返回值作用不影响。
11、上下文管理
文件IO操作可以对文件对象使用上下文管理,使用with..as语法。
With open(‘test’)as f:
Pass
class Point: pass with Point() as p: pass
提示错误信息,因为没有__exit__这个属性。
12、上下文管理对象
当一个对象同时实现了__enter__() 和__exit__()方法,他就属于上下文管理的对象。
方法 |
意义 |
__enter__ |
进入与此对象相关的上下文,如果存在此方法,with语法会把该方法的返回值作为绑定到as子句中指定的变量上 |
__exit__ |
退出与此对象相关的上下文。 |
import time class Point: def __init__(self): print(‘init‘) def __enter__(self): print(‘enter‘) def __exit__(self, exc_type, exc_val, exc_tb): print(‘exit‘) with Point() as p: print(‘to do ‘) time.sleep(2)
实例化的时候,并不会调用enter,进入with语句块调用__enter__ 方法,然后执行语句体,最后离开with语句的时候会调用__exit__语句。
13、上下文管理的安全性
class Point: def __init__(self): print(‘init‘) def __enter__(self): print(‘enter‘) def __exit__(self, exc_type, exc_val, exc_tb): print(‘exit‘) with Point() as p: raise SyntaxError(‘error‘) print(‘to do ‘)
可以看出在进入和退出的时候照样执行函数,上下文管理是安全的。
极端的例子就是采用退出当前解释器的函数,sys.exit()窗口直接关闭了,Python运行环境推出了,但是enter 和exit函数照样执行。
14、with语句
class Point: def __init__(self): print(‘init‘) def __enter__(self): print(‘enter‘) def __exit__(self, exc_type, exc_val, exc_tb): print(‘exit‘) p = Point() with p as f: print(p == f)
p和f是不一样的,因为p是实例对象,f却是enter的返回值。
__enter__方法返回值就是上下文中使用的对象,with语法会把其返回值赋值给as子句的变量。
With 可以开启一个上下文运行的环境,在执行前做一些准备工作,执行后做一些收尾工作。
__enter__ 进入
__exit__ 退出 碰到with的时候才会调用。
首先创建实例先调用__init__
With A( )as f:
f的值是__enter__的返回值。
F = A()
With f:
f就是实例,
15、__enter__ 方法和 __exit__方法的参数
__enter__ 的参数就是实例本身。
__exit__ 的参数.一共是三个。如果退出时候没有异常,则这三个值 是None。
如果存在异常,参数意义如下:
exc_type ,异常类型。
exc_value,异常的值。
traceback 异常的追踪信息。
__exit__方法返回一个等效True的值,则会压制异常,否则,继续抛出异常。
class Point: def __init__(self): print(‘init‘) def __enter__(self): print(‘enter‘) def __exit__(self, exc_type, exc_val, exc_tb): print(‘exit‘) print(exc_type) #<class ‘SyntaxError‘> print(exc_val) #error print(exc_tb) #<traceback object at 0x000000E91743AE88> return ‘abc‘ p = Point() with p as f: raise SyntaxError(‘error‘) print(‘to do ‘)
15.1练习:
为加法函数计时。
第一种方法使用装饰器。
import datetime import time def timeit(fn): def wrapper(*args,**kwargs): start =datetime.datetime.now() ret = fn(*args,**kwargs) delta = (datetime.datetime.now()-start).total_seconds() print(‘{} took {}s‘.format(fn.__name__,delta)) return ret return wrapper @timeit def add(x,y): time.sleep(2) return x + y print(add(3,4))
利用上下文实现:
import datetime import time from functools import wraps def timeit(fn): """This is a fn""" @wraps(fn) def wrapper(*args,**kwargs): start =datetime.datetime.now() ret = fn(*args,**kwargs) delta = (datetime.datetime.now()-start).total_seconds() print(‘{} took {}s‘.format(fn.__name__,delta)) return ret return wrapper @timeit def add(x,y): """this is a add func""" time.sleep(2) return x + y print(add(3,4)) class Timeit: def __init__(self,fn): self.fn = fn def __enter__(self): self.start = datetime.datetime.now() return self.fn def __exit__(self, exc_type, exc_val, exc_tb): delta = (datetime.datetime.now() - self.start).total_seconds() print(‘{}took {}s‘.format(self.fn.__name__,delta)) with Timeit(add)as fn: print(add(4,7))
利用可调用对象来实现。
import datetime import time from functools import wraps def timeit(fn): """This is a fn""" @wraps(fn) def wrapper(*args,**kwargs): start =datetime.datetime.now() ret = fn(*args,**kwargs) delta = (datetime.datetime.now()-start).total_seconds() print(‘{} took {}s‘.format(fn.__name__,delta)) return ret return wrapper @timeit def add(x,y): """this is a add func""" time.sleep(2) return x + y print(add(3,4)) class Timeit: def __init__(self,fn): self.fn = fn def __enter__(self): self.start = datetime.datetime.now() return self def __exit__(self, exc_type, exc_val, exc_tb): delta = (datetime.datetime.now() - self.start).total_seconds() print(‘{}took {}s‘.format(self.fn.__name__,delta)) def __call__(self, x, y): return self.fn(x,y) with Timeit(add)as timeobject: print(timeobject(4,7))
把类当做装饰器来实现
class Timeit: def __init__(self,fn): self.fn = fn def __enter__(self): self.start = datetime.datetime.now() return self def __exit__(self, exc_type, exc_val, exc_tb): self.delta = (datetime.datetime.now() - self.start).total_seconds() print(‘{}took {}s‘.format(self.fn.__name__,self.delta)) def __call__(self, *args,**kwargs): self.start = datetime.datetime.now() ret = self.fn(*args,**kwargs) self.delta = (datetime.datetime.now()-self.start).total_seconds() print(‘{}took {}s call‘.format(self.fn.__name__,self.delta)) return ret @Timeit def add(x,y): """this is a add func""" time.sleep(2) return x + y add(3,4)
解决文档字符串的问题: 实例的__doc__ =函数的__doc__
class Timeit: def __init__(self,fn): self.fn = fn self.__doc__ = fn.__doc__ def __enter__(self): self.start = datetime.datetime.now() return self def __exit__(self, exc_type, exc_val, exc_tb): self.delta = (datetime.datetime.now() - self.start).total_seconds() print(‘{}took {}s‘.format(self.fn.__name__,self.delta)) def __call__(self, *args,**kwargs): self.start = datetime.datetime.now() ret = self.fn(*args,**kwargs) self.delta = (datetime.datetime.now()-self.start).total_seconds() print(‘{}took {}s call‘.format(self.fn.__name__,self.delta)) return ret @Timeit def add(x,y): """this is a add func""" time.sleep(0.5) return x + y add(3,4) print(Timeit(add).__doc__) #this is a add func
print(add.__doc__) #this is a add func
利用funtools工具。
import time import datetime from functools import wraps,update_wrapper class Timeit: """this a class""" def __init__(self,fn): self.fn = fn # self.__doc__ = fn.__doc__ 把函数对象的文档字符串直接赋给类 # update_wrapper(self,fn) wraps(fn)(self) def __enter__(self): self.start = datetime.datetime.now() return self def __exit__(self, exc_type, exc_val, exc_tb): self.delta = (datetime.datetime.now() - self.start).total_seconds() print(‘{}took {}s‘.format(self.fn.__name__,self.delta)) def __call__(self, *args,**kwargs): self.start = datetime.datetime.now() ret = self.fn(*args,**kwargs) self.delta = (datetime.datetime.now()-self.start).total_seconds() print(‘{}took {}s call‘.format(self.fn.__name__,self.delta)) return ret @Timeit def add(x,y): """this is a add func""" time.sleep(0.5) return x + y add(3,4) print(Timeit(add).__doc__) print(add.__doc__)
类即可以用在上下文管理,又可以用作装饰器。
16、上下文管理用用场景
1、增强功能。
在代码执行的前后增加代码,以增强其功能,类似装饰器的功能,
2、资源管理。
打开资源需要关闭,例如文件对象,网络连接,数据库连接等。
3、权限验证。 在执行代码之前,做权限的验证, __enter__时候管理。
上下文不管异常有多强,清理等依然进行处理。
..............................................................分割线............................................................
17、Contextlib.contextmanager
他是一个装饰器实现上下文管理,装饰一个函数,而不用像类,一样实现__enter__和 __exit__方法。
对下面的函数有要求,必须有yield,也就是这个函数必须返回一个生成器,且只有yield一个值,也就是这个装饰器接受一个生产期函数作为参数。
@contextlib.contextmanager def foo(): print(‘enter‘) #相当于__enter__() yield 100 #yield值只能有一个,作为__enter__的返回值, print(‘exit‘) #相当于__exit__() with foo() as f: print(f)
as 后面的变量接的是yield语句返回的值。
@contextlib.contextmanager def foo(): print(‘enter‘) #相当于__enter__() try: yield 100 #yield值只能有一个,作为__enter__的返回值, finally: print(‘exit‘) #相当于__exit__() with foo() as f: raise IndexError print(f)
遇到异常依然会执行相应的语句。
import contextlib import datetime import time @contextlib.contextmanager def add(x,y): start = datetime.datetime.now() try: yield x+y finally: delta = (datetime.datetime.now()-start).total_seconds() print(delta) with add(3,4) as f: time.sleep(1) print(f)
总结,如果业务逻辑简单可以使用函数加contextlib.contextmanager装饰器方式,如果业务复杂,用类的方式加__enter__ 和__exit__ 方法方便。
..............................................................分割线............................................................
18、反射
概述:
运行时候(runtime),区别于编译时,指的是程序被加载到内存中执行的时候。
反射,reflection指的是运行时获取类型定义信息。
一个对象能够在运行时候,像照镜子一样,反射出其类型信息。
在Python中,能够通过一个对象,找出其type,class,attribute或method的能力,成为反射或者自省。
具有反射能力的函数有:type(),isintance(),callable(),dir(),getattr()。
19、反射相关的函数和方法
class Point: def __init__(self,x,y): self.x = x self.y = y def __str__(self): return "Point({},{})".format(self.x,self.y) def show(self): print(self.x,self.y) p = Point(4,5) print(p) # Point(4,5) print(p.__dict__) # {‘x‘: 4, ‘y‘: 5} p.__dict__[‘y‘]=16 print(p.__dict__) #{‘x‘: 4, ‘y‘: 16} p.z = 10 print(p.__dict__) #{‘z‘: 10, ‘x‘: 4, ‘y‘: 16} print(dir(p)) # print(p.__dir__()) #
Point(4,5)
{‘x‘: 4, ‘y‘: 5}
{‘x‘: 4, ‘y‘: 16}
{‘z‘: 10, ‘x‘: 4, ‘y‘: 16}
[‘__class__‘, ‘__delattr__‘, ‘__dict__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__gt__‘, ‘__hash__‘, ‘__init__‘, ‘__le__‘, ‘__lt__‘, ‘__module__‘, ‘__ne__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘__weakref__‘, ‘show‘, ‘x‘, ‘y‘, ‘z‘]
[‘__repr__‘, ‘__le__‘, ‘__str__‘, ‘__ge__‘, ‘__eq__‘, ‘__init__‘, ‘z‘, ‘__hash__‘, ‘__lt__‘, ‘__subclasshook__‘, ‘__reduce_ex__‘, ‘y‘, ‘__new__‘, ‘__ne__‘, ‘__dict__‘, ‘__sizeof__‘, ‘__setattr__‘, ‘show‘, ‘__reduce__‘, ‘__weakref__‘, ‘__delattr__‘, ‘__format__‘, ‘__getattribute__‘, ‘__doc__‘, ‘__class__‘, ‘__gt__‘, ‘__dir__‘, ‘__module__‘, ‘x‘]
通过属性字典__dict__来访问对象的属性,本质上也是利用反射能力。
Python提供了内建函数,来访问属性。
内建函数 |
意义 |
getattr(object,name[,default]) |
通过name返回object的属性值,当属性不存在,将使用default,如果没有default,则抛出属性异常,name必须为字符串 |
setattr(object,name,value) |
Object的属性存在,则覆盖,不存在,新增。 |
hasattr(object,name) |
判断对象是否有这个名字的属性,name必须有字符串 |
class Point: def __init__(self,x,y): self.x = x self.y = y def __str__(self): return "Point({},{})".format(self.x,self.y) def show(self): print(self) p1 = Point(4,5) p2 = Point(9,10) print(repr(p1),repr(p2),sep = ‘\n‘) #<__main__.Point object at 0x000000908D129550> print(p1.__dict__) #<__main__.Point object at 0x000000908D129588> setattr(p1,‘y‘,2) setattr(p1,‘z‘,3) #{‘y‘: 5, ‘x‘: 4} print(getattr(p1,‘__dict__‘)) #{‘z‘: 3, ‘y‘: 2, ‘x‘: 4} #动态调用 if hasattr(p1,‘show‘): getattr(p1,‘show‘)() #Point(4,2) ##动态增加方法 if not hasattr(Point,‘add‘): setattr(Point,‘add‘,lambda self,other:Point(self.x+other.x,self.y+other.y)) print(Point.add) #<function <lambda> at 0x000000D7350E4C80> print(p1.add) #<bound method <lambda> of <__main__.Point object at 0x0000007FA5749550>> print(p1.add(p2)) #Point(13,12)
if not hasattr(p1,‘sub‘): setattr(p1,‘sub‘,lambda self,other:Point(self.x-other.x,self.y-other.y)) print(1,p1.sub(p1,p1)) #Point(0,0) print(2,p1.sub) # 2 <function <lambda> at 0x000000304868B1E0> print(p1.__dict__) #{‘z‘: 3, ‘y‘: 2, ‘sub‘: <function <lambda> at 0x0000000CDAD0B1E0>, ‘x‘: 4} print(Point.__dict__)
{‘show‘: <function Point.show at 0x0000000CDAD0B158>, ‘__init__‘: <function Point.__init__ at 0x0000000CDAD04BF8>, ‘__module__‘: ‘__main__‘, ‘add‘: <function <lambda> at 0x0000000CDAD04D08>, ‘__doc__‘: None, ‘__str__‘: <function Point.__str__ at 0x0000000CDAD0B0D0>, ‘__dict__‘: <attribute ‘__dict__‘ of ‘Point‘ objects>, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘Point‘ objects>}
动态增删属性的方式就是运行时改变类或者实例的方式,但是装饰器或Mixin都是定义时候就决定了。
因此反射能力具有更大的灵活性。
19.1利用属性方法创建命令分发器。
class Dispatcher: def __init__(self): self.run() def cmd1(self): print(‘cmd1‘) def cmd2(self): print(‘cmd2‘) def run(self): while True: cmd = input(‘>>>‘).strip() if cmd == ‘quit‘: break getattr(self,cmd,lambda :print(‘unknown command‘.format(cmd)))() Dispatcher()
..............................................................分割线..................................................................................
20、反射相关的魔术方法
方法 |
意义 |
__getattr__ |
针对已经有的属性无效,针对没有的属性。只是和实例有关。 当搜索实例,实例的类即祖先类查不到属性,就会调用此方法 |
__setattr__ |
通过实例属性,进行增加,修改(覆盖)都要调用它 |
__delattr__ |
当通过实例来删除属性时调用此方法 |
__getattrbute__ |
实例所有的属性调用都从这个方法开始。 |
属性查找顺序:
实例调用__getattribute__() 到对象的字典,对象的类的字典,继承祖先类的字典,调用__getattr__()
***第一个路,按照其返回值。
***第二条路,所有字典都去查找,没有的话,会调用getattr方法。
1)__getattr__()
class Base: n = 0 class Point(Base): z = 6 def __init__(self,x,y): self.x = x self.y = y def show(self): print(self.x,self.y) def __getattr__(self, item): return ‘missing {}‘.format(item) p1 = Point(4,5) print(p1.x) #4 print(p1.y) #5 print(p1.z) #6 print(p1.n) #0 print(p1.t) #missing t print(p1.d) #missing d
一个类的属性会按照继承关系找,如果找不到,就会执行__getattr__()方法,如果没有这个方法,就会抛出属性异常。
查找属性的顺序为:
对象的字典,对象自己的类的字典,祖先的字典,直到object的字典里面。找不到的话调用
2)__setattr__()
class Base: n = 0 class Point(Base): z = 6 def __init__(self, x, y): self.x = x self.y = y def show(self): print(self.x, self.y) def __getattr__(self, item): return ‘missing {}‘.format(item) def __setattr__(self, key, value): print(‘setattr {}={}‘.format(key,value)) p1 = Point(4, 5) print(p1.x) # 4 print(p1.y) # 5 print(p1.z) # 6 print(p1.n) # 0 print(p1.t) # missing t print(p1.d) # missing d p1.x = 50 print(1,p1.__dict__) #1 {} p1.__dict__[‘x‘] = 60 print(p1.__dict__) #{‘x‘: 60} print(p1.x) #60
实例通过点设置属性,如同self.x = x,就会调用__setattr__(),属性要加到实例的__dict__中,就需要自己去完成。
3)__delattr__()
class Point: z = 5 def __init__(self,x,y): self.x = x self.y = y def __delattr__(self, item): print(‘can not del{}‘.format(item)) p = Point(2,3) del p.x p.z = 15 del p.z del p.z print(Point.__dict__) print(p.__dict__) #{‘z‘: 15, ‘x‘: 2, ‘y‘: 3} del Point.z print(Point.__dict__)
can not delx
can not delz
can not delz
{‘z‘: 5, ‘__init__‘: <function Point.__init__ at 0x000000A16F054D08>, ‘__module__‘: ‘__main__‘, ‘__dict__‘: <attribute ‘__dict__‘ of ‘Point‘ objects>, ‘__delattr__‘: <function Point.__delattr__ at 0x000000A16F054BF8>, ‘__doc__‘: None, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘Point‘ objects>}
{‘z‘: 15, ‘x‘: 2, ‘y‘: 3}
{‘__init__‘: <function Point.__init__ at 0x000000A16F054D08>, ‘__module__‘: ‘__main__‘, ‘__dict__‘: <attribute ‘__dict__‘ of ‘Point‘ objects>, ‘__delattr__‘: <function Point.__delattr__ at 0x000000A16F054BF8>, ‘__doc__‘: None, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘Point‘ objects>}
可以组织实例删除属性的操作,但是通过类依然可以删除属性。
4)__getattribute__
class Base: n = 0 class Point(Base): z = 6 def __init__(self,x,y): self.x = x self.y = y def __getattr__(self, item): return ‘missing{}‘.format(item) def __getattribute__(self, item): return item p1 = Point(2,3) print(p1.__dict__) #__dict__ print(p1.x) #x print(p1.z) #z print(p1.y) #y print(p1.n) #n print(p1.t) #t print(Point.__dict__) # print(Point.z) #6
实例的所有的属性访问,第一个都会被调用__getattrbute__方法,阻止了属性的查找,该方法应该返回计算后的值或者抛出属性异常。
他的return值作为属性查找的结果,如果抛出属性异常,就会调用__getattr__方法,因为表示属性没有找到。(就是通过实例.的访问返回值。)
总结:
__getattrbute__ 方法为了避免在该方法中无限的递归,他的实现应该永远调用基类的同名方法用来访问需要的任何属性,例如:object.__getattrbute__(self,name)
一般不建议使用。
属性查找顺序:实例调用__getattrbute__ () ---instance的字典--instance 的类的字典--继承到祖先类的字典(直到object)然后调用__getattr__().
原文地址:https://www.cnblogs.com/wangchunli-blogs/p/9949853.html