面向对象之魔术方法

特殊属性

属性

含义

__name__

类、函数、方法的名称

__module__

类定义所在的模块名

__class__

对象或类所属的类

__bases__

类的基类的元组,顺序为它们在基类列表中出现的顺序

__doc__

类、函数的文档字符串,若没定义则为None

__mro__

类的mro,方法查找顺序

__dict__

类或实例的属性,可写的字典

查看属性

__dir__:返回类或者对象的所有成员名称列表,dir()即调用__dir__(),若提供__dir__(),则返回属性的列表,否则会尽量从__dict__中收集多的信息

若dir([obj])参数obj包含__dir__()方法,该方法被调用。若参数obj不包含__dir__()方法,则将会收集尽量多的参数信息

对不同类型具有不同行为

若对象为模块对象,返回的列表包含模块的属性名

若对象为类型或类对象,返回的列表包含类的属性名、基类的属性名

否则,返回列表包含对象的属性名、类的属性名、类的基类的属性名

 
 

魔术方法

分类

创建、初始化、销毁

__init__、__del__

hash

bool

可视化

运算符重载

容器和大小

可调用对象

上下文管理

反射

描述器

其他

hash

hash的特点

固定大小

雪崩效应

单向不可逆

__hash__:内建函数hash()调用的返回值,返回一个整数,若定义该方法则该类的实例就可hash

__eq__:对应==,判断两个对象是否相等,返回bool值

__hash__仅返回一个hash值作为set或dict的key,但去重需要__eq__来判断2个对象是否相等

hash值相等只是hash冲突,不能说明两个对象相等

不可hash对象isinstan(obj,collections.Hashable) = False

__hash__ = None:设置类不能被hash

 
 

bool

__bool__:内建函数bool(),或者对象放在逻辑表达式的位置,调用该函数返回bool值,没定义__bool__()就找__len__()返回长度,长度非0为真,若__len__()也没有定义,则所有实例均返回真

 
 

可视化

__repr__:内建函数repr()对一个对象获取字符串表达。调用__repr__方法返回字符串表达,若__repr__没有定义,就直接返回object的内存地址信息

__str__:str()函数、内建函数format()、print()调用,需要返回对象的字符串表达。若未定义则调用__repr__返回字符串表达,__repr__未定义返回object的内存地址信息

__bytes__:bytes()函数调用,返回一个对象的bytes表达

 
 

运算符重载

<<===>>=!=

__lt____le____eq____gt____ge____ne__

+,-,*,/,%,//,**,divmod

__add____sub____mul____trudiv____mod____floordiv____pow____divmod__

+=,-=,*=,/=,%=,//=,**=

__iadd____isub____imul____itrudiv____imod____ifloordiv____ipow__

@functools.total_ordering装饰器

__lt____le____eq____gt____ge__是比较大小必须实现的方法,但是全写太麻烦,使用该装饰器可简化代码

要求__eq__必须实现,剩余其他方法实现其一即可

缺点:引入了不需要的方法,影响性能

自己实现只需保证__eq__,__lt____gt____le____ge__五选三实现即可

容器

__len__:内建函数len(),返回对象的长度,若将对象当做容器类看,bool()函数调用时,若没有__bool__()方法,则看__len__()是否存在,存在返回非0

__iter__:迭代容器时调用,返回一个新的迭代器对象

__contains__:in成员运算符,若未实现就调用__iter__方法遍历

__getitem__:实现self[key]访问。序列对象,key接受整数为索引或切片,对于set或dict,key为hashable,可以不存在KeyError异常

__setitem__:设置值

__missing__:字典或其子类使用__getitem__()调用时,key不存在执行该方法

可调用对象

__call__:类中定义一个该方法,实例就可像函数一样调用

 
 

上下文管理

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

__enter__:进入与此对象相关的上下文,若存在该方法,with语法会将该方法的返回值绑定到as字句中指定的变量上

__exit__():退出与此对象相关的上下文

实例化对象时不会调用__enter__,进入with语句块调用__enter__方法 –》执行语句体 –》离开with语句块调用__exit__()方法

安全性

存在异常,enter和exit照样执行,上下文管理是安全的

方法的参数

__enter__方法没有参数

__exit__方法有3个参数,与异常相关,没有异常则3个参数均为None

