一、魔术方法
-
__name__:类、函数、方法等的名字__module__:类定义所在的模块名__class__:对象或类所属的类__bases__:类的基类的元祖,顺序为它们在基类列表中出现的顺序__doc__:类、函数的文档字符串,如果没有定义则为 None__mro__:类的 mro,class.mro( ) 返回的结果保存在 __mro__ 中__dict__:类或实例的属性,可写的字典
-
查看属性__dir__:返回类或者对象的所有成员名称列表,dir() 函数就是调用 __dir__(),如果提供 __dir__(),则返回属性的列表(返回值要求必须是可迭代,且只对实例有影响),否则会尽量从 __dir__ 属性中收集信息dir() 对于不同类型的对象具有不同的行为:模块对象,返回的列表包含模块的属性名类型或类对象,返回的列表包含类的属性名,以及它的基类的属性名否则,返回列表包含对象的属性名、它的类的属性名和类的基类的属性名
-
__hash__:内建函数 hash() 调用的返回值,返回一个整数,如果定义这个方法,该类的实例就可 hash__eq__:对应 == 操作符,判断两个对象是否相等,返回 bool 值__hash__ 方法只是返回一个 hash 值作为 set 的 key,但是去重,还需要 __eq__ 来判断两个对象是否相当,如果 hash 值相等,只是 hash 冲突,不能说明两个对象是相等的。因此,一般来说,提供 __hash__ 方法是为了作为 set 或者 dict 的 key,所以去重要同时提供 __eq__ 方法列表类不可哈希的原因就是因为源码中有一句 __hash__ = None,也就是说如果调用 __hash__() 相当于 None(),一定报错
-
__bool__:内建函数 bool(),或者对象放在逻辑表达式的位置,调用这个函数返回布尔值。没有定义 __bool__(),就找 __len__() 返回长度,非 0 位就是真。如果 __len__() 也没有定义,那么所有实例都返回真
-
可视化__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 = nameself.age = agedef __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 jsonreturn 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,18b'{“name”: “tom”, “age”: 18}’[repr: tom,18]str: tom,18str:a, 11[‘a’] (‘1’,){‘1’, ‘a’} -
运算符重载<、<=、==、>、>=、!= 对应 __lt__、__le__、 __eq__ 、__gt__、 __ge__、__ne__+、-、*、/、%、//、**、divmod 对应 __add__、__sub__、__mul__、__truediv__、__mod__、__floordiv__、__pow__、__divmod__+=、-=、*=、/=、%=、//=、**= 对应 __iadd__、__isub__、__imul__、__itruediv__、__imod__、__ifloordiv__、__ipow__
-
__lt__、__le__、 __eq__ 、__gt__、 __ge__、__ne__ 是比较大小必须实现的方法,但是全部写完太麻烦,使用 @functools.total_ordering 装饰器就可以大大简化代码,但是要求 __eq__ 必须实现,其它方法实现其一即可from functools import total_ordering@total_orderingclass Person:def __init__(self, name, age):self.name = nameself.age = agedef __eq__(self, other):return self.age == other.agedef __gt__(self, other):return self.age > other.age
-
容器相关方法__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 不存在执行该方法
-
购物车代码实现: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] = valuedef __str__(self):return str(self.items)def __add__(self, other):self.items.append(other)return selfcart = 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))
3True1abc3TrueFalseabc[1, ‘xyz’, 3][1, ‘xyz’, 3, 4, 5, 6][1, ‘xyz’, 3, 4, 5, 6, 17, 18] -
函数即对象,对象 foo 加上 (),就是调用对象的 __call__() 方法__call__():类中定义一个此方法,实例就可以像函数一样调用
-
缓存数据,便于检索的斐波那契数列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])
二、上下文管理
-
当一个对象同时实现了 __enter__() 和 __exit__() 方法,它就属于上下文管理的对象__enter__:进入与此对象相关的上下文,如果存在该方法, with 语法会把该方法的返回值作为绑定到 as 子句中指定的变量上__exit__:退出与此对象相关的上下文实例调用顺序是:先调用 __init__,再调用 __enter__,再执行 with 内部的语句块,最后调用 __exit__如果 with 内部语句块有错误,也会调用 __exit__,所以上下文管理是安全的如果调用 sys.exit(),会退出当前解释器,但是还是会调用 __exit__
-
__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 selfdef __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’)
initentertype: <class ‘Exception’>val: New Errortb: <traceback object at 0x000001A4F146BE08>exitouter -
类既可以用在上下文管理,又可以用作装饰器import datetimeimport timefrom functools import wrapsclass TimeIt:def __init__(self, fn):self.fn = fnwraps(fn)(self)def __enter__(self):self.start = datetime.datetime.now()return selfdef __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@TimeItdef add(x, y):“””This is function.”””time.sleep(2)return x + yprint(add(10, 5))print(add.__doc__)print(‘====================’)with TimeIt(add) as timeitobj:print(timeitobj(11, 122))print(TimeIt(add).__doc__)
add 2.00000715This is function.====================add 2.000533133add 2.000533This is function. -
上下文应用场景增强功能:在代码执行的前后增加代码,以增强其功能,类似装饰器的功能资源管理:打开了资源需要关闭,例如 文件对象、网络连接、数据库连接等权限验证:在执行代码之前,做权限的验证,在 __enter__ 中处理
-
contextlib.contextmanager:它是一个装饰器实现上下文管理,装饰一个函数,而不用像类一样实现,__enter__ 和 __exit__ 方法要求:必须有 yield,就算增加一个异常,也能保证 exit 的执行yield 之前的当做 __enter__ 方法执行yield 之后的当做 __exit__ 方法执行yield 的值作为 __enter__ 的返回值import contextlibimport datetimeimport time@contextlib.contextmanagerdef add(x, y):start = datetime.datetime.now()try:yield x + yfinally:delta = (datetime.datetime.now() – start).total_seconds()print(delta)with add(4, 5) as f:# raise Exception(‘Error’)time.sleep(2)print(f)
三、反射
-
运行时,区别于编译时,指的是程序被加载到内存中执行的时候反射,reflection,指的是运行时获取类型定义信息在 Python 中,能够通过一个对象,找出其 type、class、attribute 或者 method 的能力,称为反射或自省具有反射能力的函数有:type()、isinstance()、callable()、dir()、getattr()
-
getattr(object, name[, default]):通过 name 返回 object 的属性值,当属性不存在,将使用 default 返回,如果没有 default,则抛出 AttribteError。 name 必须为字符串setattr(object, name, value):object 的属性存在,则覆盖,不存在,则新增hasattr(object, name):判断对象是否有这个名字的属性,name 必须为字符串这种动态增删属性的方式是运行时改变类或者实例的方式,但是装饰器或 Mixin 都是定义时就决定了,因此反射能力具有更大的灵活性
-
通过名称找对象的方法,命令分发器的代码实现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’:breakgetattr(self, cmd, lambda : print(‘Unknow Command {}’.format(cmd)))()Dispatcher()
-
__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)
-
总结__getattr__():当通过搜索实例、实例的类及祖先类查不到属性,就会调用此方法__setattr__():通过 点 访问实例属性,进行增加、修改都要调用它__delattr__():当通过实例来删除属性时调用此方法__getattribute__:实例所有的属性调用都从这个方法开始
本文来自投稿,不代表Linux运维部落立场,如若转载,请注明出处:http://www.178linux.com/98377