魔术方法
使用Python的魔术方法的最大优势在于他们提供了一种简单的方法来让对象可以表现的像内置类型一样。那意味着你可以避免丑陋的,违反直觉的,不标准的的操作方法。
特殊属性
属性
|
含义
|
__name__
|
类、函数、方法等名字
|
__module__
|
类定义所在的模块名?
|
__class__
|
对象或类所属的类
|
__bases__
|
类的基类的元组,顺序为它们在基类列表中出现的顺序
|
__doc__
|
类、函数的文档字符串,如果没有定义则为None
|
__mro__
|
类的mro,class.mro()返回的结果保存在__mro__中
|
__dict__
|
类或实例的属性,可写的字典
|
查看属性
方法
|
意义
|
__dir__
|
返回类或者对象的所有成员名称列表.dir()函数就是调用__dir__().如果提供了__dir__(),则返回属性的列表,否则就会尽量从__dict__属性中收集信息
|
- 如果dir([obj])参数包含方法__dir__(),该方法将被调用。如果参数不包含__dir__(),该方法将最大限度地收集参数信息。
- dir()对于不同类型的对象具有不同的行为
- 如果对象是模块对象,列表包含模块的属性名
- 如果对象是类型或者类对象,列表包含类的属性名,及它的积累的属性名
- 否则,列表包含对象的属性名,它的类的属性名和类的基类的属性名
魔术方法
- 分类:
- 创建与销毁
- __init__与__del__
- hash
- bool
- 可视化
- 运算符重载
- 容器和大小
- 可调用对象
- 上下文管理
- 反射
- 描述器
- 其他杂项
- 创建与销毁
方法
|
意义
|
__hash__
|
内建函数hash()调用的返回值,返回一个整数。如果定义这个方法该类的实例就可hash
|
- hash值相同不代表对象相同,比如hash算法为%3取模,此时3,6,9的hash值相同但是对象并不相同。
set去重会先判断is是否相同,如果不同再判断==是否相同,如都不同则会放到集合中
方法
|
意义
|
__eq__
|
对应==操作符,判断2个对象是否相等返回bool值
|
- __hash__方法只是返回一个hash值作为set的key,但是去重,还需要__eq__来判断对象是否相等。
- hash值相等,只是hash冲突,不能说明两个对象是相等的
- 因此,一般来说hash方法是为了作为set或者dict的key,所以去重同时需要eq方法
- 可hash对象昂必须提供__hash__方法,没有提供的话,instance(p1,collections.Hashable)一定为False。去重要提供__eq__方法。
- 练习:
- 设计二维坐标Point,比较2个坐标是否相等?
- 思考:为什么list不可以hash?
bool
- 四大皆空<list,tuple,set,dict>,__len__()为0所以必定为False
方法
|
意义
|
__bool__
|
内建函数bool(),或者对象放在逻辑表达式的位置,调用这个函数返回布尔值。没有定义__bool__(),就找__len__()返回长度,非0为真。如果__len__()也没有定义,那么所有实例都返回真
|
可视化
方法
|
意义
|
__repr__
|
内建函数repr()对一个对象获取字符串表达。如果一个类定义了__repr__()但没有定义__str__,那么在请求该类的‘非正式’的字符串表示时也将调用__repr__()
|
__str__
|
str()函数,内建函数format、print()函数调用,需要返回对象的字符串表达
|
__bytes__
|
bytes的时候,返回一个对象的bytes表达,即返回bytes对象
|
运算符重载
- operator模块提供一下的特殊方法,可以将类的实例使用下面的操作符来操作
运算符
|
特殊方法
|
含义
|
<,<=,==,>,>=,!=
|
__lt__,__le__,__eq__,__gt__,__ge__,__ne__
|
比较运算符
|
+,-,*, /,%,//,**,divmod
|
__add__,__sub__,__mul__,__truediv__,__mod__,__floordiv__,__pow__,__divmod__
|
算数运算符,位运算符也有对应的方法
|
+=,-=,*=,/=,%=,//=,**=
|
__iadd__,__isub__,__imul__,__itruediv__,__imod__,__floordiv__,__ipow__
|
- 练习:完成Point类设计,实现判断点相等的方法,并完成向量的加法
- 往往是用面向对象实现的类,需要做大量的运算,而运算符是这种在数学上最常见的表达方式。例如,上例中的对+进行了运算符重载,实现了Point类的二元操作,重新定义为Point+Point
- 提供运算符重载,比直接提供加法方法要更加适合该领域内使用者的习惯
- int类,几乎实现了所有操作符
方法
|
意义
|
__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不存在执行该方法
|
- python中一切皆对象,函数也不例外
- 函数即对象,对象foo加上(),就是调用对象的__call__()方法
- 可调用对象
方法
|
意义
|
__call__
|
类中的第一个该方法,实例就可以像函数一样调用
|
- 可调用对象:定义一个类,并实例化得到其实例,将实例像函数一样调用。
- 练习:定义一个斐波那契数列的类,方便调用,计算第n项
上下文管理
- 文件IO操作可以对文件对象使用上下文管理,使用with…as语法
- 上下文管理对象
- 当一个对象同时实现了__enter__和__exit__()方法,它就属于上下文管理的对象
方法
|
意义
|
__enter__
|
进入与此对象相关的上下文,如果存在该方法,with语法会把该方法的返回值作为绑定到as子句中指定的变量上
|
__exit__
|
退出与此对象相关的上下文
|
- 实例化对象的时候,并不会调用enter,进入with语句块调用__enter__方法,然后执行语句体,最后离开with语句块的时候,调用__exit__方法
- with可以开启一个上下文运行环境,在执行前做一些准备,执行后做一些收尾工作
- 看看异常对上下文的影响,通过下例可以看出在enter和exit照样执行,上下文管理是安全的
- 极端例子:调用sys.exit(),它会退出当前解释器
- 打开Python解释器,在里面敲sys.exit(),窗口直接关闭了,也就是说碰到这一句,Python运行环境直接退出了
- 从执行结果来看,依然执行了__exit__函数,哪怕是退出Python运行环境
- 由此说明上下文管理很安全
- 不相等的原因在于__enter__方法上,它将自己的返回值赋给了f,继续修改测试看下例
- __enter__方法返回值就是上下文使用的对象,with语法会把它的返回值赋给as子句的变量
- __enter__方法和__exit__方法的参数
- __enter__方法没有其他参数
- __exit__方法有三个参数(self.exctype,excvalue,traceback)
- 这三个参数都与异常有关
- 如果该上下文退出时没有异常,这三个参数都是None
- 如果有异常,参数意义如下:
- exc_type:异常类型
- exc_value:异常的值
- traceback,异常的追踪信息
- __exit方法返回一个等效True的值,则压制异常;否则继续抛出异常
- 练习:为加法函数计时
- 1.使用装饰器显示该函数的执行时长
- 2.使用上下文管理显示该函数的执行时长
- 它是一个装饰器实现上下文管理,装饰一个函数,而不用像类一样实现__enter__和__exit__方法
- 对下面的函数有要求,必须有yield,也就是说这个函数必须返回一个生成器,且只有yield一个值
- f接收yield语句的返回值
- 下面增加一个异常,发现不能保证exit执行,解决办法:增加try finally。
- 这样做得意义是,当yield发生处为生成器函数增加了上下文管理
总结:如果业务逻辑简单可以使用函数加装饰器方式,如果业务复杂,用类的方式加__enter__和__exit__
反射
- 概述:
- 运行时,区别于编译时,指的是程序被加载到内容中执行的时候
- 反射,reflection,指的是运行时获取类型定义信息
- 一个对象能够在运行时,像照镜子一样,反射出其类型信息
- 简单说,在python中,能够通过一个对象,找出其type、class、attribute或method的能力,成为反射或者自省
- 反射相关函数和方法
- 需求:有一个Point类,查看它实例的属性,并修改它,动态为实例加属性
- 上例通过属性字典__dict__来访问对象的属性,本质上也是利用的反射能力
- python提供了内置的函数
内置函数
|
意义
|
getattr(object,name[,default])
|
通过name返回object的属性值,当属性不存在,将使用defalut返回,如果没有default,则抛出AttributeError。name必须为字符串
|
setattr(object,name,value)
|
object的属性存在,则覆盖,不存在,新增
|
hasattr(object,name)
|
判断对象是否有这个名字的属性,name必须为字符串
|
- 思考:这种动态增加属性的方式和装饰器修饰一个类、Mixin方式的差异?
- 这种动态增删属性的方式是运行时改变类或者实例的方式,但是装饰器或者Mixin都是定义时就决定了,因此反射能力具有更大的灵活性
- 练习
- 1.命令分发器,通过名称找对应的函数执行。思路:名称找对象的方法
- 方法二中,使用getattr方法找到对象的属性的方式,比自己维护一个字典来建立名称和函数之间的关系好得多
反射相关的魔术方法
- __getattr__()、__setattr__()、__delattr__(),三个魔术方法
- __getattr__()测试:
一个类的属性会按照继承关系找,如果找不到,就会执行getattr方法,如果没有这个方法,就会抛出AttributeError异常表示找不到属性查找属性顺序为:instance.dict—>instance.class.dict—>继承的祖先类(直到object)的dict—>调用getattr()
- __setattr__()举例:
实例通过点设置属性,如同self.x = x,就会调用__setattr__(),属性要加到实例的dict中,就需要自己完成setattr方法,可以拦截实例属性的添加、修改操作,如果要设置生效,需要自己操作实例的dict,此例子中需要再setattr函数中加入self.__dict__[key] = value
- __delattr__()举例:
可以阻止通过实例删除属性的操作。但是通过类依然可以删除属性。如同Z
- __getattribute__举例:
- 实例的所有的属性访问,第一个都会调用getattribute方法,它阻止了属性的查找,该方法应该返回(计算后的)值或者抛出一个AttributeError异常
- 他的return值将作为属性查找的结果,如果抛出AttributError异常,则会直接调用getattr方法,因为表示属性没有找到
- getattribute方法中为了避免在该方法中无限递归,他的实现应该永远调用基类的同名方法以访问需要的任何属性,例如object.getattribute(self,name).
- 注意:除非你明确的知道getattribute方法用来做什么,否则不要使用它
总结:
魔术方法
|
意义
|
__getattr__()
|
通过搜索实例、实例的类及祖先类查不到属性,就会调用此方法
|
__setattr__()
|
通过.访问实例属性,进行增加、修改都要调用它
|
__delattr__()
|
当通过实例来删除属性时调用此方法
|
__getattribute__()
|
实例所有的属性调用都从这个方法开始
|
- 属性查找顺序
- 实例调用__getattribute__()–>instance.__dict__–>instance.__class__.__dict__–>继承祖先类(直到object)的__dict__–>调用__getattr__()
本文来自投稿,不代表Linux运维部落立场,如若转载,请注明出处:http://www.178linux.com/88799