exc_type,异常类型

exc_value,异常的值

traceback,异常的追踪信息

__exit__方法返回一个等效True的值,可以压制异常,否则继续抛出异常

应用场景

增强功能

在代码执行的前后增加代码,以增强其功能,类似装饰器

资源管理

打开文件需要关闭

权限验证

在执行代码前做权限的验证

contextlib.contextmanager

装饰器,实现上下文管理,装饰一个函数,而不用像类一样实现__enter__()和__exit__()方法

对下面的函数有要求,必须有yield,必须返回一个生成器,且只有一个yield值

该装饰器接收一个生成器对象作为参数

 
 

若业务逻辑简单可使用contextlib.contextmanager,若复杂,用类增加__enter__()和__exit__()方法更方便

反射

概述

运行时,区别于编译时,指的是程序被加载到内存中执行的时候

反射,reflection,指的是运行时获取类型定义信息

在Python中,能通过一个对象,找出其type、class、attribute或method的能力,称为反射或自省

具有反射能力的函数:type()、isinstance()、callable()、dir()、getattr()

相关函数及方法


通过属性字典__dict__可以访问对象的属性,本质上就是利用反射的能力

内建函数

意义

getattr(object, name[, default]) -> value

通过name返回object的属性值,当属性不存在返回default,若无default则抛出AttributeError,name必须为字符串

setattr(obj, name, value)

object的属性存在则覆盖,不存在则新增

hasattr(obj, name)

判断对象是否有该属性,name必须为字符串

 
 


这种动态增删属性的方式是运行时改变类或实例的方式,但装饰器或Mixin在定义时就写定了,反射具有更大的灵活性

反射相关魔术方法

__getattr__():一个类的属性会按照继承关系查找属性,若找不到,就会执行__getattr__()方法,如果没有该方法,则抛出AttributeError


属性查找顺序:instance.__dict__ ——》 instance.__class__.__dict__ ——》继承的祖先类(直到object)的__dict__ ——》找不到 ——》调用__getattr__()

__setattr__():实例通过.点设置属性,如self.x = x就会调用__setattr__()。若属性要加到实例自己的__dict__中,就需要自己完成


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


__delattr__():可以阻止通过实例删除属性的操作,但通过类依然可以删除属性


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

该方法的return值将作为属性查找的结果。如果抛出AttributeError,则会直接调用__getattr__方法,表示属性没有找到

__getattribute__方法中为了避免该方法出现无限递归,其实现应该永远调用基类的同名方法以访问需要的任何属性


魔术方法

意义

__getattr__()

当通过搜索实例、实例的类及祖先类查不到属性时,就会调用该方法

__setattr__()

通过.访问实例属性,进行增加、修改

__delattr__()

当通过实例来删除属性时调用此方法

__getattribute__()

实例的所有属性调用都从此方法开始

属性查找顺序:实例调用__getattribute__() ——》instance.__dict__——》instance.__class__.__dict__ ——》继承的祖先类(直到object)的__dict__ ——》调用__getattr__()

描述器

class A含有__get__、__set__、__delete__方法中的任意一个,class B的类属性x为class A的实例,对class B或B的实例的x属性的读取,成为对class A的实例的访问,就会调用class A中的方法,只有类属性是class A的实例才会调用class A中的方法

描述器的表现

3个魔术方法:__get__()、__set__()、__delete__()

方法签名

object.__get__(self,instance,owner)

object.__set__(self,instance,value)

object.__delete__(self,instance)

其中,self指代当前实例,调用者;instance是owner的实例;owner是属性所属的类

描述器定义

一个类实现了__get__、__set__、__delete__三个方法中的任何一个,就是描述器

若仅实现__get__,即为非数据描述器non-data descriptor

同时实现__get__、__set__就是数据描述器data descriptor

若一个类的类属性设置为描述器,则它被称为owner属主

属性的访问顺序

数据描述器 ——》实例的__dict__ ——》非数据描述器

本质:若类属性指代的是数据描述器,则实例中和类属性同名的属性将从__dict__中被删除,造成数据描述器优先访问的假象

  

原创文章,作者:ZBD20,如若转载,请注明出处:http://www.178linux.com/99067

发表评论

登录后才能评论

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

联系我们

400-080-6560

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

邮件:1823388528@qq.com

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