博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
python内置魔法方法
阅读量:4702 次
发布时间:2019-06-10

本文共 9595 字,大约阅读时间需要 31 分钟。

python

一.类中的魔法方法

1.构造和初始化方法
1.__init__,类实例化时,对象特有属性添加时,会调用此方法,完成初始化__init__和__new__共同组成了构造函数2.__new__是用来创建类,并返回该类的实例,__init__只是将传入的参数来初始化该实例3.__new__在创建一个实例的过程中必定会被调用,但__init__就不一定呢,比如通过pickle.load的方式反序列化一个实例是就不会调用__init__4.__new__方法总是需要返回该类的一个实例 ,而__init__不能返回除了None以外的任何值5.如果想控制一个类创建,通常可以通过__new__来进行控制6.__del__,在对象的生命周期结束时,__del__会被调用,可以将其理解为析构函数7.x.__del__()并不是对于del x的实现,但是往往del x的时候会调用__del__()8.foo.__del__()对象本身任然存在,但是调用了del foo,就再也没有foo这个对象了
实例:class Foo(object):def __init__(self):print 'foo __init__'return None # 必须返回None,否则抛TypeErrordef __del__(self):print 'foo __del__'
2.属性访问控制
1.__getattr__(self,name)该方法定义了视图访问一个不存在属性时的行为,因此,重写此方法可以实现捕获错误拼写,然后进行重定向,或者提出警告。2.__setattr__(self,name,value)__setattr__ 是实现封装的解决方案,它定义了你对属性进行赋值和修改操作时的行为。不管对象的某个属性是否存在,它都允许你为该属性进行赋值,因此你可以为属性的值进行自定义操作。有一点需要注意,实现__setattr__时要避免"无限递归"的错误,下面的代码示例中会提到。3.__delattr__(self,name)__delattr__与__setattr__很像,只是它定义的是你删除属性时的行为。实现__delattr__是同时要避免"无限递归"的错误。4.__getattribute__(self,name)__getattribute__定义了你的属性被访问时的行为,相比较,__getattr__只有该属性不存在时才会起作用。因此,在支持__getattribute__的Python版本,调用__getattr__前必定会调用 __getattribute__。__getattribute__同样要避免"无限递归"的错误。需要提醒的是,最好不要尝试去实现__getattribute__,因为很少见到这种做法,而且很容易出bug。
实例:class Access(object):def __getattr__(self, name):print '__getattr__'return super(Access, self).__getattr__(name)def __setattr__(self, name, value):print '__setattr__'return super(Access, self).__setattr__(name, value)def __delattr__(self, name):print '__delattr__'return super(Access, self).__delattr__(name)def __getattribute__(self, name):print '__getattribute__'return super(Access, self).__getattribute__(name)access = Access()access.attr1 = True # __setattr__调用access.attr1 # 属性存在,只有__getattribute__调用try:access.attr2 # 属性不存在, 先调用__getattribute__, 后调用__getattr__except AttributeError:passdel access.attr1 # __delattr__调用
3.描述器对象
1.__get__(self,instance,owner)参数instance是拥有者类的实例。参数owner是拥有者类本身。__get__在其拥有者对其读值的时候调用。2.__set__(self,instance,value)__set__在其拥有者对其进行修改值的时候调用。3.__delete__(self,instance)__delete__在其拥有者对其进行删除的时候调用。
注意:一个类要成为描述器对象至少实现上面三种方法中的一种
实例:我们知道,距离既可以用单位"米"表示,也可以用单位"英尺"表示。现在我们定义一个类来表示距离,它有两个属性: 米和英尺。class Meter(object):'''Descriptor for a meter.'''def __init__(self, value=0.0):self.value = float(value)def __get__(self, instance, owner):return self.valuedef __set__(self, instance, value):self.value = float(value)class Foot(object):'''Descriptor for a foot.'''def __get__(self, instance, owner):return instance.meter * 3.2808def __set__(self, instance, value):instance.meter = float(value) / 3.2808class Distance(object):meter = Meter()foot = Foot()d = Distance()print d.meter, d.foot # 0.0, 0.0d.meter = 1print d.meter, d.foot # 1.0 3.2808d.meter = 2print d.meter, d.foot # 2.0 6.5616在上面例子中,在还没有对Distance的实例赋值前, 我们认为meter和foot应该是各自类的实例对象, 但是输出却是数值。这是因为__get__发挥了作用.我们只是修改了meter,并且将其赋值成为int,但foot也修改了。这是__set__发挥了作用.描述器对象(Meter、Foot)不能独立存在, 它需要被另一个所有者类(Distance)所持有。描述器对象可以访问到其拥有者实例的属性,比如例子中Foot的instance.meter。在面向对象编程时,如果一个类的属性有相互依赖的关系时,使用描述器来编写代码可以很巧妙的组织逻辑。在Django的ORM中, models.Model中的IntegerField等, 就是通过描述器来实现功能的。
4.构造自定义的容器
1.在python中,常见的容器有:dict,tuple,list,string,tuple,set2.可变容器和不可变容器的区别在于,不可变容器一旦赋值后,不可对其中的某个元素进行更改。3.我们要自定义一些数据结构,使之能够和上面容器类型一样,那就需要去实现某些协议。4.这些协议和其他语言中的接口很像,一眼搞得需要你去实现才行,只不过没那么正式而已。5.如果要自定义不可变容器,只需要定义__len__和__getitem__方法。6.如果自定义可变容器,还需要定义在不可变容器类型基础上增加定义__setitem__和__delitem__。7.如果希望自定义数据结构还支持迭代。那就还需要定义__iter__。
1.__len__(self)需要返回数字类型,以表示容器的长度,该方法在可变容器和不可变容器中必须实现。2.__getitem__(self,key)当执行self[key]对的时候,调用的就是该方法,该方法在可变容器和不可变容器也必须实现。调用的时候,如果key的类型错误,该方法会抛出TypeError。如果没法返回key对应数值时,该方法会抛出ValueError。3.__setitem__(self,key,value)当执行self[key]的时候,调用的就是该方法。4.__delitem__(self,key)当你执行del self[key]的时候,调用的就是此方法。5.__iter__(self)该方法需要返回一个迭代器(iterator),当你执行for i in contaniner: 或者使用iter(container),该方法被调用。6.__reversed__(self)如果想要改数据结构被内建函数reversed()支持,就还要实现此方法。7.__contains__(self,item)如果定义了该方法,那么在执行item in container 或者 item not in container时该方法会被调用。如果没有定义,那么python会迭代容器中的元素一个一个比较,从而决定返回True或者False。8.__missing__(self,key)dict字典类型会有该方法,它定义了key如果在容器中找不到时触发的行为。比如:d={'a':1} , 当执行d[notexist]时,d.__misssing__(notexist)就会被调用。
实例:class FunctionalList:''' 实现了内置类型list的功能,并丰富了一些其他方法: head, tail, init, last, drop, take'''def __init__(self, values=None):if values is None:self.values = []else:self.values = valuesdef __len__(self):return len(self.values)def __getitem__(self, key):return self.values[key]def __setitem__(self, key, value):self.values[key] = valuedef __delitem__(self, key):del self.values[key]def __iter__(self):return iter(self.values)def __reversed__(self):return FunctionalList(reversed(self.values))def append(self, value):self.values.append(value)def head(self):# 获取第一个元素return self.values[0]def tail(self):# 获取第一个元素之后的所有元素return self.values[1:]def init(self):# 获取最后一个元素之前的所有元素return self.values[:-1]def last(self):# 获取最后一个元素return self.values[-1]def drop(self, n):# 获取所有元素,除了前N个return self.values[n:]def take(self, n):# 获取前N个元素return self.values[:n]
5.上下文管理
1.在with声明的代码中,我们可以做一些对象的开始操作和清除操作,还能对异常进行处理。
1.__enter__(self)__enter__会返回一个值,并赋值给as关键词之后的变量。在这里,你可以定义代码段开始的一些操作。2.__exit__(self, exception_type, exception_value, traceback)__exit__定义了代码段结束后的一些操作,可以这里执行一些清除操作,或者做一些代码段结束后需要立即执行的命令,比如文件的关闭,socket断开等。如果代码段成功结束,那么exception_type, exception_value, traceback 三个参数传进来时都将为None。如果代码段抛出异常,那么传进来的三个参数将分别为: 异常的类型,异常的值,异常的追踪栈。如果__exit__返回True, 那么with声明下的代码段的一切异常将会被屏蔽。如果__exit__返回None, 那么如果有异常,异常将正常抛出,这时候with的作用将不会显现出来。
实例:这该示例中,IndexError始终会被隐藏,而TypeError始终会抛出。class DemoManager(object):def __enter__(self):passdef __exit__(self, ex_type, ex_value, ex_tb):if ex_type is IndexError:print ex_value.__class__return Trueif ex_type is TypeError:print ex_value.__class__return # return Nonewith DemoManager() as nothing:data = [1, 2, 3]data[4] # raise IndexError, 该异常被__exit__处理了with DemoManager() as nothing:data = [1, 2, 3]data['a'] # raise TypeError, 该异常没有被__exit__处理'''输出:
Traceback (most recent call last):...'''
6.一元运算符和函数
1.__pos__(self)实现了'+'号的一元运算符(比如+some_object)2.__neg__(self)实现了'-'号的一元运算符(比如-some_object)3.__invert__(self)实现了`号一元运算符(比如`some_object)4.__abs__(self)实现了abs()内建函数5.__round__(self,n)实现了round()内建函数,参数n表示四舍五入的精度6.__floor__(self)实现了math.floor(),向下取整7.__ceil__(self)实现了math.ceil(),向上取整8.__trunc__(self)实现了math.trunc(),向0取整
7.算数运算符
1.__add__(self,other)实现了加号运算2.__sub__(self,other)实现了减号运算3.__mul__(self,other)实现了乘法运算4.__floordiv__(self,other)实现了//运算符5.__truediv__(self,other)实现了true division,只有声明了from __future__ import division该方法才会生效。6.__mod__(self,other)实现了%运算符,取余运算。7.__divmod__(self,other)实现了divmod()内建函数8.__pow__(self,other)实现了**操作,N次方操作。9.__and__(self,other)实现了位操作&10.__or__(self,other)实现了位操作|11.__xor__(self,other)实现了位操作^
8.反算数运算符
1.__radd__(self,other)2.__rsub__(self, other)3.__rmul__(self, other)4.__rfloordiv__(self, other)5.__rtruediv__(self, other)6.__rmod__(self, other)7.__rdivmod__(self, other)8.__rpow__(self, other)9.__rand__(self, other)10.__ror__(self, other)11.__rxor__(self, other)
注意:这里只需要解释一下概念即可。假设针对some_object这个对象:some_object + other上面的代码非常正常地实现了some_object的__add__方法。那么如果遇到相反的情况呢?other + some_object这时候,如果other没有定义__add__方法,但是some_object定义了__radd__, 那么上面的代码照样可以运行。这里的__radd__(self, other)就是__add__(self, other)的反算术运算符。
9.增量赋值运算
1.__iadd__(self, other)2.__isub__(self, other)3.__imul__(self, other)4.__ifloordiv__(self, other)5.__itruediv__(self, other)6.__imod__(self, other)7.__ipow__(self, other)8.__iand__(self, other)9.__ior__(self, other)10.__ixor__(self, other)
注意:x = 5x += 1 # 这里的+=就是增量赋值,将x+1赋值给了x因此对于a += b, __iadd__ 将返回a + b, 并赋值给a。
10.类型转换
1.__int__(self)实现了类型转化为int的行为.2.__long__(self)实现了类型转化为long的行为.3.__float__(self)实现了类型转化为float的行为.4.__complex__(self)实现了类型转化为complex(复数, 也即1+2j这样的虚数)的行为.5.__oct__(self)实现了类型转化为八进制数的行为.6.__hex__(self)实现了类型转化为十六进制数的行为.7.__index__(self)在切片运算中将对象转化为int, 因此该方法的返回值必须是int。
实例:class Thing(object):def __index__(self):return 1thing = Thing()list_ = ['a', 'b', 'c']print list_[thing] # 'b'print list_[thing:thing] # []上面例子中, list_[thing]的表现跟list_[1]一致,正是因为Thing实现了__index__方法。
如果对一个dict对象执行dict_[thing]会怎么样呢?dict_ = {1: 'apple', 2: 'banana', 3: 'cat'}print dict_[thing] # raise KeyError这个时候就不是调用__index__了。虽然list和dict都实现了__getitem__方法, 但是它们的实现方式是不一样的。如果希望上面例子能够正常执行, 需要实现Thing的__hash__ 和 __eq__方法.class Thing(object):def __hash__(self):return 1def __eq__(self, other):return hash(self) == hash(other)dict_ = {1: 'apple', 2: 'banana', 3: 'cat'}print dict_[thing] # apple
11.其他的方法
1.__str__(self)对实例使用str()时调用2.__repr__(self)对实例使用repr()时触发注意:str()和repr()都是返回一个代表该实例的字符串,主要区别在于: str()的返回值要方便人来看,而repr()的返回值要方便计算机看。3.__unicode__(self)对实例使用unicode()时调用。unicode()与str()的区别在于: 前者返回值是unicode, 后者返回值是str。unicode和str都是basestring的子类。当你对一个类只定义了__str__但没定义__unicode__时,__unicode__会根据__str__的返回值自动实现,即return unicode(self.__str__());但返回来则不成立。4.__format__(self, formatstr)"Hello, {0:abc}".format(a)等价于format(a, "abc"), 等价于a.__format__("abc")。这在需要格式化展示对象的时候非常有用,比如格式化时间对象。5.__hash__(self)对实例使用hash()时调用, 返回值是数值类型。6.__bool__(self)对实例使用bool()时调用, 返回True或者False。7.__dir__(self)对实例使用dir()时调用。通常实现该方法是没必要的。8.__sizeof__(self)对实例使用sys.getsizeof()时调用。返回对象的大小,单位是bytes。9.__instancecheck__(self, instance)对实例调用isinstance(instance, class)时调用。 返回值是布尔值。它会判断instance是否是该类的实例。10.__subclasscheck__(self, subclass)对实例使用issubclass(subclass, class)时调用。返回值是布尔值。它会判断subclass否是该类的子类。11.__copy__(self)对实例使用copy.copy()时调用。返回"浅复制"的对象。12.__deepcopy__(self, memodict={})对实例使用copy.deepcopy()时调用。返回"深复制"的对象。13.__call__(self,*args,**kwargs)该方法允许类的实例跟函数一样表现
注意:Python3中的差异Python3中,str与unicode的区别被废除了,因而__unicode__没有了,取而代之地出现了__bytes__.Python3中,division默认就是true division, 因而__div__废弃.__coerce__因存在冗余而废弃.__cmp__因存在冗余而废弃.__nonzero__改名为__bool__.

转载于:https://www.cnblogs.com/stephenZCG/p/10091278.html

你可能感兴趣的文章
asp.net 设置session失效时间
查看>>
杭电多校第四场 E Matrix from Arrays
查看>>
ReactiveCocoa操作方法-线程\时间
查看>>
oracle 分析函数
查看>>
CHD-5.3.6集群上sqoop安装
查看>>
解决无/var/log/messages 问题
查看>>
js 判断是不是空、值是否存在
查看>>
分布式一致性协议-2PC与3PC(二)
查看>>
SCP-bzoj-1079
查看>>
Python 实践项目 游戏
查看>>
AJAX--Jquery
查看>>
模拟新浪微博随便看看
查看>>
环境搭建
查看>>
解密EXL
查看>>
简易版cnlog
查看>>
erlang程序运行的几种方式
查看>>
堆heap和栈Stack(百科)
查看>>
html5页面实现点击复制功能
查看>>
633. 寻找重复的数
查看>>
沉淀,再出发:python中的pandas包
查看>>