一、参考
A Guide to Python's Magic Methods
二、构造和初始化
2.1 __new__
在对象实例化过程中最先调用的方法是__new__
, 该方法接收参数为类,然后将其他参数,传递给__init__
, 该魔法函数比较少见,可以使用其,创建单例类; __new__
方法是一个类方法,需要携带的第一个参数是类
class T1(object):
_instances = {}
def __new__(class_, *args, **kwargs):
if class_ not in class_._instances:
class_._instances[class_] = super(T1, class_).__new__(class_, *args, **kwargs)
return class_._instances[class_]
def __init__(self, *args, **kwargs):
pass
2.2 __init__
__init__
是一个实例方法,用于将构造的实例初始化,在类定义中十分常见
2.3 __del__
类比于C++
, __new__
和__init__
可以当作类的构造函数,__del__
充当类的析构函数,该函数在垃圾回收时候调用,而不是在del object
时候触发,可以用于添加套接字或者文件的close()
逻辑,但是使用需要小心,实际几乎不使用该方法
from os.path import join
class FileObject:
'''Wrapper for file objects to make sure the file gets closed on deletion.'''
def __init__(self, filepath='~', filename='sample.txt'):
# open a file filename in filepath in read and write mode
self.file = open(join(filepath, filename), 'r+')
def __del__(self):
self.file.close()
del self.file
三、重定义运算符
四、获取类表示信息
4.1 __str__
__str__(self)
自定义对类调用str()
方法时候的行为
4.2 __repr__
__repr__(self)
定义对类的实例调用repr()
时候的行为,str()
和repr()
的不同之处是:目标受众的不同,repr()
的目的是生成主要是机器可读的输出,许多情况下,可能输出为python代码, 而str()
一般输出为人类可读的信息
4.3 __unicode__
__unicode__(self)
定义对类实例执行unicode()
方法时候的行为,其返回unicode
字符串,如果只定义了unicode()
,使用str()
会报错,所以需要同事定义两个函数
4.4 __format__
__format__(self, formatstr)
定义在新样式字符串格式中使用类实例时候的行为
4.5 __hash__
__hash__(self)
当调用hash()
函数时候定义行为,通常用于字典中的key
的快速比较是否相同,通常也需要实现__eq__
, 遵循下面的规则,
a == b
实际为 hash(a) == hash(b)
4.6 __nonzero__
__nonzero__(self)
定义对类的实例调用bool()
时候的行为,返回值为True
或者False
,取决于具体的类
4.7 __dir__
__dir__(self)
定义对类的实例调用dir()
函数时候的行为,返回值是用户的属性列表,通常,实现dir()
函数是不必要的,但是,如果重新定义了__getattr__
或者__getattribute__
或者其他动态生成属性,则其对于交互式使用类非常重要
4.8 __sizeof__
__sizeof__(self)
定义对类实例调用sys.getsizeof()
函数时候的行为,返回值为对象的大小(字节为单位),通常对于C扩展实现的Python类有作用
五、控制属性访问
如果与其他语言比较(例如: Java
),Python
中好像没有真正的封装,例如,没有办法通过公共函数getter
和setter
定义私有属性,这是事实。Python
通过下列的魔法函数实现属性的封装,而不是使用显式的修饰符
5.1 __getattr__
__getattr__(self, name)
可以定义当用户试图访问一个不存在的属性时候的行为,这对于捕获或者重定向常见的拼写错误、引发警告等非常有用,只有当访问不存在的属性时候才会调用该方法,因此它不是真正的封装解决方案
5.2 __setattr__
__setattr__(self, name, value)
与__getattr__
不相同,__setattr__
是一种封装解决方案,允许定义分配属性值的行为,如果该属性值已经存在,则会覆盖,由此可以自定义属性值的赋值规则
def __setattr__(self, name, value):
self.name = value
# since every time an attribute is assigned, __setattr__() is called, this
# is recursion.
# so this really means self.__setattr__('name', value). Since the method
# keeps calling itself, the recursion goes on forever causing a crash
由上,代码self.name = value
会调用__setattr__
内置函数,所以会导致循环无限递归,正确的定义方式为
def __setattr__(self, name, value):
self.__dict__[name] = value # assigning to the dict of names in the class
# define custom behavior here
5.3 __delattr__
__delattr__(self, name)
与__setattr__
相同,但是作用是删除属性而不是设置属性,为了防止无限递归,还需要采取与__setattr__
相同的预防措施(在__delattr__
的实现中调用del self.name
将导致无限递归)
5.4 __getattribute__
__getattribute__(self, name)
不建议使用该函数,因为极少情况下可以不产生bug正确使用。
每次获取属性时候,都会调用该函数
5.5 总结
Python
的魔法方法非常重要且强大,如果任意使用可能会带来破坏,因此,在已经充分了解了自定义属性访问之前,不要随意使用该魔法方法,事实上,魔法方法往往过于强大和反直觉,它存在的原因是:通过它,可以自由的做任何想做的事情,但是如果不充分熟悉,使用将会非常困难。
class AccessCounter(object):
'''A class that contains a value and implements an access counter.
The counter increments each time the value is changed.'''
def __init__(self, val):
super(AccessCounter, self).__setattr__('counter', 0)
super(AccessCounter, self).__setattr__('value', val)
def __setattr__(self, name, value):
if name == 'value':
super(AccessCounter, self).__setattr__('counter', self.counter + 1)
# Make this unconditional.
# If you want to prevent other attributes to be set, raise AttributeError(name)
super(AccessCounter, self).__setattr__(name, value)
def __delattr__(self, name):
if name == 'value':
super(AccessCounter, self).__setattr__('counter', self.counter + 1)
super(AccessCounter, self).__delattr__(name)
六、自定义序列
七、反射
八、可调用对象
在python
中,函数是第一类对象,这意味着它们可以被传递给函数和方法,好像其他第一类对象一样(数值、字符串等),而这是一个非常强大的功能。
通过魔法函数__call__
,可以使得实例也表现的像函数一样,以便可以调用实例,可以将实例作为参数在函数调用中传递。
8.1 __call__
__call__(self, [args...])
允许将实例进行函数调用,如下,实际作用为e(5,6)
实际为调用e.__call__(5,6)
在经常需要修改实例的状态的场景下,使用__call__
调用实例,修改实例状态是一种十分直观且优雅的方式
class Entity(object):
'''Class to represent an entity. Callable to update the entity's position.'''
def __init__(self, size, x, y):
self.x, self.y = x, y
self.size = size
def __call__(self, x, y):
'''Change the position of the entity.'''
self.x, self.y = x, y
if __name__ == '__main__':
e = Entity(size=10, x=1, y=2)
print(e)
e(5, 6)
print(e)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。