Python 部分知识点总结(八)

此篇博客只是记录第十周未掌握或不熟悉的知识点,用来加深印象。

一、魔术方法

  1. __name__:类、函数、方法等的名字
         __module__:类定义所在的模块名
         __class__:对象或类所属的类
         __bases__:类的基类的元祖,顺序为它们在基类列表中出现的顺序
         __doc__:类、函数的文档字符串,如果没有定义则为 None
         __mro__:类的 mro,class.mro( ) 返回的结果保存在 __mro__ 中
         __dict__:类或实例的属性,可写的字典
  2. 查看属性
    __dir__:返回类或者对象的所有成员名称列表,dir() 函数就是调用 __dir__(),如果提供 __dir__(),则返回属性的列表(返回值要求必须是可迭代,且只对实例有影响),否则会尽量从 __dir__ 属性中收集信息
    dir() 对于不同类型的对象具有不同的行为:
                模块对象,返回的列表包含模块的属性名
                类型或类对象,返回的列表包含类的属性名,以及它的基类的属性名
                否则,返回列表包含对象的属性名、它的类的属性名和类的基类的属性名
  3. __hash__:内建函数 hash() 调用的返回值,返回一个整数,如果定义这个方法,该类的实例就可 hash
    __eq__:对应 == 操作符,判断两个对象是否相等,返回 bool 值
    __hash__ 方法只是返回一个 hash 值作为 set 的 key,但是去重,还需要  __eq__ 来判断两个对象是否相当,如果 hash 值相等,只是 hash 冲突,不能说明两个对象是相等的。
    因此,一般来说,提供 __hash__ 方法是为了作为 set 或者 dict 的 key,所以去重要同时提供 __eq__ 方法
    列表类不可哈希的原因就是因为源码中有一句 __hash__ = None,也就是说如果调用 __hash__() 相当于 None(),一定报错
  4. __bool__:内建函数 bool(),或者对象放在逻辑表达式的位置,调用这个函数返回布尔值。没有定义 __bool__(),就找 __len__() 返回长度,非 0 位就是真。如果 __len__() 也没有定义,那么所有实例都返回真
  5. 可视化
    __repr__:内建函数 repr() 对一个对象获取字符串表达,调用 __repr__ 方法返回字符串表达,如果 __repr__ 没有定义,就直接返回 object ,即显示内存地址信息
    __str__:str() 函数、内建函数 format()、print() 函数调用,需要返回对象的字符串表达,如果没有定义,就去调用 __repr__ 方法返回字符串表达,如果 __repr__ 没有定义,就直接返回对象的内存地址信息
    __bytes__:byte() 函数调用,返回一个对象的 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 ‘repr: {},{}’.format(self.name, self.age)
            import json
            return json.dumps(self.__dict__).encode()
    a = A(‘tom’)
    print(a)  –> 调用 __str__
    print(bytes(a))
    print([a])  –> 列表使用 __str__,但其内部使用 __repr__
    print(str(a))
    print(‘str:a, 1’)  –> 字符串直接输出没有引号
    s = ‘1’
    print(s)
    print([‘a’], (s,))  –>  字符串在基本数据类型内部输出有引号
    print({s, ‘a’})

    str: tom,18
    b'{“name”: “tom”, “age”: 18}’
    [repr: tom,18]
    str: tom,18
    str:a, 1
    1
    [‘a’] (‘1’,)
    {‘1’, ‘a’}
  6. 运算符重载
    <、<=、==、>、>=、!=  对应  __lt__、__le__、 __eq__ 、__gt__、 __ge__、__ne__
    +、-、*、/、%、//、**、divmod  对应  __add__、__sub__、__mul__、__truediv__、__mod__、__floordiv__、__pow__、__divmod__
    +=、-=、*=、/=、%=、//=、**=  对应  __iadd__、__isub__、__imul__、__itruediv__、__imod__、__ifloordiv__、__ipow__
  7. __lt__、__le__、 __eq__ 、__gt__、 __ge__、__ne__ 是比较大小必须实现的方法,但是全部写完太麻烦,使用 @functools.total_ordering 装饰器就可以大大简化代码,但是要求 __eq__ 必须实现,其它方法实现其一即可
    from functools import total_ordering
    @total_ordering
    class Person:
        def __init__(self, name, age):
            self.name = name
            self.age = age
        def __eq__(self, other):
            return self.age == other.age
        def __gt__(self, other):
            return self.age > other.age
  8. 容器相关方法
    __len__:内建函数 len(),返回对象的长度(>=0 的整数),如果把对象当作容器类型看,就如同 list 或者 dict。bool() 函数调用的时候,如果没有 __bool__ 方法,则会看 __len__ 方法是否存在,存在返回非 0 为真
    __iter__:迭代容器时调用,返回一个新的迭代器对象
    __contains__:in 成员运算符,没有实现,就调用 __iter__ 方法遍历
    __getitem__:实现 self[key] 访问,序列对象,key 接受整数为索引,或者切片。对于 set 和 dict,key 为 hashable。key 不存在引发 KeyError 异常
    __setitem__:和 __getitem__ 的访问类似,是设置值的方法
    __missing__:字典或其子类使用 __getitem__() 调用时,key 不存在执行该方法
  9. 购物车代码实现:
    class Cart:
        def __init__(self):
            self.items = []
        def __len__(self):
            return len(self.items)
        def additem(self, item):
            self.items.append(item)
        def __iter__(self):
            return iter(self.items)
        def __getitem__(self, index):
            return self.items[index]
        def __setitem__(self, key, value):
            self.items[key] = value
        def __str__(self):
            return str(self.items)
        def __add__(self, other):
            self.items.append(other)
            return self
    cart = Cart()
    cart.additem(1)
    cart.additem(‘abc’)
    cart.additem(3)
    print(len(cart))
    print(bool(cart))
    for x in cart:
        print(x)
    print(3 in cart)
    print(2 in cart)
    print(cart[1])
    cart[1] = ‘xyz’
    print(cart)
    print(cart + 4 + 5 + 6)
    print(cart.__add__(17).__add__(18))

    3
    True
    1
    abc
    3
    True
    False
    abc
    [1, ‘xyz’, 3]
    [1, ‘xyz’, 3, 4, 5, 6]
    [1, ‘xyz’, 3, 4, 5, 6, 17, 18]
  10. 函数即对象,对象 foo 加上 (),就是调用对象的 __call__() 方法
            __call__():类中定义一个此方法,实例就可以像函数一样调用
  11. 缓存数据,便于检索的斐波那契数列
    class Fib:
        def __init__(self):
            self.items = [0, 1, 1]
        def __call__(self, index):
            return self[index]
        def __iter__(self):
            return iter(self.items)
        def __len__(self):
            return len(self.items)
        def __getitem__(self, index):
            if index <= 0:
                raise IndexError(‘Wrong Index’)
            if index < len(self.items):
                return self.items[index]
            for i in range(len(self), index+1):
                self.items.append(self.items[i-1] + self.items[i-2])
            return self.items[index]
        def __str__(self):
            return str(self.items)
        __repr__ = __str__
    f = Fib()
    print(f(10), len(f))
    print(f(5), len(f))
    for x in f:
        print(x)
    print(f[5], f[6])

