Python-面向对象之魔术方法

阅读目录:

  1、特殊属性:Python的精华之一

  2、查看属性

  3、魔术方法

    1. 创建,初始化,销毁 (__new__, __init__, __del__)
    2. hash
    3. bool
    4. 可视化
    5. 运算符重载
    6. 可调用对象
    7. 上下文管理
    8. 反射
    9. 描述器
    10. 其他杂项

  

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有关,其他的都是属于实例的方法        

  分类:
    1. 创建,初始化,销毁 (__new__, __init__, __del__)
    2. hash
    3. bool
    4. 可视化
    5. 运算符重载
    6. 可调用对象
    7. 上下文管理
    8. 反射
    9. 描述器
    10. 其他杂项
    ①、创建,初始化,销毁:
__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
  结果:
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
View Code
  测试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
View Code
      总结:
        往往是用面向对象实现类的,需要做大量的运算,而运算符是这种运算在数学上最常见的表达方式
        通过重载,重新定义 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'))
missing
  结果:
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))
View Code

  结果:

 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]
View Code

    ⑦ 、可调用对象:

    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)
View Code

      如下是正确的:因为通过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)
View Code



        上下文应用场景:
      1. 增强功能:在代码执行的前后增加代码,以增强其功能,类似装饰器的功能
      2. 资源管理:打开了资源管理需要关闭,例如文件对象,网络连接,数据库连接
      3. 权限验证:在执行代码之前,做权限的验证,在__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)
View Code
      结果:
1 enter
2 None
3 exit
4 -----------------------------
5 enter
6 5
7 exit
View Code

  






只要后面能加括号就能调用!!!!!!

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

      结果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
结果2

       这种动态增加属性的方式 和装饰器修饰一个类,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)
只测get
       结果:
 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
View Code
      

       测试: 非数据描述器
 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
View Code

       测试:数据描述器

 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
View Code
     总结:
    1. 描述器的定义:
           只实现了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() # 直接打印
classmethod,staticmethod

       如果是保护变量,或者 私有变量,直接加到属性列表中

    
















































 

为什么要坚持,想一想当初!
原文地址:https://www.cnblogs.com/JerryZao/p/9672032.html