Python中类的特殊属性和魔术方法
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__().