魔术方法不过是一种特殊的方法,它不需要人工调用,在特定的时刻会自动执行。
比如像a-b
其实"魔术"般地执行了a.__sub__(b)
,像__sub__
这样的以"__"双下划线包起来的方法,都统称魔术方法。
魔法方法大致可以分为两类:与运算符相关以及与运算符无关的,而两大类下还有很多小类
1、与运算符相关的魔术方法很简单,即触发条件为运算符
2、而与运算符无关的才是我们需要着重关心的地方,也就是说以下才是你能秀起来的魔术方法
__new__
用来创建类并返回这个类的实例, 然后__init__
将传入的参数来初始化该实例,可以说这两个方法共同构成了"构造函数"1、__init__
通常用于初始化一个新实例,控制这个初始化的过程,比如添加一些属性, 做一些额外的操作,发生在类实例被创建完以后,所以不需要返回值。它是实例级别的方法。
2、__new__
通常用于控制生成一个新实例的过程,所以必须要有返回值。它是类级别的方法
使用__new__
和__init__
来实现单例模式:
import threading
class Singleton(object):
_instance_lock = threading.Lock()
#初始化实例
def __init__(self, arg1, arg2):
self.arg1 = arg1
self.arg2 = arg2
#创建类实例
def __new__(cls, arg1, arg2):
with Singleton._instance_lock:
if not hasattr(Singleton, "_instance"):
#由于重写了__new__所以要调用父类的方法达到原本的效果
Singleton._instance = object.__new__(cls)
return Singleton._instance
obj1 = Singleton(1,1)
obj2 = Singleton(2,2) #会覆盖obj1初始化的属性
print(obj1 is obj2) #true
__del__
方法会被调用,可以理解为“构析函数” :del obj <-> obj.__del__()
__getattr__(self,name)
:获取一个不存在的属性时执行的方法。
__setattr__(self,name,value)
:设置一个属性时执行的方法。
__delattr__(self,name)
:删除一个属性时执行的方法。
__getattribute__(self,name)
:当该类的属性被访问时执行的方法。
class A(object):
def __getattr__(self,name):
print('__getattr__') #没有在父类中进行定义
def __setattr__(self,name,value):
print('__setattr__')
super(A).__setattr__(name,value)
def __delattr__(self,name):
print('__delattr__')
return super(A).__delattr__(name)
def __getattribute__(self,name):
print('__getattribute__')
return super(A).__getattribute__(name)
>>> a = A()
>>> a.name
__getattribute__ #只要访问属性就会先调用 __getattribute__
__getattr__ #由于当前没有name属性,则调用__getattr__
>>> a.name = 'jod'
__setattr__
>>> a.name
__getattribute__
'jod'
>>> del a.name
__delattr__
>>> a.name
__getattribute__
__getattr__
__dict__
:返回一个字典,存放了实例或类相关的属性或方法注意:在__setattr__
时,不能直接使用self.name = value
,会造成死循环,因为在执行self.name = value的时候又会调用本身,无限循环下去,可以调用父类的方法,或者使用__dict__
实现:
def __setattr__(self,name,value):
print('__setattr__')
self.__dict__[name] = value
实例的__dict__仅存储与该实例相关的实例属性,正是因为实例的__dict__属性,每个实例的实例属性才会互不影响。
类的__dict__存储所有实例共享的变量和函数(类属性,方法等),类的__dict__并不包含其父类的属性。
dir()
是Python提供的一个API函数,dir()函数会自动寻找一个对象的所有属性(包括从父类中继承的属性);__dict__是dir()的子集,dir()包含__dict__中的属性
__set__(self,instance,value)
:当作一个描述符(属性)被赋值时执行的方法
__get__(self,instance,owner)
:当作一个描述符(属性)被获取时执行的方法
__delete__(self,instance)
:当作一个描述符(属性)被删除时执行的方法
这一部分涉及到描述器的知识,在这里稍作介绍,一个类中只要定义了__get__
、__set__
、__delete__
中的一个或几个,这个类的实例就可以叫做一个描述器。链接
描述器是是@property、super、静态方法、类方法、甚至属性、实例背后的实现机制。其实是定义了一些查找规则。
class A(object):
def __get__(self, instance, owner):
print('__get__', 'self:', self, 'instance:', instance, 'owner:', owner)
def __set__(self, instance, value):
print('__set__', 'self:', self, 'instance:', instance, 'value:', value)
def __delete__(self, instance):
print('__delete__', 'self:', self, 'instance:', instance)
class B(object):
# 一个函数在实例化的时候调用了其他类的对象
# 把A类的对象在B实例化的时候当作描述符(属性)进行赋值,这个name就是一个描述器的实例
def __init__(self,name):
self.name = A()
>>> b = B()
>>> b.name
__get__ self: <__main__.A object at 0x1134247f0> instance: <__main__.B object at 0x113411d68> owner:
>>> b.name = 'abc'
__set__ self: <__main__.A object at 0x1134247f0> instance: <__main__.B object at 0x113411d68> value: abc
>>> del b.name
__delete__ self: <__main__.A object at 0x1134247f0> instance: <__main__.B object at 0x113411d68>
>>>
这一部分的魔术方法提供了自定义容器的"接口规范"
__getitem__
:获取容器中的元素,比如mylist[1]
__setitem__
:设置容器中的元素,比如mylist[2] = ‘hello’
__delitem__
:删除容器中的某个元素,比如 del mylist[2]
__len__
:获取集合的长度,len()时被调用
__contains__
:查询是否包含指定元素,如mylist.contains(1)
在collections模块中已经有了很多如Counter、OrderedDict这样的高级集合,必要时可以结合业务特性自定义容器
__enter__(self)
定义当使用 with 语句时的初始化行为__enter__ 的返回值被 with 语句的目标或者 as 后的名字绑定
__exit__(self, exc_type, exc_value, traceback)
定义当一个代码块被执行或者终止后上下文管理器应该做什么
一般被用来处理异常,清除工作或者做一些代码块执行完毕之后的日常工作
with所求值的对象必须要有这两个方法
紧跟with后面的语句被求值后,返回对象的__enter__()
方法被调用,这个方法的返回值将被赋值给as后面的变量。当with后面的代码块全部被执行完之后,将调用前面返回对象的__exit__()
方法。
class Sample(object):
def __enter__(self):
print "In __enter__()"
return "Foo"
def __exit__(self, type, value, trace):
print "In __exit__()"
def get_sample():
return Sample()
with get_sample() as sample:
print "sample:", sample
'''
流程:
1、enter__()方法被执行
2、enter__()方法返回的值 - 这个例子中是"Foo",赋值给变量'sample'
3、执行代码块,打印变量"sample"的值为 "Foo"
4、exit__()方法被调用
'''
In __enter__()
sample: Foo
In __exit__()