魔法方法及其使用
__开头和结束的方法,定义外部没有办法直接调用,但会有影响使用
运算符号的魔法方法, + ,-,*,/,%,//,**, __add__,__sub__,__mul__,__truediv__,__mod__,__floordiv__,__pow__,
__divmod__(?),
系统内部对于数值型,字符串型,容器内型都定义了其中部分或者全部的运算符使用要求和办法,
我们在类中定义这些魔法方法,可以使得它们按照需要对对象类型进行运算符运算, 仍需要满足一些基本的概念
比如加减乘除都是在两个对象而言的,所以形参数量要为二 ,且其中一个为self(类自身的对象)其他大部分的运算符魔法方法对于参数数量和类型也有类似要求,
其他变种运算符 +=,-=,*=,/=,%=,//=,**= ,它们的魔法方法写法就是相应的名称前加一个i,它们对于形参数和类型也与前面的类似
判断运算符 <,<=,==,>,>=,!=
和运算符号的一样,他么的返,定义一般要求两个参数,且其中一个为self(类自身的对象),它们不一定要求返回布尔型的值,可以没有或者返回其他类型的值
在使用这些运算符对系统数据类型使用这些符号时不会有覆盖,或者不能使用的问题,因为他们内部的类中有魔法方法定义使用这些符号的使用规则,即使我们在自定义的类中对基本数据类型时使用运算符,依然能够准确表达含义
容器相关方法:__len__,__iter__,__contains__,__getitem__,__setitem__,__missing__
获取长度,迭代器, in运算符,索引访问,索引设置,其它
容器相关方法,在基本数据类型中的容器类型,list,tuple,set,dict中有实现其中大部分或全部方法,
对他们调用 len( ),可以使用是因为实现了__len__的魔法方法,在类中可以定义它,使得我们也可以在外部调用该方法,这个方法要求只有一个形参,且为类的对象,要有返回值且为整型,使用 len(对象)会调用这个方法
当迭代一个对象(使用for in 遍历)时会调用__iter__的方法,获取该方法返回的迭代器,for in 遍历的实质是遍历这个迭代器,所以这个对象可以被成为可迭代对象,任何数据类型如果实现__iter_就是一个可迭代类型. 实现__iter__
方法要求传入一个参数,为self(类自身的对象)返回一个迭代器 (iter( )函数,yield from iterable都可以返回一个迭代器)
__contains__(self,item) 使用判断语句 in 的时候调用的方法 如果使用 ” item in 对象 ” 就会调用这个方法
如果没有定义该方法依然可以使用,in 语句 自是他会自动调用 对象的 __iter__方法获取它的迭代器 相当于对对迭代器使用 in 语句. __contains__ 方法要求有返回值且为布尔型,若不为布尔型根据返回值是否为空类型返回True 或 False
索引访问 对象[ ] ,调用__getitem__方法 __getitem__(self,item) item为索引
形参只能为两个(如果不定义两个调用时将会出现无法处理的错误),item为传入中括号中的值,但系统对这个值要求非常松,可以为任意类型
使用索引赋值(赋值即定义) 对象[ ] =值 ,调用__setitem__方法 __setitem__(self,key,value) key为中括号中的值,value为等号右边的值
其他 __missing__, 当调用了__getitem__()方法,而key不存在就会调用这个方法(只能被动的调用)
总结:魔法方法,在使用中不像普通方法一样调用方法名就能够使用,他是系统中设置好的,我们在类中能够对它重写,对于运算符,重写的要求很简单,传入两个参数对于函数内部实现没有要求. 对于函数体实现没有要求.
对于容器类型的相关方法,它的参数可能要求很低,但使用中可能会出错,很多对于返回值也有要求,要求有且返回
指定类型. 实际在一个容器中,对于函数的实现它的要求其实会更高,例如它要实现索引设置和获取,要求把设置的内容保存,获取时从内部获取.基本数据类型中除了容器类型,还有其他类型实现了其中的部分方法
对象的打印,正常使用print函数打印一个对象会获得<__main__.A object at 0x00000272662DF6A0>这样的结果
__repr__ , __str__ 两个方法可以作为对象的显示方法
在print 和 format 函数中如果要打印对象会优先调用__str__方法, 如果打印一个包装了对象的基本数据类型的时候,会先执行打印基本数据类型的方法,其中的对象只会调用对象的__repr__方法
可调用对象__call__ 如果一个对象可以像函数一样的访问,因为实现了这个方法,对参数,返回值没有要求
__enter__,__exit__,使用条件苛刻,上下文管理(with语句)时才会执行,这两个方法都是被动调用的
@functools.total_ordering装饰器
类中实现包含等于,包含大于小于之一的两个方法就会实现六个方法
反射相关魔术方法:
首先看看反射相关的内建函数 getattr(对象,name[,default]) 通过name(字符串类型) 获取对象中叫作name的属性,没有就返回default,没设置会报错
setattr(对象,name,value) 设置对象的属性和值,有就覆盖,没有就新增
hasattr(对象,name) 判断对象是否有这个名字的属性
__getattr__(self,item) 外部对对象使用 对象.name 访问时,访问字典不能得到,最后执行的方法
__setattr__(self,key,value) 需要通过 对象.key = value 来为对象添加属性的时候自动调用,并拦截添加或覆盖操作,需要在其中重写这个方法,或用属性字典手动添加
__delattr__ 使用del语句删除对象时调用,并拦截删除
__getattribute__ 获取对象属性时会优先执行,如果要能正常执行获得真实结果返回
return object.__getattribute__(self.item)
总结:魔术方法,如果定义只要满足调用的条件就会自动的触发,暂时没有出现可以自行设置触发条件的魔术方法.
方法中,系统实现的部分直接或间接指示了参数含义,如果不顺着系统意思,在实际调用中可能会出现意想不到的错误. 类中的方法在实现时如果满足类中定义的魔术方法触发条件,将会触发. 魔术方法除了有方便应用和表达含义的符号,还有的实现特殊功能的,比如上下文管理 ,__call__外部访问,这些方法极大地扩展了类的功能.
显示和反射有关的魔术方法都属于工程,工具类型的魔术方法
描述器和它的三个魔术方法
:__get__ (self,instance,owner), __set__(self,instance,value) , __delete__(self, instance)
__get__ 外部的类的属性(类变量)访问就会调用,这个方法, 对象如果通过类变量访问,也会调用,它的返回值为访问到的值
__set__ 外部的类中设置对象属性值会调用,并拦截对象属性的设置
一个类中如果有这几个方法之一它就是一个描述器
如果一个类中的类属性(类变量)引入了一个描述器对象, 修饰器就能够作用于这个类. 满足条件的情况就会调用描述器中的方法. 分为很多情况
1,只有__get__, 描述器被类访问,会调用描述方法,且类变量的值为它的返回值,如果它对象通过类变量访问到修饰器,也会执行相同的操作
2,只有__set__, 在类的内部或外部,给类的对象添加或修改与描述器变量名相同的属性, 会触发描述器方法,它的返回值没有意义
3,有__del__, 在外部使用del 作用对象访问的,与描述器变量名想同的属性时,会调用 ,如果是用类访问到的不会调用
对于一个描述器有多个这样的方法,如果满足各自的条件就会触发访问,其中只有__get__为 非数据描述器
有__get__和__set__成为为数据描述器. 描述器的使用大概分为三块:1,定义描述器2,生成被描述的类并加载描述器3,外部调用被描述类的属性触发描述方法,后两种它可以起到对特定属性的监视作用.
数据描述器功能比非数据描述器强大
应用实例定义一个静态方法装饰器
class StaticMtd:
def __init__(self,fn):
self.fn = fn
def __get__(self, instance, owner):
print(‘get’)
return self.fn
def __set__(self, instance, value):
print(self,instance,value)
print(‘set’)
class A:
@StaticMtd # add = StaticMtd(add) 相当与引入了一个描述器
def add(x,y):
print(x,y)
A.add(4,5) #使用类访问触发描述器的方法
a =A()
a.add = 3 #使用对象添加与描述器变量名相同属性,触发set方法
本文来自投稿,不代表Linux运维部落立场,如若转载,请注明出处:http://www.178linux.com/88806