阅读目录:
1、特殊属性:Python的精华之一
2、查看属性
3、魔术方法
- 创建,初始化,销毁 (__new__, __init__, __del__)
- hash
- bool
- 可视化
- 运算符重载
- 可调用对象
- 上下文管理
- 反射
- 描述器
- 其他杂项
1、特殊属性:Python的精华之一:
属性 | 含义 |
__name__ | 类,函数,方法等的名字,实例没有 |
__module__ | 类定义所在的模块名 |
__class__ | 对象或类所属的 类 |
__bases__ | 类的基类的元组,顺序为它们在基类列表中出现的顺序 |
__doc__ | 类,函数的文档字符串,如果没有定义则为None |
__mro__ | 类的mro,class.mro()返回的结果保存在__mro__中 |
__dict__ | 类或实例的属性,可写的字典 |
测试:
1 class A:pass 2 3 class B(A): 4 def bb(self):pass 5 6 class C(B, A): 7 def cc(self):pass 8 9 a = A() 10 b = B() 11 c = C() 12 13 print(A.__name__) 14 print(B.__name__) 15 print(C.__name__) 16 print(b.bb.__name__) 17 print('-'*40) 18 # 因为是在当前类中调用,所以是返回值 __main__ 19 # 如果该模块 a.py 被导入到其他地方,那么打印 就是模块名 a 20 print(A.__module__) 21 print(B.__module__) 22 print(C.__module__) 23 print('-'*40) 24 print(A.__bases__) 25 print(A.__base__) 26 print(B.__bases__) 27 print(B.__base__) 28 print(C.__bases__) 29 print(C.__base__) 30 print('-'*40) 31 print(A.__doc__) 32 print(a.__doc__) 33 print(b.bb.__doc__) 34 print('-'*40) 35 print(A.mro()) 36 print(A.__mro__) 37 print(B.mro()) 38 print(B.__mro__) 39 print(C.mro()) 40 print(C.__mro__) 41 print('-'*40) 42 print(A.__dict__) 43 print(a.__dict__) 44 print(B.__dict__) 45 print(b.__dict__) 46 print(C.__dict__) 47 print(c.__dict__)
结果:
1 A 2 B 3 C 4 bb 5 ---------------------------------------- 6 __main__ 7 __main__ 8 __main__ 9 ---------------------------------------- 10 (<class 'object'>,) 11 <class 'object'> 12 (<class '__main__.A'>,) 13 <class '__main__.A'> 14 (<class '__main__.B'>, <class '__main__.A'>) 15 <class '__main__.B'> 16 ---------------------------------------- 17 None 18 None 19 None 20 ---------------------------------------- 21 [<class '__main__.A'>, <class 'object'>] 22 (<class '__main__.A'>, <class 'object'>) 23 [<class '__main__.B'>, <class '__main__.A'>, <class 'object'>] 24 (<class '__main__.B'>, <class '__main__.A'>, <class 'object'>) 25 [<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>] 26 (<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>) 27 ---------------------------------------- 28 {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None} 29 {} 30 {'__module__': '__main__', 'bb': <function B.bb at 0x0000000002240268>, '__doc__': None} 31 {} 32 {'__module__': '__main__', 'cc': <function C.cc at 0x00000000022402F0>, '__doc__': None} 33 {}
2、查看属性
方法————————————————————意义
__dicr__:(double under ---> dunder,双下划线)
类或实例的属性不一定都在dict中
print(dir(ClassName)) ---->属性key,放置在列表中,在dict中没有的,都是从objecct中来的,或者继承下来的
print(ClassName.__dict__):
def __dir__(self):# 返回可迭代对象,但是是实例方法,与类没关系,所以,调用类的dir,并没有覆盖,只有实例调用dir才有变化
return iterableObj
print(dir()): 当前作用域的标识符,如果在函数中,就是形参的名称,不管是函数还是方法,一般用在模块上
测试:
1 class A:pass 2 3 class B(A): 4 def bb(self):pass 5 6 class C(B, A): 7 def __init__(self): 8 self.x = 10 9 # def __dir__(self): 10 # pass # return None 11 pass 12 13 # 类只收集类的属性 14 print(C.__dict__) 15 print(dir(C)) # 尽可能收集属性key,dict中的也收集过来,还会收集跟当前对象继承的类的属性也会收集过来 16 ############################################### 17 print('-' * 40) 18 # 注意self,只影响实例,不影响 类! 19 # 收集实例的属性,包括类,继承的类的属性 20 # 没有提供 __dir__(self) 21 c = C() 22 print(c.__dict__) 23 print(dir(c)) # 等价 c.__dir__() 24 # 提供 __dir__(self) 25 # def __dir__(self): 26 # pass # return None --------> 报错,要一个可迭代对象 27 # return ('a','b','c') -----> ['a','b','c'] 28 ############################################### 29 print('-' * 40) 30 # 收集当前作用域的标识符,如果在函数中,就是形参的名称,不管是函数还是方法,一般用在模块上 31 print(dir()) #而且,只会打印之前的,之后的不会收集到 32 def foo(): 33 print(dir()) 34 foo() 35 36 def foo(a): 37 print(dir()) 38 foo(1) 39 # 如果a.py被导入,在被导入的地方,a.py中的内容会重载一次,该运行的云行一次,该打印的也会打印 40 # 在被导入的地方,执行dir(a) 把模块a中的属性(标识符)都打印 41 # 如果被导入的地方执行dir(), 模块名a 也会被收集 42 # 如果 from a import C 执行dir(),C也会被收集到,这样就可以用这个类C,否则无法使用 43 ############################################### 44 print('-' * 40) 45 c = C() 46 print(c.__dir__()) 47 # 因为字典的key是类set,所以可以 并集 48 print(object.__dict__.keys() | c.__class__.__dict__.keys() | c.__dict__.keys()) 49 # 可以看到,两者结果差不多,所以实例收集到的就是这些东西的并集
结果:
1 {'__module__': '__main__', '__init__': <function C.__init__ at 0x00000000029502F0>, '__doc__': None} 2 ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bb'] 3 ---------------------------------------- 4 {'x': 10} 5 ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bb', 'x'] 6 ---------------------------------------- 7 ['A', 'B', 'C', '__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'c'] 8 [] 9 ['a'] 10 ---------------------------------------- 11 ['x', '__module__', '__init__', '__doc__', 'bb', '__dict__', '__weakref__', '__repr__', '__hash__', '__str__', '__getattribute__', '__setattr__', '__delattr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__new__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__'] 12 {'__repr__', '__class__', '__ge__', '__subclasshook__', '__sizeof__', '__hash__', '__le__', '__setattr__', '__reduce_ex__', '__init_subclass__', '__init__', '__module__', '__delattr__', '__new__', '__eq__', '__reduce__', 'x', '__ne__', '__doc__', '__dir__', '__lt__', '__str__', '__gt__', '__format__', '__getattribute__'}
总结:
__dir__:
返回类或者对象的所有成员名称列表,dir()函数就是调用__dir__()
使用实例调用时,如果提供__dir__(),则返回其返回值,要求是可迭代对象
如果没有提供__dir__(),则会从实例和类及祖先类中收集信息
如果dir([obj])参数obj包含方法__dir__() ,该方法将被调用,如果参数obj不含__dir__ ,该方法将最大限度的收集属性信息
dir(obj) 对于不同类型的对象obj具有不同的行为。
-
-
- 如果对象是模块对象,返回的列表包含模块的属性名和变量名
- 如果对象是类型或者类对象,返回的列表包含类的属性名,及它的基类属性名
- 如果obj 不写,返回列表包含内容不同
- 在模块中,返回模块的属性和变量名
- 在函数中,返回本地作用域的变量名
- 在方法中,返回本地作用域的变量名
-
3、魔术方法: 处理new是与cls有关,其他的都是属于实例的方法
分类:
- 创建,初始化,销毁 (__new__, __init__, __del__)
- hash
- bool
- 可视化
- 运算符重载
- 可调用对象
- 上下文管理
- 反射
- 描述器
- 其他杂项
①、创建,初始化,销毁:
__new__:目前写法固定,一般不用自定义,是staticmethod,元类编程时用
def __new__(cls, *args, **kwargs): return super().__new__(cls)
②、 hash:
1 class A:pass 2 3 class B(A):pass 4 5 class C(B): 6 def __hash__(self): 7 # return 'abc' # 这种是抛异常的,因为hash 返回的是一个数字 8 return hash('abc') 9 def __init__(self, name): 10 self.name = name 11 print("__________________") 12 13 14 c = C('tom') 15 print(hash(c))
总结:
hash(z) z 都一样,求得hash应该不变的,幂等性,一般来说,z 不一样,hash应该不一样
不同的hash算法,都有hash冲突(因为只有一个hash空间),不同的z求得同样的hash值。
hash空间满了,就会出现hash冲突。
出现冲突后,要考虑去重了,如果出现冲突,判断是否eq,如果eq是相等,返回True就会去重。
先说hash,再说eq
如果不提供hash 和 eq 就用object的hash,但是使用的是内存地址求hash的
如果提供eq,不提供hash,默认是hash返回值是None,也就是 不可hash,这个时候提供一个hash方法就可以
提供一个__hash__ = None ,就不可hash,list就是这样实现不可hash的
如果只提供hash,冲突就用到eq处理,是否相等,再去重
先比较is, 在比较== ,is相等,内存地址都一样,就没必要再看==了
因此,一般来说,提供__hash__方法是为了作为set或者dict的key,所以去重要同时提供EQ方法
不可hash对象 isinstance(p1, collections,Hashable) 一定是False
去重 需要提供__eq__方法
③、bool:
__bool__内建函数bool(),或者对象放在逻辑表达式的位置,调用这个函数返回布尔值
如果没有定义__bool__(self),就找len()返回长度,非 0 为真,
如果__len__(self)也没有定义,那么所有实例都返回真
如果有,__bool__(self) 类 真, 实例,看里边返回什么(True 还是False,只能返回布尔值)
__len__(self)return 整数>=0或者布尔值
有了bool 就不看len!,全没有,都是真。
如果len都是等价True,那就看bool返回什么
④、可视化:
__repr__(self)必须是字符串
内建函数repr()对一个对象获取字符串表达
调用__repr__方法返回字符串表达式,如果__repr__也没有定义,就直接返回object的定义就显示内存地址信息
如果只提供repr,都转为repr
__str__(self)
str() 函数,format() 函数,print()函数调用,需要返回对象的字符串表达,如果没有定义,就去调用__repr__方法,
返回字符串表达,如果__repr__没有定义,返回内存地址信息
直接作用到实例上,用str ,如果没有str,都是repr,如果没有repr,该找str的找str,找repr的如果找不到就去找object,打印内存地址
如果都没有,找到的都是内存地址,去父类,或object找的。
__bytes__(self) return bytes类型
bytes() 函数调用,返回一个对象的bytes表达,即返回bytes对象
测试:
1 class A: 2 def __init__(self, name): 3 self.name = name 4 5 def __repr__(self): 6 return 'repr name={}'.format(self.name) 7 8 def __str__(self): 9 return 'str name={}'.format(self.name) 10 11 def __bytes__(self): 12 return self.name.encode() 13 14 print(A) 15 print(A('tom')) 16 print(str(A('tom'))) 17 print("{}".format(A('jerry'))) 18 19 print(1,repr(A('tom'))) 20 print(2,repr('{}'.format(A('jerry')))) # 因为先format的,所以先调用str 21 22 a = A('tom') 23 print([a,a]) # print,没有直接作用到实例上 24 print(str({a, a})) # print,没有直接作用到实例上 25 print('{}'.format(*{a, a})) # *{a, a} 参数解析 先去重,*{a} 还是a,format直接作用上去 26 27 print(bytes(a))
结果:
1 <class '__main__.A'> 2 str name=tom 3 str name=tom 4 str name=jerry 5 1 repr name=tom 6 2 'str name=jerry' 7 [repr name=tom, repr name=tom] 8 {repr name=tom} 9 str name=tom 10 b'tom'
注:不能通过print输出,来判断类型,类型判断要用 type 或isinstance
⑤、运算符重载:
__iadd__ 这里的i 就地修改,其他 i也是该意思
不同类型之间是可以==比较的。
没有定义的话,是不可以比较大小的,报错
虽然可以返回其他类型,但是一般返回本类型
测试1:减法实现
1 class A: 2 def __init__(self, name, age=18): 3 self.name = name 4 self.age = age 5 6 def __sub__(self, other): 7 return self.age - other.age 8 9 tom = A('tom') 10 jerry = A('jerry', 20) 11 12 # 实现了实例减法 13 print(tom - jerry) 14 15 # 这相当于 tom被重新赋值了,因为没有提供isub 16 tom -= jerry 17 print(tom) # -2 18 19 ###################################################3 20 21 class A: 22 def __init__(self, name, age=18): 23 self.name = name 24 self.age = age 25 26 def __sub__(self, other): 27 return self.age - other.age # 这里可以使用-号 是因为作用到age上,而不是self本身上,所以不会出现自己调自己 28 29 def __isub__(self, other): 30 return A(self.name,self- other) # 这里调用了sub 31 32 tom = A('tom') 33 jerry = A('jerry', 20) 34 35 # 实现了实例减法 36 print(tom - jerry) 37 38 # 这调用了isub 返回一个新的A 实例赋值给tom 39 # 当然直接使用sub 也可以返回一个A 实例,活学活用 40 tom -= jerry 41 print(tom) # -2
结果:
1 -2 2 -2 3 -2 4 <__main__.A object at 0x0000000002928668>
测试2:比较大小,使用装饰器,只需要提供两个 比较符号
1 from functools import total_ordering 2 3 @total_ordering 4 class Point: 5 def __init__(self, x, y): 6 self.x = x 7 self.y = y 8 9 def __hash__(self): 10 return hash((self.x , self.y)) 11 12 13 14 def __add__(self, other): 15 x = self.x + other.x 16 y = self.y + other.y 17 return Point(x, y) 18 19 def __sub__(self, other): 20 x = self.x - other.x 21 y = self.y - other.y 22 return Point(x, y) 23 24 def distance(self): 25 pass 26 27 def __eq__(self, other): 28 return True 29 # if self.x == other.x and self.y == other.y: 30 # return True 31 # else: 32 # return False 33 def __gt__(self, other):#这里只是为了测试,数学上比较大小是不对的 34 return self.x > other.x 35 36 p1 = Point(1,2) 37 p2 = Point(3,4) 38 p3 = Point(1,2) 39 40 # z调用eq,只能解决等 或 不等,不能解决 谁大谁小 41 print(p1 == p2) 42 print(p1 != p2) 43 print(p1 >= p2) 44 print(p1 <= p2) 45 print(p1 > p2) 46 print(p1 < p2)
结果:
1 True 2 False 3 True 4 True 5 False 6 False
测试3:不是用装饰器,只需要三个比较符
1 class Point: 2 def __init__(self, x, y): 3 self.x = x 4 self.y = y 5 6 def __hash__(self): 7 return hash((self.x , self.y)) 8 9 10 11 def __add__(self, other): 12 x = self.x + other.x 13 y = self.y + other.y 14 return Point(x, y) 15 16 def __sub__(self, other): 17 x = self.x - other.x 18 y = self.y - other.y 19 return Point(x, y) 20 21 def distance(self): 22 pass 23 24 def __eq__(self, other): 25 return True 26 # if self.x == other.x and self.y == other.y: 27 # return True 28 # else: 29 # return False 30 def __gt__(self, other):#这里只是为了测试,数学上比较大小是不对的 31 return self.x > other.x 32 33 def __ge__(self, other): 34 return self.x >= other.x 35 36 p1 = Point(1,2) 37 p2 = Point(3,4) 38 p3 = Point(1,2) 39 40 # z调用eq,只能解决等 或 不等,不能解决 谁大谁小 41 print(p1 == p2) 42 print(p1 != p2) 43 print(p1 >= p2) 44 print(p1 <= p2) 45 print(p1 > p2) 46 print(p1 < p2)
结果
1 True 2 False 3 False 4 True 5 False 6 True
总结:
往往是用面向对象实现类的,需要做大量的运算,而运算符是这种运算在数学上最常见的表达方式
通过重载,重新定义 Point + Point
提供运算符重载,比直接提供加法方法更加合适该领域内使用者的习惯
int类 几乎实现了所有操作符,可以参看
只需要三个,是因为,大于可以推断小于等于,以此类推。。。。。。
⑥容器相关方法:
方法 | 意义 |
__len__ | 内建函数len(),返回对象的长度 >= 0,如果把对象当做容器类型看,就如同list或者dict,bool()函数调用的时候,如果没有__bool__()方法,则会看__len__()方法是否存在,存在返回非0 为真 |
__iter__ | 迭代容器,调用,返回一个新的迭代器对象 |
__contains__ | in成员运算符,没有实现,就调用__iter__方法遍历,所以,可以不实现 |
__getitem__ | 实现self[key] 访问,序列对象,key接受整数位索引,或者切片,对于set 和dict,key为hashbale,key不存在引发 KeyError异常 |
__setitem__ | 和__getitem__的访问类似,是设置的方法 |
__missing__ | 字典或其子类使用__getitem__()调用时,key不存在执行该方法 |
测试:
1 class A(dict): 2 def __missing__(self, key): 3 print('missing key', key) 4 return 0 5 6 a = A() 7 print(a['k']) # 如果没有missing方法,报KeyError 8 print(a.get('k'))
结果:
1 missing key k 2 0 3 None
空list,set,dict,str等效False:
因为为空,不实现bool(),往往实现len()返回为0 等效False
举例:购物车作为容器:
1 class Cart: 2 def __init__(self): 3 self.item = [] 4 5 def additem(self, item): 6 self.item.append(item) 7 8 def getallitem(self): 9 return self.item 10 11 def __getitem__(self, index): 12 return self.item[index] 13 14 def __iter__(self): 15 yield from self.item 16 #return iter(self.item) 17 18 def __setitem__(self, key, value): 19 self.item[key] = value 20 21 def __str__(self): 22 return '{}'.format(self.item) 23 24 def __len__(self): 25 return len(self.item) 26 27 def __bool__(self): 28 return True 29 30 def __add__(self, other): # car + 5 31 self.item.append(other) 32 return self 33 34 35 36 c = Cart() 37 c.additem(1) 38 c.additem(2) 39 c.additem(3) 40 c.additem(4) 41 42 43 print('----',len(c)) 44 print('++++',bool(Cart)) 45 print(bool(c)) 46 47 for x in c: 48 print(x) 49 50 print(3 in c) # __iter__ 不需要contaions 51 52 53 print(c[1]) # getitem 54 55 c[2] = 'sdasda' # setitem 56 print(c) 57 58 # 链式加法 59 print(c + 6) 60 print(c + 7 + 8 + 9) 61 print(c.__add__(20).__add__(22))
结果:
1 ++++ True 2 True 3 1 4 2 5 3 6 4 7 True 8 2 9 [1, 2, 'sdasda', 4] 10 [1, 2, 'sdasda', 4, 6] 11 [1, 2, 'sdasda', 4, 6, 7, 8, 9] 12 [1, 2, 'sdasda', 4, 6, 7, 8, 9, 20, 22]
⑦ 、可调用对象:
Python中一切皆对象,函数也不例外。
1 def foo(): 2 print(foo.__module__, foo.__name__) 3 4 foo() 5 6 #等价于 7 8 foo.__call__()
函数即对象,对象foo加上(),就是调用此函数对象的__call__()方法
可调用对象:
方法 | 意义 |
__call__ | 类中定义一个该方法,实例就可以向函数一样调用 |
可调用对象:定义一个类,并实例化得到其实例,将实例像函数一样调用。
举例:
举例:定义一个斐波那契数列,方便调用,计算第n项,并实现了长度,迭代,支持索引
1 class Fib: 2 def __init__(self): 3 self.items = [0, 1, 1] # 而且使用了缓存 4 5 def __call__(self, index): 6 if index >= len(self.items):#但是注意长度,否则f(10)之后再f(4) 还会执行append 7 for i in range(3, index + 1): 8 self.items.append(self.items[i-1] + self.items[i-2]) 9 return self.items[index] 10 11 def __iter__(self): 12 yield from self.items 13 14 def __len__(self): 15 return len(self.items) 16 17 def __getitem__(self, item): 18 return self.items[item] 19 20 def __str__(self): 21 return str(self.items) 22 23 24 25 f = Fib() 26 print(f(10)) 27 print(f.items) 28 print(f(4))注意这两项,一个是可调用, 一个是支持索引 29 print(f[4]) 30 print(f.items) 31 print(len(f))
⑧、上下文管理:
class A:
pass
with A() as f:
print(f)
3.6 提示少 __enter__, 之前的提示 __exit__,正常情况下,两个一起用
pt = Point()
with pt as f:
print(pt == f)
如果使用as, 则f 其值为enter的返回值
参数问题:
enter方法不可添加参数,因为传不进去,传进去给谁啊???,所以。。。。
exit的参数:excption
exc_type
exc_val
exc_tb
做一些清理工作
retrun一个等效True,就可以压制异常,
看图:
有没有as,都会调 enter exit,但是里边没法引用,只有with知道,所以给了一个as
但是这个as,要看enter的返回值怎么写,如果是self,就是实力本身。
enter做资源申请,exit 做一些清理工作 (区别__del__做资源清零工作)
实例化,和 实力消亡是一次性的,分别创建和消亡是调用,而with语法可以对一个实例操作很多次。
使用exit ,保证资源的释放,下次很久之后,再次使用这个对象,在这段长时间中,也不会占用这个资源,比如,文件
但是del,直接消亡了,必须重新实例化。
上下文管理对象:
当一个对象同时实现了 __enter__ 和 __exit__ 方法,它就属于上下文管理的对象
enter 和 exit 都是在执行with语句的时候才会执行。
with可以开启一个上下文运行环境,在执行前做一些准备工作,执行后做一些首尾工作、
注:with 并不开启一个新的作用域。
即便调用 sys.exit(100) 0-256,它会退出当前解释器,直接敲,窗口会关闭,但是
遇到with,依然还是会执行 exit语句
举例: 类做装饰器,类装饰器和函数装饰器同时使用顺序问题!!!!
1 #################################################################### 2 3 import time 4 import datetime 5 from functools import wraps, update_wrapper 6 7 def timeit(fn): 8 @wraps(fn)# update_wrapper(wapper)(fn) 9 def wrapper(*args, **kwargs): 10 start = datetime.datetime.now() 11 ret = fn(*args, **kwargs) 12 delta = (datetime.datetime.now() - start).total_seconds() 13 print(delta) 14 return ret 15 return wrapper 16 17 @timeit 18 def add(x, y): 19 time.sleep(1) 20 return x + y 21 22 ##################################################################### 23 class TimeIt: 24 def __init__(self): 25 pass 26 27 def __enter__(self): 28 self.start = datetime.datetime.now() 29 return self 30 31 def __exit__(self, exc_type, exc_val, exc_tb): 32 delta = (datetime.datetime.now() - self.start).total_seconds() 33 print(delta) 34 35 with TimeIt() as f: 36 print('*', add(4, 5)) 37 # 此 add 非彼 add,这里只看print('*', add(4, 5))执行了多少秒, 38 # 也就是with开始后,进入enter到退出exit执行了多少秒 39 ######################################实现对象的可调用################################ 40 class TimeIt: 41 def __init__(self, fn): 42 self.fn = fn 43 44 def __enter__(self): 45 self.start = datetime.datetime.now() 46 return self 47 48 def __exit__(self, exc_type, exc_val, exc_tb): 49 delta = (datetime.datetime.now() - self.start).total_seconds() 50 print(delta) 51 52 def __call__(self, *args, **kwargs): 53 return self.fn(*args, **kwargs) 54 55 f = TimeIt(add) 56 print('*', f(4, 5)) 57 58 ######################################类作为装饰器################################ 59 60 class TimeIt: 61 def __init__(self, fn): 62 self.fn = fn 63 64 def __enter__(self): 65 self.start = datetime.datetime.now() 66 return self 67 68 def __exit__(self, exc_type, exc_val, exc_tb): 69 delta = (datetime.datetime.now() - self.start).total_seconds() 70 print(delta) 71 72 def __call__(self, *args, **kwargs): 73 start = datetime.datetime.now() 74 ret = self.fn(*args, **kwargs) 75 delta = (datetime.datetime.now() - start).total_seconds() 76 print(delta) 77 return ret 78 79 @TimeIt # add=TimeIt(add) 一看发现 是 实例化 80 def add(x, y): 81 time.sleep(1) 82 return x + y 83 84 add(4, 5) # 相当于实例 调用,如果作为一个计时器的还,进调用开始计时,退出调用,结束计时 85 86 ################## 处理字符串文档 ################################ 87 class TimeIt: 88 ''' this is class''' 89 def __init__(self, fn): 90 # 方法 1 91 # self.__doc__ = fn.__doc__ 92 # self.__name__ = fn.__doc__ 93 # 方法 2 94 # update_wrapper(self, fn) 95 # 方法 3 这里并不是装饰器,使用的是等价方式。 96 wraps(fn)(self) 97 self.fn = fn 98 99 def __enter__(self): 100 self.start = datetime.datetime.now() 101 return self 102 103 def __exit__(self, exc_type, exc_val, exc_tb): 104 delta = (datetime.datetime.now() - self.start).total_seconds() 105 print(delta) 106 107 def __call__(self, *args, **kwargs): 108 start = datetime.datetime.now() 109 ret = self.fn(*args, **kwargs) 110 delta = (datetime.datetime.now() - start).total_seconds() 111 print(delta) 112 return ret 113 114 @TimeIt # add=TimeIt(add) 一看发现 是 实例化 115 def add(x, y): 116 '''this is add function''' 117 time.sleep(1) 118 return x + y 119 120 print(add.__doc__) 121 122 123 # 此时,上面这个例子的类,可以做装饰器,又可以用在上下文管理。都实现了同样的功能 124 125 126 127 128 ############# 两个装饰器同时使用 ############################ 129 import time 130 import datetime 131 from functools import wraps, update_wrapper 132 133 def timeit(fn): 134 @wraps(fn)# update_wrapper(wapper)(fn) 135 def wrapper(*args, **kwargs): 136 start = datetime.datetime.now() 137 ret = fn(*args, **kwargs) 138 delta = (datetime.datetime.now() - start).total_seconds() 139 print('{} tooks {}'.format(fn.__name__, delta), '==+++++++=') 140 return ret 141 return wrapper 142 143 class TimeIt: 144 def __init__(self, fn): 145 self.__fn = fn 146 147 def __call__(self, *args, **kwargs): 148 start = datetime.datetime.now() 149 ret = self.__fn(*args, **kwargs) 150 delta = (datetime.datetime.now() - start).total_seconds() 151 print('{} tooks {}'.format(self.__fn.__name__, delta), '===============') 152 return ret 153 154 @timeit # add=timeit(add)=wrapper 155 @TimeIt # add=TimeIt(add) 156 def add(x, y): 157 '''this is add function''' 158 time.sleep(1) 159 return x + y 160 161 add(4, 5)
如下是正确的:因为通过wraps装饰器,给self提供了一个__name__属性
1 import time 2 import datetime 3 from functools import wraps, update_wrapper 4 5 def timeit(fn): 6 @wraps(fn)# update_wrapper(wapper)(fn) 7 def wrapper(*args, **kwargs): 8 start = datetime.datetime.now() 9 ret = fn(*args, **kwargs) 10 delta = (datetime.datetime.now() - start).total_seconds() 11 print('{} tooks {}'.format(fn.__name__, delta), '==+++++++=') 12 return ret 13 return wrapper 14 15 class TimeIt: 16 def __init__(self, fn): 17 wraps(fn)(self) # self.__name__ = fn.__name__ 这是 给了实例一个__name__属性 18 self.__fn = fn 19 20 def __call__(self, *args, **kwargs): 21 start = datetime.datetime.now() 22 ret = self.__fn(*args, **kwargs) 23 delta = (datetime.datetime.now() - start).total_seconds() 24 print('{} tooks {}'.format(self.__fn.__name__, delta), '===============') 25 return ret 26 27 @timeit # add=timeit(add) # 此时, add是一个实例 28 @TimeIt # add=TimeIt(add) 一看发现 是 实例化 29 def add(x, y): 30 '''this is add function''' 31 time.sleep(1) 32 return x + y 33 34 add(4, 5)
上下文应用场景:
- 增强功能:在代码执行的前后增加代码,以增强其功能,类似装饰器的功能
- 资源管理:打开了资源管理需要关闭,例如文件对象,网络连接,数据库连接
- 权限验证:在执行代码之前,做权限的验证,在__enter__中处理
** contextlib.contextmanager
它是一个装饰器实现上下文管理,装饰一个函数,而不用像类一样实现__enter__和__exit__方法
对下面的函数要求,必须有yield,也就是这个函数必须返回一个生成器,且只有yield一个值
也就是这个装饰器接受一个生成器对象作为参数。
举例:
1 import contextlib 2 3 @contextlib.contextmanager 4 def foo(): 5 print('enter') 6 yield 7 print('exit') 8 9 with foo() as f: 10 print(f) 11 print('-----------------------------') 12 @contextlib.contextmanager 13 def foo(): 14 print('enter') 15 yield 5 16 print('exit') 17 18 with foo() as f: 19 print(f)
结果:
1 enter 2 None 3 exit 4 ----------------------------- 5 enter 6 5 7 exit
只要后面能加括号就能调用!!!!!!
inspect他看一下类的签名,可调用对象才有签名
⑨、反射:
概念:
运行时区别于编译时,指的是程序被加载到内存中执行的时候。
反射,reflection,指的是运行时获取类型定义信息。
一个对象能够在运行时,能够像照镜子一样,反射出其类型信息。
简单说在Python中,能够通过一个对象,找出其type,class,attribute,或method的能力
称为反射或者自省
具有反射能力的函数有,type(), isinstance(),callable(),dir(),getattr()
编译时:
运行时:
解释器直接运行,或者 转换为.exe 文件直接运行。
反射相关的函数和方法:
需求:有一个Point类,查看他的属性,并修改它,动态为实例增加属性
测试:
1 ########################################### 简单的使用内建函数 #################################### 2 class Point: 3 def __init__(self, x, y): 4 self.x = x 5 self.y = y 6 7 def __str__(self): 8 return 'Point {}, {}'.format(self.x, self.y) 9 10 p = Point(4, 5) 11 print(p) 12 print(p.__dict__) 13 p.__dict__['y'] = 16 14 print(p.__dict__) 15 print(dir(p)) # 拿到的都是字符串 16 print(p.__dir__()) 17 18 print('-' * 40) 19 for n in dir(p): 20 print(getattr(p, n)) # p.n的意思,但是n是字符串,只不过通过点调用不用加引号 21 print() 22 23 print('-' * 40) 24 if hasattr(p, 'x'): 25 print(getattr(p, 'y')) 26 27 print(getattr(p, 'o', 100000))# 如果没有可以提供一个默认值 28 29 setattr(p, 'b', 1000) 30 print(p.__dict__)
结果:
1 D:python3.7python.exe E:/code_pycharm/code_test_daye/a测试.py 2 Point 4, 5 3 {'x': 4, 'y': 5} 4 {'x': 4, 'y': 16} 5 ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'x', 'y'] 6 ['x', 'y', '__module__', '__init__', '__str__', '__dict__', '__weakref__', '__doc__', '__repr__', '__hash__', '__getattribute__', '__setattr__', '__delattr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__new__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__'] 7 ---------------------------------------- 8 <class '__main__.Point'> 9 10 <method-wrapper '__delattr__' of Point object at 0x0000000001D65400> 11 12 {'x': 4, 'y': 16} 13 14 <built-in method __dir__ of Point object at 0x0000000001D65400> 15 16 None 17 18 <method-wrapper '__eq__' of Point object at 0x0000000001D65400> 19 20 <built-in method __format__ of Point object at 0x0000000001D65400> 21 22 <method-wrapper '__ge__' of Point object at 0x0000000001D65400> 23 24 <method-wrapper '__getattribute__' of Point object at 0x0000000001D65400> 25 26 <method-wrapper '__gt__' of Point object at 0x0000000001D65400> 27 28 <method-wrapper '__hash__' of Point object at 0x0000000001D65400> 29 30 <bound method Point.__init__ of <__main__.Point object at 0x0000000001D65400>> 31 32 <built-in method __init_subclass__ of type object at 0x0000000000482AC8> 33 34 <method-wrapper '__le__' of Point object at 0x0000000001D65400> 35 36 <method-wrapper '__lt__' of Point object at 0x0000000001D65400> 37 38 __main__ 39 40 <method-wrapper '__ne__' of Point object at 0x0000000001D65400> 41 42 <built-in method __new__ of type object at 0x000007FEE0208EC0> 43 44 <built-in method __reduce__ of Point object at 0x0000000001D65400> 45 46 <built-in method __reduce_ex__ of Point object at 0x0000000001D65400> 47 48 <method-wrapper '__repr__' of Point object at 0x0000000001D65400> 49 50 <method-wrapper '__setattr__' of Point object at 0x0000000001D65400> 51 52 <built-in method __sizeof__ of Point object at 0x0000000001D65400> 53 54 <bound method Point.__str__ of <__main__.Point object at 0x0000000001D65400>> 55 56 <built-in method __subclasshook__ of type object at 0x0000000000482AC8> 57 58 None 59 60 4 61 62 16 63 64 ---------------------------------------- 65 16 66 100000 67 {'x': 4, 'y': 16, 'b': 1000} 68 69 Process finished with exit code 0
测试2:给实例,类分别动态添加属性
1 class Point: 2 def __init__(self, x, y): 3 self.x = x 4 self.y = y 5 6 setattr(Point, 'show', lambda self:print(self.x, self.y)) # 定义一个类属性 7 8 p = Point(4, 5) 9 10 p.show() # 或者 11 getattr(p, 'show')() 12 13 setattr(p, 'show', lambda self:print(self, '---------'))# 在实例上定义了一个方法 14 getattr(p, 'show')(p) # p.show(p) # 可以看出,定义在实例上,没有绑定实例需要手动传入
结果2:
1 D:python3.7python.exe E:/code_pycharm/code_test_daye/a测试.py 2 4 5 3 4 5 4 <__main__.Point object at 0x0000000002978320> --------- 5 6 Process finished with exit code 0
这种动态增加属性的方式 和装饰器修饰一个类,Mixin方式的差异
装饰器和Mixin 定义时决定,而且不能修改实例属性而反射,可以运行时定义,修改类 或 实例的属性。
装饰器在定义时,加载到内存中的时候,就已经将原函数修饰了
Mixin类,是一种继承类,需要之前就定义好。对一些方法增强或者覆盖掉了。
这种动态增删属性的方式,是运行时改变类或者实例的方式。
因此,反射能力具有更大的灵活性。
在 if __name__ == '__main__': 下面可以对类进行属性的增删,但是添加装饰器??Mixin类??肯定不行啊!!!!
举例:使用类封装实现一个命令分发器:
1 ########################### 命令分发器################################# 2 # 简单实现 函数注册,并且传入参数 3 class Dispatcher: 4 def __init__(self): 5 pass 6 7 def reg(self, cmd, fn): 8 setattr(self, cmd, fn) 9 10 def run(self, *args, **kwargs): 11 while True: 12 cmd = input('>>>').strip() 13 if cmd == '': 14 break 15 else: 16 getattr(self, cmd, lambda :print('unkonw'))(*args, **kwargs) 17 18 def add(x, y): 19 print(x + y) 20 21 def sub(x, y): 22 print(x - y) 23 24 dis = Dispatcher() 25 dis.reg('add', add) 26 dis.reg('sub', sub) 27 dis.run(4, y = 5) 28 29 # # 实现函数注册,并且传入参数 30 class Dispatcher: 31 def __init__(self): 32 # self.commands = {} 33 pass 34 35 def reg(self, cmd):# 风险,万一人家实例本身有相同的属性名,这样就会冲突,覆盖,最好的方式是使用一个字典存 36 def wrapper(fn): 37 setattr(self, cmd, fn) 38 return fn 39 return wrapper 40 41 def run(self, *args, **kwargs): 42 while True: 43 cmd = input('>>>').strip() 44 if cmd == '': 45 break 46 else: 47 getattr(self, cmd, lambda :print('unkonw'))(*args, **kwargs) 48 49 dis = Dispatcher() 50 51 # 这样的话,加载的时候,函数已经被注册进去了,运行的时候,根据cmd直接调用 52 @dis.reg('add') # add = dis.reg('add')(add) 53 def add(x, y): 54 print(x + y) 55 56 @dis.reg('sub') # add = dis.reg('add')(add) 57 def sub(x, y): 58 print(x - y) 59 60 dis.run(4, y = 5)
反射相关的魔术方:都是实例相关的与类没有关系 self
__getattr__, __setattr__, __delattr__
单独使用__getattr__:
测试 :__setattr__
容易犯的递归调用!!!!!!!!!!!!
正常使用:只要是 点 属性的方式,都会到__setattr__设置,包括等价方式setattr(self, 'y', y) #self.y = y
比较灵活的使用:
测试: __delattr__的使用:
测试:__getattribute__
总结:
属性查找顺序:(没有描述器)
实例调用__getattribute__() ---> instance.__dict__ ---> instance.__class__.__dict__
---> 继承的祖先类(直到object)的__dict__ ——> 最后调用__getattr__()
当找不到属性后,没有__getattr__就抛异常,有,就先不抛,进入,处理之后,如果不需要抛,那就不会再抛异常,如果没处理了,异常抛出去,结束属性查找
*** 前后都有下划线,解释器管的
⑩、描述器:Descriptors
描述器的表现:
用到三个方法:__get__() __set__(), __delete__()
测试: 只测get,而且x都是作为类属性来测试
1 class A: 2 def __init__(self): 3 print('A() init') 4 self.a1 = 100 5 6 def __get__(self, instance, owner): 7 print('****************************') 8 print(1, self) 9 print(1, instance) 10 print(1, owner) 11 print('****************************') 12 13 return self 14 15 def __set__(self, instance, value): 16 print('****************************') 17 print(2, self) 18 print(2, instance) 19 print(2, value) 20 print('****************************') 21 22 def __delete__(self, instance): 23 print(3, self) 24 print(3, instance) 25 26 def __repr__(self): 27 return '<A 实例 {}>'.format(self.a1) 28 29 class B: 30 x = A() 31 def __int__(self): 32 print('B() init') 33 34 print(B.x) # 就会触发__get__ 35 print(B.x.a1) 36 37 print('---' * 50) 38 b = B() 39 print(b.x) 40 print(b.x.a1)
结果:
1 D:python3.7python.exe E:/code_pycharm/code_test_daye/a测试.py 2 A() init 3 **************************** 4 1 <A 实例 100> 5 1 None 6 1 <class '__main__.B'> 7 **************************** 8 <A 实例 100> 9 **************************** 10 1 <A 实例 100> 11 1 None 12 1 <class '__main__.B'> 13 **************************** 14 100 15 ------------------------------------------------------------------------------------------------------------------------------------------------------ 16 **************************** 17 1 <A 实例 100> 18 1 <__main__.B object at 0x0000000002938400> 19 1 <class '__main__.B'> 20 **************************** 21 <A 实例 100> 22 **************************** 23 1 <A 实例 100> 24 1 <__main__.B object at 0x0000000002938400> 25 1 <class '__main__.B'> 26 **************************** 27 100 28 29 Process finished with exit code 0
测试: 非数据描述器
1 ###非数据描述器 2 class A: 3 x = 1000000000 4 def __init__(self): 5 print('A() init') 6 self.a1 = 100 7 8 def __get__(self, instance, owner): 9 print('****************************') 10 print(1, self) 11 print(1, instance) 12 print(1, owner) 13 print('****************************') 14 # return self 15 16 # def __set__(self, instance, value): 17 # print('\\\\\\\\\\\\\\\') 18 # print(2, self) 19 # print(2, instance) # B的实例 20 # print(2, value) 21 # print('\\\\\\\\\\\\\\\') 22 23 def __repr__(self): 24 return '<A 实例 {}>'.format(self.a1) 25 26 class B: 27 x = A() 28 def __init__(self): 29 print('B() init') 30 self.x = 1000 31 32 b = B() 33 print(b.__dict__) 34 print(B.__dict__) 35 36 print(b.x) # 实例的x 37 38 print(B.x) # 类的x 39 40 非数据描述器,如果类访问 类属性,直接跳到 描述器的get 41 如果实例调用 ,先找自己有没有,没有在找 类的, 再跳到描述器的get
结果:
1 A() init 2 B() init 3 {'x': 1000} 4 {'__module__': '__main__', 'x': <A 实例 100>, '__init__': <function B.__init__ at 0x0000000002960400>, '__dict__': <attribute '__dict__' of 'B' objects>, '__weakref__': <attribute '__weakref__' of 'B' objects>, '__doc__': None} 5 1000 6 **************************** 7 1 <A 实例 100> 8 1 None 9 1 <class '__main__.B'> 10 **************************** 11 None
测试:数据描述器
1 ###################数据描述器 2 3 class A: 4 x = 1000000000 5 def __init__(self): 6 print('A() init') 7 self.a1 = 100 8 9 def __get__(self, instance, owner): 10 print('****************************') 11 print(1, self) 12 print(1, instance) 13 print(1, owner) 14 print('****************************') 15 return self 16 17 def __set__(self, instance, value): 18 print('\\\\\\\\\\\\\\\') 19 print(2, self) 20 print(2, instance) # B的实例 21 print(2, value) 22 print('\\\\\\\\\\\\\\\') 23 24 def __repr__(self): 25 return '<A 实例 {}>'.format(self.a1) 26 27 class B: 28 x = A() 29 def __init__(self): 30 print('B() init') 31 self.x = 1000 32 self.__dict__['y'] = 20000 33 34 b = B() 35 print(b.__dict__) 36 print(B.__dict__) 37 38 39 # 实例: 40 print(b.x ) # 数据描述器,优先使用描述器的 数据 41 print(b.y) #不同命,正常访问
结果:
1 A() init 2 B() init 3 \\\\\\\ 4 2 <A 实例 100> 5 2 <__main__.B object at 0x0000000002958400> 6 2 1000 7 \\\\\\\ 8 {'y': 20000} 9 {'__module__': '__main__', 'x': <A 实例 100>, '__init__': <function B.__init__ at 0x0000000002950488>, '__dict__': <attribute '__dict__' of 'B' objects>, '__weakref__': <attribute '__weakref__' of 'B' objects>, '__doc__': None} 10 **************************** 11 1 <A 实例 100> 12 1 <__main__.B object at 0x0000000002958400> 13 1 <class '__main__.B'> 14 **************************** 15 <A 实例 100> 16 20000
总结:
- 描述器的定义:
只实现了get,就是非数据描述器,实现了,get 和 set 或 delete 就是数据描述器
Python中的描述器:
描述器在 Python中应用非常广泛,Python的方法(包括staticmethod() 和classmethod都实现为非数据描述器,
因此,实例可以重新定义和覆盖方法。这允许单个实例获取与同一类的其他实例不同的行为
property() 函数实现为一个数据描述器,因此,实例不能覆盖属性的行为,
实现了:classmethod(), staticmethod()
1 ##### staticmethod 2 class StaticMethod: # 非数据描述器 3 def __init__(self, fn): 4 self.fn = fn 5 6 def __get__(self, instance, owner): 7 return self.fn 8 9 class B: 10 def __init__(self): 11 pass 12 13 @StaticMethod # foo = StaticMethod(foo) 14 def foo(): 15 print('static method') 16 17 18 B.foo() # 直接打印 19 20 ############# classmethod 21 class ClassMethod: # 非数据描述器 22 def __init__(self, fn): 23 self.fn = fn 24 25 def __get__(self, instance, owner): 26 ret = partial(self.fn, owner) 27 return ret 28 29 class B: 30 def __init__(self): 31 pass 32 33 @ClassMethod # foo = ClassMethod(foo) 34 def foo(cls): 35 print('class method') 36 37 38 B.foo() # 直接打印
如果是保护变量,或者 私有变量,直接加到属性列表中