二、上下文管理

  1. 当一个对象同时实现了 __enter__() 和 __exit__() 方法,它就属于上下文管理的对象
    __enter__:进入与此对象相关的上下文,如果存在该方法, with 语法会把该方法的返回值作为绑定到 as 子句中指定的变量上
    __exit__:退出与此对象相关的上下文
    实例调用顺序是:先调用 __init__,再调用 __enter__,再执行 with 内部的语句块,最后调用 __exit__
                               如果 with 内部语句块有错误,也会调用 __exit__,所以上下文管理是安全的
                               如果调用 sys.exit(),会退出当前解释器,但是还是会调用 __exit__
  2. __enter__ 方法没有其他参数
    __exit__(self, exc_type, exc_value, traceback)如果该上下文退出时没有异常,这三个参数都为 None;如果有异常,exc_type 代表异常类型,exc_value 代表异常的值,traceback 代表异常的追踪信息
    但是如果 __exit__ 方法返回一个等效 True 的值,则压制异常,否则,继续抛出异常
    class Point:
        def __init__(self):
            print(‘init’)
        def __enter__(self):
            print(‘enter’)
            return self
        def __exit__(self, exc_type, exc_val, exc_tb):
            print(‘type:’, exc_type)
            print(‘val:’, exc_val)
            print(‘tb:’, exc_tb)
            print(‘exit’)
            return “abc”
    p = Point()
    with p as f:
        raise Exception(‘New Error’)
        print(‘do sth’)
    print(‘outer’)

    init
    enter
    type: <class ‘Exception’>
    val: New Error
    tb: <traceback object at 0x000001A4F146BE08>
    exit
    outer
  3. 类既可以用在上下文管理,又可以用作装饰器
    import datetime
    import time
    from functools import wraps
    class TimeIt:
        def __init__(self, fn):
            self.fn = fn
            wraps(fn)(self)
        def __enter__(self):
            self.start = datetime.datetime.now()
            return self
        def __exit__(self, exc_type, exc_val, exc_tb):
            delta = (datetime.datetime.now() – self.start).total_seconds()
            print(self.fn.__name__, delta)
        def __call__(self, *args, **kwargs):
            self.start = datetime.datetime.now()
            ret = self.fn(*args, **kwargs)
            self.delta = (datetime.datetime.now() – self.start).total_seconds()
            print(self.fn.__name__, self.delta)
            return ret
    @TimeIt
    def add(x, y):
        “””This is function.”””
        time.sleep(2)
        return x + y
    print(add(10, 5))
    print(add.__doc__)
    print(‘====================’)
    with TimeIt(add) as timeitobj:
        print(timeitobj(11, 122))
    print(TimeIt(add).__doc__)

    add 2.000007
    15
    This is function.
    ====================
    add 2.000533
    133
    add 2.000533
    This is function.
  4. 上下文应用场景
            增强功能:在代码执行的前后增加代码,以增强其功能,类似装饰器的功能
            资源管理:打开了资源需要关闭,例如 文件对象、网络连接、数据库连接等
            权限验证:在执行代码之前,做权限的验证,在 __enter__ 中处理
  5. contextlib.contextmanager:它是一个装饰器实现上下文管理,装饰一个函数,而不用像类一样实现,__enter__ 和 __exit__ 方法
    要求:必须有 yield,就算增加一个异常,也能保证 exit 的执行
              yield 之前的当做 __enter__ 方法执行
              yield 之后的当做 __exit__ 方法执行
              yield 的值作为 __enter__ 的返回值
    import contextlib
    import datetime
    import time
    @contextlib.contextmanager
    def add(x, y):
        start = datetime.datetime.now()
        try:
            yield x + y
        finally:
            delta = (datetime.datetime.now() – start).total_seconds()
            print(delta)
    with add(4, 5) as f:
        # raise Exception(‘Error’)
        time.sleep(2)
        print(f)

