Skip to content
SRE运维进阶之路SRE运维进阶之路
github icon
    • 1 Python 简介

      • 1.1 初识Python
        • 1.2 Python 代码规范
          • 1.3 Python 虚拟环境
            • 1.4 使用 vscode 打造 python 开发环境
              • 1.5 pypi 配置国内镜像
              • 2 Python 基础

                • 2.1 Python基础语法
                  • 2.2 程序控制
                    • 2.3 Python数据类型

                      • 2.3.1 数值型
                        • 2.3.2 字符串 str
                          • 2.3.3 字节序列
                            • 2.3.4 列表 list & 元组 tuple
                              • 2.3.5 集合 set & 字典 dict
                            • 3 高级特性

                              • 3.1 线性结构特征 可迭代 & 切片
                                • 3.2 列表、集合、字典解析式
                                  • 3.3 生成器
                                    • 3.4 迭代器
                                    • 4 函数

                                      • 4.1 函数的定义 & 调用 & 返回值
                                        • 4.2 函数参数
                                          • 4.3 作用域
                                            • 4.4 递归函数
                                            • 5 函数式编程

                                              • 5.1 高阶函数
                                                • 5.2 返回函数
                                                  • 5.3 匿名函数
                                                    • 5.4 装饰器
                                                      • 5.5 偏函数
                                                      • 6 模块

                                                        • 6.1 Python 模块常用的几种安装方式
                                                          • 6.2 Python 的 setup.py 详解
                                                          • 7 IO编程

                                                            • 7.1 操作文件和目录
                                                              • 7.2 序列化和反序列化
                                                              • 8 异常、调试和测试

                                                                • 8.1 异常处理
                                                                • 9 面向对象编程

                                                                  • 9.1 类、实例和封装
                                                                    • 9.2 访问控制和属性装饰器
                                                                      • 9.3 继承、多态和Mixin
                                                                      • 10 进程和线程

                                                                        • 10.1 多进程
                                                                          • 10.2 多线程
                                                                            • 10.2 线程同步
                                                                            • 11 网络编程

                                                                              • 11.1 SocketServer
                                                                                • 11.2 TCP 编程
                                                                                • 11 魔术方法
                                                                                  • 实例化
                                                                                    • 可视化
                                                                                      • hash
                                                                                        • bool
                                                                                          • 运算符重载
                                                                                            • 上下文管理
                                                                                              • 上下文管理对象
                                                                                                • 上下文管理的安全性
                                                                                                  • with 语句
                                                                                                    • 上下文应用场景
                                                                                                      • contextlib.contextmanager
                                                                                                      • 反射概念
                                                                                                        • __getattr__()
                                                                                                          • __setattr__()
                                                                                                            • __delattr__()
                                                                                                            • __getattribute__
                                                                                                            • 17 IO 模型
                                                                                                              • python 实际工作中的实例
                                                                                                              • 前端学习笔记

                                                                                                                11 魔术方法

                                                                                                                author iconClaycalendar icon2021年5月11日category icon
                                                                                                                • Python
                                                                                                                timer icon大约 12 分钟

                                                                                                                此页内容
                                                                                                                • 实例化
                                                                                                                • 可视化
                                                                                                                • hash
                                                                                                                • bool
                                                                                                                • 运算符重载
                                                                                                                • 上下文管理
                                                                                                                  • 上下文管理对象
                                                                                                                  • 上下文管理的安全性
                                                                                                                  • with 语句
                                                                                                                  • 上下文应用场景
                                                                                                                  • contextlib.contextmanager
                                                                                                                • 反射概念
                                                                                                                  • __getattr__()
                                                                                                                  • __setattr__()
                                                                                                                  • __delattr__()
                                                                                                                • __getattribute__

                                                                                                                # 11 魔术方法

                                                                                                                # 实例化

                                                                                                                方法意义
                                                                                                                __new__实例化一个对象
                                                                                                                该方法需要返回一个值,如果该值不是 cls 的实例,则不会调用 __init__
                                                                                                                该方法永远都是静态方法
                                                                                                                class A:
                                                                                                                    def __new__(cls, *args, **kwargs):
                                                                                                                        print(cls)
                                                                                                                        print(args)
                                                                                                                        print(kwargs)
                                                                                                                        #return super().__new__(cls)
                                                                                                                        #return 1
                                                                                                                        return None
                                                                                                                
                                                                                                                    def __init__(self, name):
                                                                                                                        self.name = name
                                                                                                                        
                                                                                                                a = A()
                                                                                                                print(a)
                                                                                                                
                                                                                                                1
                                                                                                                2
                                                                                                                3
                                                                                                                4
                                                                                                                5
                                                                                                                6
                                                                                                                7
                                                                                                                8
                                                                                                                9
                                                                                                                10
                                                                                                                11
                                                                                                                12
                                                                                                                13
                                                                                                                14

                                                                                                                __new__ 方法很少使用,即使创建了该方法,也会使用 return super().__new__(cls) 基类object的 __new__ 方法来创建实例并返回。

                                                                                                                # 可视化

                                                                                                                方法意义
                                                                                                                __str__str()函数、format()函数、print()函数调用,需要返回对象的字符串表达。如果没
                                                                                                                有定义,就去调用__repr__ 方法返回字符串表达,如果 __repr__ 没有定义,就直接返回对象的内存地址信息
                                                                                                                __repr__内建函数repr()对一个对象获取字符串表达。
                                                                                                                调用 __repr__ 方法返回字符串表达,如果 __repr__ 也没有定义,就直接返回 object 的定义就是显示内存地址信息
                                                                                                                __bytes__bytes() 函数调用,返回一个对象的 bytes 表达,即返回 bytes 对象
                                                                                                                class A:
                                                                                                                    def __init__(self, name, age=18):
                                                                                                                        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()
                                                                                                                        import json
                                                                                                                        return json.dumps(self.__dict__).encode()
                                                                                                                    
                                                                                                                print(A('tom')) # print函数使用__str__
                                                                                                                print('{}'.format(A('tom')))
                                                                                                                print([A('tom')]) # []使用__str__,但其内部使用__repr__
                                                                                                                print([str(A('tom'))]) # []使用__str__,其中的元素使用str()函数也调用__str__
                                                                                                                print(bytes(A('tom')))
                                                                                                                
                                                                                                                1
                                                                                                                2
                                                                                                                3
                                                                                                                4
                                                                                                                5
                                                                                                                6
                                                                                                                7
                                                                                                                8
                                                                                                                9
                                                                                                                10
                                                                                                                11
                                                                                                                12
                                                                                                                13
                                                                                                                14
                                                                                                                15
                                                                                                                16
                                                                                                                17
                                                                                                                18
                                                                                                                19
                                                                                                                20
                                                                                                                21

                                                                                                                # hash

                                                                                                                方法意义
                                                                                                                __hash__内建函数 hash() 调用的返回值,返回一个整数。如果定义这个方法该类的实例就
                                                                                                                可hash。
                                                                                                                print(hash(1))
                                                                                                                print(hash('tom'))
                                                                                                                print(hash(('tom',)))
                                                                                                                
                                                                                                                1
                                                                                                                2
                                                                                                                3
                                                                                                                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'))) # 可hash 1
                                                                                                                print((A('tom'), A('tom'))) # (tom, tom)
                                                                                                                print([A('tom'), A('tom')]) # [tom, tom]
                                                                                                                print('~~~~~~~~~~~~~~~~~~~~')
                                                                                                                print({1, 1}) # {1}
                                                                                                                print({'tom', 'tom'}) # {'tom'}
                                                                                                                a1 = A('tom')
                                                                                                                a2 = A('tom')
                                                                                                                s = {a1, a2} # set
                                                                                                                print(s) # 去重了吗 没有 {tom, tom}
                                                                                                                print(hash(a1), hash(a2)) # 1, 1
                                                                                                                t1 = ('tom',)
                                                                                                                t2 = ('tom',)
                                                                                                                print(t1 is t2) # 3.7+是True,3.6及以下是False
                                                                                                                print(t1 == t2) # True
                                                                                                                print({t1, t2}, hash(t1), hash(t2)) #(tom,)
                                                                                                                print({('tom',), ('tom',)})
                                                                                                                print({'tom', 'tom'})
                                                                                                                
                                                                                                                
                                                                                                                1
                                                                                                                2
                                                                                                                3
                                                                                                                4
                                                                                                                5
                                                                                                                6
                                                                                                                7
                                                                                                                8
                                                                                                                9
                                                                                                                10
                                                                                                                11
                                                                                                                12
                                                                                                                13
                                                                                                                14
                                                                                                                15
                                                                                                                16
                                                                                                                17
                                                                                                                18
                                                                                                                19
                                                                                                                20
                                                                                                                21
                                                                                                                22
                                                                                                                23
                                                                                                                24
                                                                                                                25
                                                                                                                26
                                                                                                                27
                                                                                                                28
                                                                                                                29

                                                                                                                上例中, A的实例放在set中,它们hash值是相同的,为什么不能去重? hash值相同就会去重吗?

                                                                                                                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')))
                                                                                                                print((A('tom'), A('tom')))
                                                                                                                print([A('tom'), A('tom')])
                                                                                                                print('~~~~~~~~~~~~~~~~~~~~')
                                                                                                                s = {A('tom'), A('tom')} # set
                                                                                                                print(s)
                                                                                                                
                                                                                                                1
                                                                                                                2
                                                                                                                3
                                                                                                                4
                                                                                                                5
                                                                                                                6
                                                                                                                7
                                                                                                                8
                                                                                                                9
                                                                                                                10
                                                                                                                11
                                                                                                                12
                                                                                                                13
                                                                                                                14
                                                                                                                15
                                                                                                                16
                                                                                                                17
                                                                                                                18
                                                                                                                19
                                                                                                                方法意义
                                                                                                                __eq__对应==操作符,判断2个对象内容是否相等,返回bool值
                                                                                                                定义了这个方法,如果不提供 __hash__ 方法,那么实例将不可hash了

                                                                                                                __hash__ 方法只是返回一个hash值作为set的key,但是 去重 ,还需要 __eq__ 来判断2个对象是否相等。 hash值相等,只是hash冲突,不能说明两个对象是相等的。

                                                                                                                因此,一般来说提供 __hash__ 方法是为了作为set或者dict的key,如果 去重 要同时提供 __eq__ 方法。

                                                                                                                不可 hash 对象 isinstance(p1, collections.Hashable) 一定为 False。 去重 需要提供 __eq__ 方法。

                                                                                                                思考: list类实例为什么不可hash?

                                                                                                                源码中有一句 __hash__ = None,也就是如果调用 __hash__ ()相当于None(),一定报错。 所有类都继承object,而这个类是具有 __hash__ ()方法的,如果一个类不能被hash,就把 __hash__ 设置为None。

                                                                                                                # bool

                                                                                                                方法意义
                                                                                                                __bool__内建函数bool(),或者对象放在逻辑表达式的位置,调用这个函数返回布尔值。
                                                                                                                没有定义 __bool__ (),就找 __len__ ()返回长度,非0为真。
                                                                                                                如果 __len__ ()也没有定义,那么所有实例都返回真
                                                                                                                class A: pass
                                                                                                                a = A()
                                                                                                                
                                                                                                                print(bool(A)) # True
                                                                                                                print(bool(a)) #True
                                                                                                                
                                                                                                                class B:
                                                                                                                    def __bool__(self):
                                                                                                                        return False
                                                                                                                
                                                                                                                print(bool(B)) # True
                                                                                                                print(bool(B())) # False
                                                                                                                
                                                                                                                if B():
                                                                                                                    print('Real B instance')
                                                                                                                
                                                                                                                class C:
                                                                                                                    def __len__(self):
                                                                                                                        return 0
                                                                                                                
                                                                                                                print(bool(C)) # True
                                                                                                                print(bool(C())) # False
                                                                                                                
                                                                                                                if C():
                                                                                                                    print('Real C instance')
                                                                                                                
                                                                                                                1
                                                                                                                2
                                                                                                                3
                                                                                                                4
                                                                                                                5
                                                                                                                6
                                                                                                                7
                                                                                                                8
                                                                                                                9
                                                                                                                10
                                                                                                                11
                                                                                                                12
                                                                                                                13
                                                                                                                14
                                                                                                                15
                                                                                                                16
                                                                                                                17
                                                                                                                18
                                                                                                                19
                                                                                                                20
                                                                                                                21
                                                                                                                22
                                                                                                                23
                                                                                                                24
                                                                                                                25

                                                                                                                # 运算符重载

                                                                                                                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=18):
                                                                                                                        self.name = name
                                                                                                                        self.age = age
                                                                                                                
                                                                                                                    def __eq__(self, other):
                                                                                                                        return self.name == other.name and self.age == other.age
                                                                                                                
                                                                                                                    def __gt__(self, other):
                                                                                                                        return self.age > other.age
                                                                                                                
                                                                                                                    def __ge__(self, other):
                                                                                                                        return self.age >= other.age
                                                                                                                
                                                                                                                tom = A('tom')
                                                                                                                jerry = A('jerry', 16)
                                                                                                                print(tom == jerry, tom != jerry)
                                                                                                                print(tom > jerry, tom < jerry)
                                                                                                                print(tom >= jerry, tom <= jerry)
                                                                                                                
                                                                                                                1
                                                                                                                2
                                                                                                                3
                                                                                                                4
                                                                                                                5
                                                                                                                6
                                                                                                                7
                                                                                                                8
                                                                                                                9
                                                                                                                10
                                                                                                                11
                                                                                                                12
                                                                                                                13
                                                                                                                14
                                                                                                                15
                                                                                                                16
                                                                                                                17
                                                                                                                18
                                                                                                                19

                                                                                                                __eq__ 等于可以推断不等于 __gt__ 大于可以推断小于 __ge__ 大于等于可以推断小于等于 也就是用3个方法,就可以把所有比较解决了

                                                                                                                实现两个学生的成绩差

                                                                                                                class A:
                                                                                                                    def __init__(self, name, score):
                                                                                                                        self.name = name
                                                                                                                        self.score = score
                                                                                                                
                                                                                                                tom = A('tom', 80)
                                                                                                                jerry = A('jerry', 85)
                                                                                                                print(tom.score - jerry.score)
                                                                                                                
                                                                                                                1
                                                                                                                2
                                                                                                                3
                                                                                                                4
                                                                                                                5
                                                                                                                6
                                                                                                                7
                                                                                                                8
                                                                                                                class A:
                                                                                                                    def __init__(self, name, score):
                                                                                                                        self.name = name
                                                                                                                        self.score = score
                                                                                                                
                                                                                                                    def __sub__(self, other):
                                                                                                                        return self.score - other.score
                                                                                                                
                                                                                                                tom = A('tom', 80)
                                                                                                                jerry = A('jerry', 85)
                                                                                                                print(tom.score - jerry.score)
                                                                                                                
                                                                                                                print(tom - jerry)
                                                                                                                print('~~~~~~~~~~~~~~~~~~~~~~~~~~~')
                                                                                                                jerry -= tom # 调用什么
                                                                                                                print(tom)
                                                                                                                print(jerry) # 显示什么 5
                                                                                                                
                                                                                                                1
                                                                                                                2
                                                                                                                3
                                                                                                                4
                                                                                                                5
                                                                                                                6
                                                                                                                7
                                                                                                                8
                                                                                                                9
                                                                                                                10
                                                                                                                11
                                                                                                                12
                                                                                                                13
                                                                                                                14
                                                                                                                15
                                                                                                                16
                                                                                                                17
                                                                                                                class A:
                                                                                                                    def __init__(self, name, score):
                                                                                                                        self.name = name
                                                                                                                        self.score = score
                                                                                                                
                                                                                                                    def __sub__(self, other):
                                                                                                                        return self.score - other.score
                                                                                                                
                                                                                                                    def __isub__(self, other):
                                                                                                                        #return A(self.name, self.score - other.score)
                                                                                                                        self.score -= other.score
                                                                                                                        return self
                                                                                                                    
                                                                                                                    def __repr__(self):
                                                                                                                        return "<A name={}, score={}>".format(self.name, self.score)
                                                                                                                
                                                                                                                tom = A('tom', 80)
                                                                                                                jerry = A('jerry', 85)
                                                                                                                print(tom.score - jerry.score)
                                                                                                                print(tom - jerry)
                                                                                                                print('~~~~~~~~~~~~~~~~~~~~~~~~~~~')
                                                                                                                jerry -= tom # 调用什么
                                                                                                                print(tom)
                                                                                                                print(jerry)
                                                                                                                
                                                                                                                1
                                                                                                                2
                                                                                                                3
                                                                                                                4
                                                                                                                5
                                                                                                                6
                                                                                                                7
                                                                                                                8
                                                                                                                9
                                                                                                                10
                                                                                                                11
                                                                                                                12
                                                                                                                13
                                                                                                                14
                                                                                                                15
                                                                                                                16
                                                                                                                17
                                                                                                                18
                                                                                                                19
                                                                                                                20
                                                                                                                21
                                                                                                                22
                                                                                                                23
                                                                                                                24

                                                                                                                思考:list的+和+=的区别。tuple呢?

                                                                                                                # 上下文管理

                                                                                                                文件 IO 操作可以对文件对象使用上下文管理,使用 with...as 语法。

                                                                                                                with open('test') as f:
                                                                                                                	pass
                                                                                                                
                                                                                                                1
                                                                                                                2

                                                                                                                仿照上例写一个自己的类,实现上下文管理

                                                                                                                class Point:
                                                                                                                	pass
                                                                                                                
                                                                                                                with Point() as p: # AttributeError: __exit__
                                                                                                                	pass
                                                                                                                
                                                                                                                1
                                                                                                                2
                                                                                                                3
                                                                                                                4
                                                                                                                5

                                                                                                                提示属性错误,没有 __exit__ ,看了需要这个属性 某些版本会显示没有 __enter__

                                                                                                                # 上下文管理对象

                                                                                                                当一个对象同时实现了 __enter__ ()和 __exit__ ()方法,它就属于上下文管理的对象

                                                                                                                方法意义
                                                                                                                __enter__进入与此对象相关的上下文。如果存在该方法,with 语法会把该方法的返回值作
                                                                                                                为绑定到 as 子句中指定的变量上
                                                                                                                __exit__退出与此对象相关的上下文
                                                                                                                import time
                                                                                                                
                                                                                                                class Point:
                                                                                                                    def __init__(self):
                                                                                                                        print('init ~~~~~~~~')
                                                                                                                        time.sleep(1)
                                                                                                                        print('init over')
                                                                                                                
                                                                                                                    def __enter__(self):
                                                                                                                        print('enter ~~~~~~~~')
                                                                                                                
                                                                                                                    def __exit__(self, exc_type, exc_val, exc_tb):
                                                                                                                        print('exit ============')
                                                                                                                
                                                                                                                with Point() as p:
                                                                                                                    print('in with-------------')
                                                                                                                
                                                                                                                    time.sleep(2)
                                                                                                                    print('with over')
                                                                                                                
                                                                                                                print('=======end==========')
                                                                                                                
                                                                                                                # 输出结果为
                                                                                                                init ~~~~~~~~
                                                                                                                init over
                                                                                                                enter ~~~~~~~~
                                                                                                                in with-------------
                                                                                                                with over
                                                                                                                exit ============
                                                                                                                =======end==========
                                                                                                                
                                                                                                                1
                                                                                                                2
                                                                                                                3
                                                                                                                4
                                                                                                                5
                                                                                                                6
                                                                                                                7
                                                                                                                8
                                                                                                                9
                                                                                                                10
                                                                                                                11
                                                                                                                12
                                                                                                                13
                                                                                                                14
                                                                                                                15
                                                                                                                16
                                                                                                                17
                                                                                                                18
                                                                                                                19
                                                                                                                20
                                                                                                                21
                                                                                                                22
                                                                                                                23
                                                                                                                24
                                                                                                                25
                                                                                                                26
                                                                                                                27
                                                                                                                28
                                                                                                                29
                                                                                                                30

                                                                                                                实例化对象的时候,并不会调用enter,进入with语句块调用 __enter__ 方法,然后执行语句体,最后离开 with 语句块的时候,调用 __exit__ 方法。

                                                                                                                with 可以开启一个上下文运行环境,在执行前做一些准备工作,执行后做一些收尾工作。

                                                                                                                注意,with并不开启一个新的作用域。

                                                                                                                # 上下文管理的安全性

                                                                                                                import time
                                                                                                                
                                                                                                                class Point:
                                                                                                                    def __init__(self):
                                                                                                                        print('init ~~~~~~~~')
                                                                                                                        time.sleep(1)
                                                                                                                        print('init over')
                                                                                                                
                                                                                                                    def __enter__(self):
                                                                                                                        print('enter ~~~~~~~~')
                                                                                                                
                                                                                                                    def __exit__(self, exc_type, exc_val, exc_tb):
                                                                                                                        print('exit ============')
                                                                                                                
                                                                                                                with Point() as p:
                                                                                                                    print('in with-------------')
                                                                                                                    raise Exception('error')
                                                                                                                    time.sleep(2)
                                                                                                                    print('with over')
                                                                                                                    
                                                                                                                print('=======end==========')
                                                                                                                
                                                                                                                1
                                                                                                                2
                                                                                                                3
                                                                                                                4
                                                                                                                5
                                                                                                                6
                                                                                                                7
                                                                                                                8
                                                                                                                9
                                                                                                                10
                                                                                                                11
                                                                                                                12
                                                                                                                13
                                                                                                                14
                                                                                                                15
                                                                                                                16
                                                                                                                17
                                                                                                                18
                                                                                                                19
                                                                                                                20
                                                                                                                21

                                                                                                                可以看出在抛出异常的情况下,with的__exit__照样执行,上下文管理是安全的。

                                                                                                                # with 语句

                                                                                                                # t3.py文件中写入下面代码
                                                                                                                class Point:
                                                                                                                    def __init__(self):
                                                                                                                        print('init')
                                                                                                                
                                                                                                                    def __enter__(self):
                                                                                                                        print('enter')
                                                                                                                
                                                                                                                    def __exit__(self, exc_type, exc_val, exc_tb):
                                                                                                                        print('exit')
                                                                                                                
                                                                                                                f = open('t3.py')
                                                                                                                with f as p:
                                                                                                                    print(f)
                                                                                                                    print(p)
                                                                                                                    print(f is p) # 打印什么 True
                                                                                                                    print(f == p) # 打印什么 True
                                                                                                                
                                                                                                                p = f = None
                                                                                                                p = Point()
                                                                                                                with p as f:
                                                                                                                    print('in with-------------')
                                                                                                                    print(p == f)
                                                                                                                    print('with over')
                                                                                                                
                                                                                                                print('=======end==========')
                                                                                                                
                                                                                                                1
                                                                                                                2
                                                                                                                3
                                                                                                                4
                                                                                                                5
                                                                                                                6
                                                                                                                7
                                                                                                                8
                                                                                                                9
                                                                                                                10
                                                                                                                11
                                                                                                                12
                                                                                                                13
                                                                                                                14
                                                                                                                15
                                                                                                                16
                                                                                                                17
                                                                                                                18
                                                                                                                19
                                                                                                                20
                                                                                                                21
                                                                                                                22
                                                                                                                23
                                                                                                                24
                                                                                                                25
                                                                                                                26

                                                                                                                问题在于 __enter__ 方法上,它将自己的返回值赋给f。修改上例

                                                                                                                # t3.py文件中写入下面代码
                                                                                                                class Point:
                                                                                                                    def __init__(self):
                                                                                                                        print('init')
                                                                                                                
                                                                                                                    def __enter__(self):
                                                                                                                        print('enter')
                                                                                                                        return self
                                                                                                                
                                                                                                                    def __exit__(self, exc_type, exc_val, exc_tb):
                                                                                                                        print('exit')
                                                                                                                p = f = None
                                                                                                                p = Point()
                                                                                                                with p as f:
                                                                                                                    print('in with-------------')
                                                                                                                    print(p == f)
                                                                                                                    print('with over')
                                                                                                                
                                                                                                                print('=======end==========')
                                                                                                                
                                                                                                                1
                                                                                                                2
                                                                                                                3
                                                                                                                4
                                                                                                                5
                                                                                                                6
                                                                                                                7
                                                                                                                8
                                                                                                                9
                                                                                                                10
                                                                                                                11
                                                                                                                12
                                                                                                                13
                                                                                                                14
                                                                                                                15
                                                                                                                16
                                                                                                                17
                                                                                                                18
                                                                                                                19

                                                                                                                with 语法,会调用 with 后的对象的__enter__方法,如果有 as,则将该方法的返回值赋给 as 子句的变量

                                                                                                                上例,可以等价为f = p.__enter__()

                                                                                                                # 上下文应用场景

                                                                                                                1. 增强功能 在代码执行的前后增加代码,以增强其功能。类似装饰器的功能。
                                                                                                                2. 资源管理 打开了资源需要关闭,例如文件对象、网络连接、数据库连接等
                                                                                                                3. 权限验证 在执行代码之前,做权限的验证,在 __enter__ 中处理

                                                                                                                # contextlib.contextmanager

                                                                                                                contextlib.contextmanager 它是一个装饰器实现上下文管理,装饰一个函数,而不用像类一样实现__enter__ 和 __exit__ 方法。

                                                                                                                对下面的函数有要求:必须有yield,也就是这个函数必须返回一个生成器,且只有yield一个值。

                                                                                                                也就是这个装饰器接收一个生成器对象作为参数。

                                                                                                                import contextlib
                                                                                                                
                                                                                                                @contextlib.contextmanager
                                                                                                                def foo(): #
                                                                                                                    print('enter') # 相当于__enter__()
                                                                                                                    yield # yield 5,yield的值只能有一个,相当于作为__enter__方法的返回值
                                                                                                                    print('exit') # 相当于__exit__()
                                                                                                                
                                                                                                                with foo() as f:
                                                                                                                    #raise Exception()
                                                                                                                    print(f)
                                                                                                                
                                                                                                                1
                                                                                                                2
                                                                                                                3
                                                                                                                4
                                                                                                                5
                                                                                                                6
                                                                                                                7
                                                                                                                8
                                                                                                                9
                                                                                                                10
                                                                                                                11

                                                                                                                f 接收 yield 语句的返回值。

                                                                                                                上面的程序看似不错但是,增加一个异常试一试,发现不能保证 exit 的执行,怎么办? 增加 try finally。

                                                                                                                import contextlib
                                                                                                                
                                                                                                                @contextlib.contextmanager
                                                                                                                def foo(): #
                                                                                                                    print('enter') # 相当于__enter__()
                                                                                                                    try:
                                                                                                                        yield # yield 5,yield的值只能有一个,相当于作为__enter__方法的返回值
                                                                                                                    finally:
                                                                                                                        print('exit') # 相当于__exit__()
                                                                                                                
                                                                                                                with foo() as f:
                                                                                                                    raise Exception()
                                                                                                                    print(f)
                                                                                                                
                                                                                                                1
                                                                                                                2
                                                                                                                3
                                                                                                                4
                                                                                                                5
                                                                                                                6
                                                                                                                7
                                                                                                                8
                                                                                                                9
                                                                                                                10
                                                                                                                11
                                                                                                                12
                                                                                                                13

                                                                                                                上例这么做有什么意义呢? 当 yield 发生处为生成器函数增加了上下文管理。这是为函数增加上下文机制的方式。

                                                                                                                • 把 yield 之前的当做__enter__方法执行
                                                                                                                • 把 yield 之后的当做__exit__方法执行
                                                                                                                • 把 yield 的值作为__enter__的返回值

                                                                                                                练习:为add函数计时

                                                                                                                import contextlib
                                                                                                                import datetime
                                                                                                                import time
                                                                                                                
                                                                                                                @contextlib.contextmanager
                                                                                                                def timeit():
                                                                                                                    print('enter')
                                                                                                                    start = datetime.datetime.now()
                                                                                                                    try:
                                                                                                                        yield
                                                                                                                    finally:
                                                                                                                        print('exit')
                                                                                                                        delta = (datetime.datetime.now() - start).total_seconds()
                                                                                                                        print('delta = {}'.format(delta))
                                                                                                                
                                                                                                                def add(x, y):
                                                                                                                    time.sleep(2)
                                                                                                                    return x + y
                                                                                                                
                                                                                                                with timeit():
                                                                                                                    print(add(4, 5))
                                                                                                                
                                                                                                                1
                                                                                                                2
                                                                                                                3
                                                                                                                4
                                                                                                                5
                                                                                                                6
                                                                                                                7
                                                                                                                8
                                                                                                                9
                                                                                                                10
                                                                                                                11
                                                                                                                12
                                                                                                                13
                                                                                                                14
                                                                                                                15
                                                                                                                16
                                                                                                                17
                                                                                                                18
                                                                                                                19
                                                                                                                20
                                                                                                                21

                                                                                                                总结

                                                                                                                如果业务逻辑简单可以使用函数加 contextlib.contextmanager 装饰器方式,如果业务复杂,用类的方 式加 __enter__ 和 __exit__ 方法方便。

                                                                                                                # 反射概念

                                                                                                                概述

                                                                                                                运行时,runtime,区别于编译时,指的是程序被加载到内存中执行的时候。 反射,reflection,指的是运行时获取类型定义信息。 一个对象能够在运行时,像照镜子一样,反射出其类型信息。 简单说,在Python中,能够通过一个对象,找出其type、class、attribute 或 method 的能力,称为反射或者自省。 具有反射能力的函数有 type()、isinstance()、callable()、dir()、getattr() 等

                                                                                                                内建函数意义
                                                                                                                getattr(object, name[, default])通过name返回object的属性值。当属性不存在,将使用default返回,如果
                                                                                                                没有default,则抛出AttributeError。name必须为字符串
                                                                                                                setattr(object, name, value)object的属性存在,则覆盖,不存在,新增
                                                                                                                hasattr(object, name)判断对象是否有这个名字的属性,name必须为字符串
                                                                                                                class Point:
                                                                                                                    def __init__(self, x, y):
                                                                                                                        self.x = x
                                                                                                                        self.y = y
                                                                                                                
                                                                                                                p1 = Point(4, 5)
                                                                                                                print(p1)
                                                                                                                
                                                                                                                1
                                                                                                                2
                                                                                                                3
                                                                                                                4
                                                                                                                5
                                                                                                                6
                                                                                                                7

                                                                                                                为上面 Point 类增加打印的方法

                                                                                                                class Point:
                                                                                                                    def __init__(self, x, y):
                                                                                                                        self.x = x
                                                                                                                        self.y = y
                                                                                                                
                                                                                                                p1 = Point(4, 5)
                                                                                                                print(p1)
                                                                                                                print(p1.x, p1.y)
                                                                                                                print(getattr(p1, 'x'), getattr(p1, 'y'))
                                                                                                                setattr(p1, 'x', 10)
                                                                                                                setattr(Point, '__str__', lambda self: "<Point {},{}>".format(self.x, self.y))
                                                                                                                print(p1)
                                                                                                                
                                                                                                                1
                                                                                                                2
                                                                                                                3
                                                                                                                4
                                                                                                                5
                                                                                                                6
                                                                                                                7
                                                                                                                8
                                                                                                                9
                                                                                                                10
                                                                                                                11
                                                                                                                12

                                                                                                                反射相关的魔术方法 __getattr__() 、 __setattr__() 、 __delattr__() 这三个魔术方法,分别测试这三个方法

                                                                                                                # __getattr__()

                                                                                                                class Point:
                                                                                                                    def __init__(self, x, y):
                                                                                                                        self.x = x
                                                                                                                        self.y = y
                                                                                                                
                                                                                                                    def __getattr__(self, item):
                                                                                                                        print('getattr~~~')
                                                                                                                        print(item)
                                                                                                                        return 100
                                                                                                                
                                                                                                                p1 = Point(4, 5)
                                                                                                                print(p1.x)
                                                                                                                print(p1.y)
                                                                                                                print(p1.z)
                                                                                                                
                                                                                                                1
                                                                                                                2
                                                                                                                3
                                                                                                                4
                                                                                                                5
                                                                                                                6
                                                                                                                7
                                                                                                                8
                                                                                                                9
                                                                                                                10
                                                                                                                11
                                                                                                                12
                                                                                                                13
                                                                                                                14

                                                                                                                实例属性查找顺序为:

                                                                                                                __instance.__dict__ --> instance.__class__.__dict__ --> 继承的祖先类(直到object)的__dict__ --> 找不到 --> 调用__getattr__()

                                                                                                                # __setattr__()

                                                                                                                class Point:
                                                                                                                    def __init__(self, x, y):
                                                                                                                        self.x = x
                                                                                                                        self.y = y
                                                                                                                
                                                                                                                    def __getattr__(self, item):
                                                                                                                        print('getattr~~~')
                                                                                                                        print(item)
                                                                                                                        return 100
                                                                                                                
                                                                                                                    def __setattr__(self, key, value):
                                                                                                                        print('setattr~~~, {}={}'.format(key, value))
                                                                                                                
                                                                                                                p1 = Point(4, 5)
                                                                                                                print(p1.x)
                                                                                                                print(p1.y)
                                                                                                                print(p1.__dict__)
                                                                                                                
                                                                                                                1
                                                                                                                2
                                                                                                                3
                                                                                                                4
                                                                                                                5
                                                                                                                6
                                                                                                                7
                                                                                                                8
                                                                                                                9
                                                                                                                10
                                                                                                                11
                                                                                                                12
                                                                                                                13
                                                                                                                14
                                                                                                                15
                                                                                                                16
                                                                                                                17

                                                                                                                p1 的实例字典里面什么都没有,而且访问x和y属性的时候竟然访问到了__getattr__(),为什么?

                                                                                                                class Point:
                                                                                                                    def __init__(self, x, y):
                                                                                                                        self.x = x
                                                                                                                        self.y = y
                                                                                                                
                                                                                                                    def __getattr__(self, item):
                                                                                                                        print('getattr~~~')
                                                                                                                        print(item)
                                                                                                                        return 100
                                                                                                                
                                                                                                                    def __setattr__(self, key, value):
                                                                                                                        print('setattr~~~, {}={}'.format(key, value))
                                                                                                                        self.__dict__[key] = value
                                                                                                                        # setattr(self, key, value) # 对吗 不对,self会产生递归
                                                                                                                
                                                                                                                p1 = Point(4, 5)
                                                                                                                print(p1.x)
                                                                                                                print(p1.y)
                                                                                                                print(p1.__dict__)
                                                                                                                
                                                                                                                1
                                                                                                                2
                                                                                                                3
                                                                                                                4
                                                                                                                5
                                                                                                                6
                                                                                                                7
                                                                                                                8
                                                                                                                9
                                                                                                                10
                                                                                                                11
                                                                                                                12
                                                                                                                13
                                                                                                                14
                                                                                                                15
                                                                                                                16
                                                                                                                17
                                                                                                                18
                                                                                                                19

                                                                                                                __setattr__() 方法,可以拦截对实例属性的增加、修改操作,如果要设置生效,需要自己操作实例 的 __dict__ 。

                                                                                                                # __delattr__()

                                                                                                                class Point:
                                                                                                                    Z = 100
                                                                                                                    def __init__(self, x, y):
                                                                                                                        self.x = x
                                                                                                                        self.y = y
                                                                                                                
                                                                                                                    def __delattr__(self, item):
                                                                                                                        print('delattr, {}'.format(item))
                                                                                                                
                                                                                                                p1 = Point(4, 5)
                                                                                                                del p1.x
                                                                                                                del p1.y
                                                                                                                del p1.Z
                                                                                                                print(p1.__dict__)
                                                                                                                print(Point.__dict__)
                                                                                                                del Point.Z
                                                                                                                print(Point.__dict__)
                                                                                                                
                                                                                                                1
                                                                                                                2
                                                                                                                3
                                                                                                                4
                                                                                                                5
                                                                                                                6
                                                                                                                7
                                                                                                                8
                                                                                                                9
                                                                                                                10
                                                                                                                11
                                                                                                                12
                                                                                                                13
                                                                                                                14
                                                                                                                15
                                                                                                                16
                                                                                                                17

                                                                                                                通过实例删除属性,就会尝试调用该魔术方法。

                                                                                                                # __getattribute__

                                                                                                                class Point:
                                                                                                                    Z = 100
                                                                                                                    def __init__(self, x, y):
                                                                                                                        self.x = x
                                                                                                                        self.y = y
                                                                                                                
                                                                                                                
                                                                                                                p1 = Point(4, 5)
                                                                                                                print(p1.x, p1.y)
                                                                                                                print(Point.Z, p1.Z)
                                                                                                                print('-' * 30)
                                                                                                                
                                                                                                                # 为Point类增加__getattribute__,观察变化
                                                                                                                class Point:
                                                                                                                    Z = 100
                                                                                                                    def __init__(self, x, y):
                                                                                                                        self.x = x
                                                                                                                        self.y = y
                                                                                                                
                                                                                                                    def __getattribute__(self, item):
                                                                                                                        print(item)
                                                                                                                
                                                                                                                p1 = Point(4, 5)
                                                                                                                print(p1.x, p1.y)
                                                                                                                print(Point.Z, p1.Z)
                                                                                                                print(p1.__dict__)
                                                                                                                
                                                                                                                1
                                                                                                                2
                                                                                                                3
                                                                                                                4
                                                                                                                5
                                                                                                                6
                                                                                                                7
                                                                                                                8
                                                                                                                9
                                                                                                                10
                                                                                                                11
                                                                                                                12
                                                                                                                13
                                                                                                                14
                                                                                                                15
                                                                                                                16
                                                                                                                17
                                                                                                                18
                                                                                                                19
                                                                                                                20
                                                                                                                21
                                                                                                                22
                                                                                                                23
                                                                                                                24
                                                                                                                25
                                                                                                                26

                                                                                                                实例的所有的属性访问,第一个都会调用 __getattribute__ 方法,它阻止了属性的查找,该方法应该返回(计算后的)值或者抛出一个 AttributeError 异常。

                                                                                                                • 它的return值将作为属性查找的结果。
                                                                                                                • 如果抛出AttributeError异常,则会直接调用 __getattr__ 方法,因为表示属性没有找到。
                                                                                                                
                                                                                                                class Point:
                                                                                                                    Z = 100
                                                                                                                    def __init__(self, x, y):
                                                                                                                        self.x = x
                                                                                                                        self.y = y
                                                                                                                
                                                                                                                    def __getattr__(self, item):
                                                                                                                        return 'missing {}'.format(item)
                                                                                                                
                                                                                                                    def __getattribute__(self, item):
                                                                                                                        # print(item)
                                                                                                                        # raise AttributeError('Not Found')
                                                                                                                        # return self.__dict__[item] # 不能用,会产生递归
                                                                                                                        # pass
                                                                                                                        # return object.__getattribute__(self, item)
                                                                                                                        return super().__getattribute__(item)
                                                                                                                
                                                                                                                p1 = Point(4, 5)
                                                                                                                print(p1.x, p1.y)
                                                                                                                print(Point.Z, p1.Z)
                                                                                                                print(p1.__dict__)
                                                                                                                
                                                                                                                1
                                                                                                                2
                                                                                                                3
                                                                                                                4
                                                                                                                5
                                                                                                                6
                                                                                                                7
                                                                                                                8
                                                                                                                9
                                                                                                                10
                                                                                                                11
                                                                                                                12
                                                                                                                13
                                                                                                                14
                                                                                                                15
                                                                                                                16
                                                                                                                17
                                                                                                                18
                                                                                                                19
                                                                                                                20
                                                                                                                21
                                                                                                                22

                                                                                                                __getattribute__ 方法中为了避免在该方法中无限的递归,它的实现应该永远调用基类的同名方法以访问需要的任何属性,例如 object.__getattribute__(self, name) 。

                                                                                                                注意,除非你明确地知道 __getattribute__ 方法用来做什么,否则不要使用它。

                                                                                                                总结

                                                                                                                魔术方法意义
                                                                                                                __getattr__()当通过搜索实例、实例的类及祖先类查不到属性,就会调用此方法
                                                                                                                __setattr__()通过 .访问实例属性,进行增加、修改都要调用它
                                                                                                                __delattr__()当通过实例来删除属性时调用此方法
                                                                                                                __getattribute__()实例所有的属性调用都从这个方法开始

                                                                                                                实例属性查找顺序:

                                                                                                                实例调用__getattribute__() --> instance.__dict__ --> instance.__class__.__dict__ --> 继承的祖先类(直到object)的__dict__ --> 调用__getattr__()

                                                                                                                edit icon编辑此页open in new window
                                                                                                                上次编辑于: 2021/6/22 09:43:45
                                                                                                                贡献者: clay-wangzhi
                                                                                                                下一页
                                                                                                                17 IO 模型
                                                                                                                备案号:冀ICP备2021007336号
                                                                                                                Copyright © 2023 Clay