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运维部落立场,如若转载,请注明出处:/98377

发表评论

登录后才能评论

联系我们

400-080-6560

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

邮件:1823388528@qq.com

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

友情链接:万达娱乐平台  万达娱乐直属  万达登录  万达娱乐主管  万达娱乐  万达娱乐主管QQ  万达主管QQ  guoqibee.com  万达娱乐主管QQ