三、反射

  1. 运行时,区别于编译时,指的是程序被加载到内存中执行的时候
         反射,reflection,指的是运行时获取类型定义信息
         在 Python 中,能够通过一个对象,找出其 type、class、attribute 或者 method 的能力,称为反射或自省
         具有反射能力的函数有:type()、isinstance()、callable()、dir()、getattr()
  2. getattr(object, name[, default]):通过 name 返回 object 的属性值,当属性不存在,将使用 default 返回,如果没有 default,则抛出 AttribteError。 name 必须为字符串
    setattr(object, name, value):object 的属性存在,则覆盖,不存在,则新增
    hasattr(object, name):判断对象是否有这个名字的属性,name 必须为字符串
    这种动态增删属性的方式是运行时改变类或者实例的方式,但是装饰器或 Mixin 都是定义时就决定了,因此反射能力具有更大的灵活性
  3. 通过名称找对象的方法,命令分发器的代码实现
    class Dispatcher:
        def __init__(self):
            self._run()
        def cmd1(self):
            print(“I’m cmd1”)
        def cmd2(self):
            print(“I’m cmd2”)
        def _run(self):
            while 1:
                cmd = input(‘Please input a command:’).strip()
                if cmd == ‘quit’:
                    break
                getattr(self, cmd, lambda : print(‘Unknow Command {}’.format(cmd)))()
    Dispatcher()
  4. __getattr__:一个类的属性会按照继承关系找,如果找不到,就会执行 __getattr__  方法,如果没有此方法,则会抛出 AttributeError 异常找不到属性
    属性查找顺序为:instance.__dict__  –> instance.__class__.__dict__  –>  继承的祖先类(直到 object)的 __dict__  –> 找不到  –>  调用 __getattr__()
    __setattr__:实例通过 点 设置属性,如同 self.x = x,就会调用 __setattr__(),属性要加到 __dict__ 中,就需要自己完成
    此方法可以拦截对实例属性的增加、修改操作,如果要设置生效,需要自己操作实例的 __dict__
        def __setattr__(self, key, value):
            print(key, value)
            self.__dict__[key] = value
    __delattr__:可以阻止通过实例删除属性的操作,但是通过类依然可以删除属性
    __getattribute__:实例的所有属性访问,第一个都会调用此方法,它阻止了属性的查找,该方法应该返回(计算后的)值或者抛出 AttributeError 异常
    它的 return 值将作为属性查找的结果,如果抛出 AttributeError 异常,则会直接调用 __getattr__ 方法,因为表示属性没有找到
    为了避免在该方法中无限的递归,它的实现应该永远调用基类的同名方法,以访问需要的任何属性,例如 object.__getattribute__(slef, name)
  5. 总结
    __getattr__():当通过搜索实例、实例的类及祖先类查不到属性,就会调用此方法
    __setattr__():通过 点 访问实例属性,进行增加、修改都要调用它
    __delattr__():当通过实例来删除属性时调用此方法
    __getattribute__:实例所有的属性调用都从这个方法开始

本文来自投稿,不代表Linux运维部落立场,如若转载,请注明出处:http://www.178linux.com/98377

发表评论

登录后才能评论

This site uses Akismet to reduce spam. Learn how your comment data is processed.

联系我们

400-080-6560

在线咨询:点击这里给我发消息

邮件:1823388528@qq.com

工作时间:周一至周五,9:30-18:30,节假日同时也值班