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
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
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>
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)   #
print(exc_val)   #error
print(exc_tb)    #
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>
print(p1.__dict__)   #<__main__.point object at>
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)   # at 0x000000D7350E4C80>
print(p1.add)   # of <__main__.point object at>>
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 at 0x000000304868B1E0>
print(p1.__dict__)   #{'z': 3, 'y': 2, 'sub': at 0x0000000CDAD0B1E0>, 'x': 4}
print(Point.__dict__)
{'show': , '__init__': , '__module__': '__main__', 'add': at 0x0000000CDAD04D08>, '__doc__': None, '__str__': , '__dict__': , '__weakref__': }
动态增删属性的方式就是运行时改变类或者实例的方式,但是装饰器或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__': , '__module__': '__main__', '__dict__': , '__delattr__': , '__doc__': None, '__weakref__': }
{'z': 15, 'x': 2, 'y': 3}
{'__init__': , '__module__': '__main__', '__dict__': , '__delattr__': , '__doc__': None, '__weakref__': }
可以组织实例删除属性的操作,但是通过类依然可以删除属性。
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__().