站内链接:

初始化和构造

__new__

格式:__new__(self, …)

功能:控制实例对象的创建,负责返回一个实例对象。除非你要继承不可变类型例如(tuple, str, unicode),否则一般不会 override 该方法

PS:一般情况下,使用 Factory 来代替使用__new__方法

__init__

格式:__init__(self, …)

功能:负责对象的初始化工作,此时实例对象已经创建,不返回任何值

__del__

格式:__del__(self)

功能:析构器,但是并非 del x 的实现逻辑,用于定义一个对象进行 GC 操作时的行为,但是如果对象仍旧被引用则 GC 无法作用。

__copy__

格式:__copy__(self)
功能:浅复制,copy.copy()时调用

__deepcopy__

格式:__deepcopy__(self, memodict={})

功能:深复制,copy.deepcopy(object)时调用

__mro__

格式:__mro__(cls)

功能:祖先链查找,另见mro legb.

运算符

逻辑运算

  1. __cmp__

格式:__cmp__(self, other)

功能:最基本的用于比较的魔术方法,但是可能不同的比较符号的规则可能不一样。

PS:python3 中被废弃,使用下面的 6 个函数(operator 模块中)

  1. __eq__

格式:__eq__(self, other)

功能:实现==的行为逻辑

等式比较级别:

  • 相同的 hash 值,见下面的__hash__
  • 等号比较,hash 值相等 + 值相等, 见==操作符
  • 相等的 IDD, hash 值相等 + 值相等 + 同一对象,见 is 操作符

3 . __ne__

格式:__ne__(self, other)

功能:实现!=的行为逻辑

  1. __lt__

格式:__lt__(self, other)

功能:实现&lt 的行为逻辑

  1. __gt__

格式:__gt__(self, other)
功能:实现&gt 的行为逻辑

  1. __le__, __ge__

功能: 大于等于, 小于等于

算数运算

一元操作符

1
2
3
4
5
6
7
8
# 1. 实现+正号的特性
__pos__(self)
# 2. 实现-减号的特性
__neg__(self)
# 3. 实现abs绝对值特性
__abs__(self)
# 4. 实现~符号的特性
__inver__(self)

算数操作符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 1. 加法
__add__(self, other)
# 2. 减法
__sub__(self, other)
# 3. 乘法
__mul__(self, other)
# 4. 地板除, 5//2 == 2.5
__floordiv__(self, other)
# 5. 除法, 5/2 == 2, 5.0/2 == 2.5
__div__(self, other)
# 6. python3, 除法, 5/2 == 2.5
__truediv__(self, other)`

# 1. 取模, 5 % 2 == 1
__mod__(self, other)
# 2. 除法取余, dirmode(5, 2) == (2, 1)
__divmode__(self, other)
# 3. 幂等, 5 ** 2 = 25
__pow__(self, exponent)


反运算

即将自己当做操作符的第二个操作对象,其他和上面的算法运算符是一直的,一般情况下,正运算和反运算结果一直。

1
2
3
__radd__(self, other)   # 反加
__rsub__(self, other) # 反减
__rmul__(self, other) # 反乘

2.2..4 增量赋值

增量赋值语句,将操作符和赋值结合在一起。

1
2
3
4
5
6
7
8
__iadd__(self, other)    #+=赋值加法
__isub__(slef, other) #-=赋值减法
__imul__(self, other) #*=赋值乘法
__ifloordiv__(self, other) #//=赋值地板除
__idiv__(self, other) #/=赋值除法
__itruediv__(self, other) #/=赋值真除
__imod__(self, mod) #%=赋值取模
__ipow__(self, other) #**=赋值幂运算

位运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1. 左移, 5 << 1 == binary(101) << 1 == binary(1010)
__lshift__(self, other)
# 2. 右移, 5>> 1 == 2
__rshift__(self, other)

# 3. 位与, 5 & 2 == 0, 5 & 3 == 1
__and__(self, other)
# 4. 为或, 5 | 2 == 7
__or__(self, other)
# 5. 异或, 5 ^ 2 == 0
__xor__(self, other)

__ilshift__(self, other) #<<=赋值位左移
__irshift__(self, other) #>>=赋值位右移
__iand__(self, other) #&=赋值位与
__ior__(self, other) #|=赋值位或
__ixor__(self, other) #^=赋值异或

类型转换

通用

类型转换魔术方法,用以实现内置类型转换

1
2
3
4
5
6
7
8
9
__int__(self)    #整形
__long__(self) #长整型
__float__(self) #浮点型
__complex__(self) #复数
__oct__(self) #八进制
__hex__(self) #16进制

__trunc__(self) #math.trunc(self)时调用,获取实现并将数值截取成整形,通常为长整型
__coerce__(self) #混合模式算数

__index__

类实例作为切片的整型值,常常用于多进制代码中,例如:

1
2
3
4
class A8:
def __index__(self):
return 8
9 == [0, 1, 2, 3, 4, 5, 6, 9, 12][A8()]

类输出或者表现类

字符串表示一个类,实现 human 可读性,记录日志时更为简单明了

__str__

格式:__str__(self)

功能:调用 str(object)时的输出值,该值为 human 可读

__repr__

格式:__repr__(self)

功能:调用 repr(object)时的输出值,该值为机器可读,通常情况下 obj == eval(repr(obj)),repr 实现的功能类似``。

其他说明:

1
2
3
默认情况下,如果__repr__定义了,但是__str__未定义, 则会执行__str__=__repr__;
记录详细的日志信息,使用 logging 和 %r 来进行对象的完成信息记录, "%s(%r)" % (self.__class__, self.__dict__)
建议: 建议对所有的类实现__repr__方法

__unicode__

格式:__unicode__(self)

功能:调用 unicode(object)时的输出值,返回 unicode 字符串

PS:在 python2 下面用以替换__str__避免出错,python3 中直接使用__str__,新增__bytes__

__hash__

参考:https://segmentfault.com/a/1190000003969462

格式:__hash__(self)

功能:调用 hash(object)时的输出值–整形。对于 mutable,该函数返回 None。 当手动为一个对象设置一个 hash 值时(immutable),那么该 object 就可以作为字典的 key 存在,关于等值比较见上面的__eq__说明.

值:默认情况下 hash 值 == id(object)/16

相等性检测

1
2
3
4
5
6
7
8
# 1 不可变对象
tuples, nametuples等
默认: 默认继续基类的__eq__和__hash__,相等性检测步骤为:__hash__获取ID值,__eq__比较ID值
重写:__hash__和__eq__方法,复杂对象使用hashlib或者ziblib进行计算

# 2 可变对象
__hash__: 返回None
__eq__: 重写

冻结版本

1
2
可以使用冻结版本对mutable对象进行hash操作,类似版本控制,fronzenset就是如此。
另见<Python 核心编程175页>

__nonzero__

格式: __nonzero__(self)

功能:调用 bool(object)时的返回值,必须返回 true 或者 false

PS:在 Python3 中更名为__bool__

__format__

格式:__format__(self, formatstr)

功能:格式化展示对象,’{0:abc}’.format(a)等价于 format(a, ‘abc’), 等价于 a.__format__(‘abc’),实现了对内建 format 方法的封装

例子:见 magic-method/test_format.py 中的 Foo 类,另见笔记<字符串输出>

__sizeof__

格式:__sizeof__(self)

功能:使用 sys.getsizeof(object)时调用,返回对象的大小,单位为 bytes

属性控制

完成类的封装,私有属性以及相应的 getter、setter 方法。
可以用于实现计数功能,每一次赋值,counter+1

__getattr__

格式:__getattr__(self, name)

功能:当试图获取一个不存在的属性时,发生的行为操作,当且仅当属性不存在的是该方法才会被调用

__setattr__

格式:__setattr__(self, name, value)

功能:当试图赋值一个属性时(不管是否存在),发生的行为操作,避免递归操作

例子:

1
2
3
4
5
6
7
def __setattr__(self, name, vlaue):
# 每次赋值时都会调用__setattr__,递归
self.name = value

def __setattr__(self, name, value):
# 定制特有属性
self.__dict__[name] = value

__delattr__

格式:__delattr__(self, name)

功能:删除一个属性时,发生的行为操作,del self.name,避免递归操作

__getattribute__(存在默认值)

格式:__getattribute__(self, name)

功能:当访问一个存在的属性时调用(先查看是否存在该属性,如果不存在,默认会调用__getattr__, 如果没有定义__getattr__, 则抛出异常 AttributeError 异常)

PS:__getattribute__在很多地方有默认行为,例如下面的 descriptor,所以一般不建议 rewrite 该方法。

默认情况下, 可以对类或者对象添加新的属性值, 并可以获取属性的值

hasattr

格式:hasattr(obj, attr)

功能:检查 obj 是否存在 attr 属性

PS: 对__getattr__的封装, 抛异常表示没有

getattr

格式:getattr(obj, attr)

功能:获取 Obj 中的 attr 值

setattr

格式:setattr(obj, attr, value)

功能:设置值

实例代码

见 python/src/magic-method/test_attribute.py

序列

将类定制成类似 list,tuple,string,dict 的内置序列功能,使用非常广泛

协议或者条件

python 中的协议更像一种编程指南,必须按照某些特定的格式才能实现序列功能。

  • 不可变容器:__len____getitem__
  • 可变容器:__len__, __getitem__, __setitem__, __delitem__
  • 可迭代:__iter__, next, 见< Python 核心编程 202 页>说明

__len__

格式:__len__(self)

功能:容器长度

__getitem__

格式:__getitem__(self, key)

功能:可以使用 self[key]访问某一个条目

PS:注意和__getattribute__以及__getattr__区分开来, 前者使用 obj[key]来访问, 后者使用 obj.key 访问属性.

__setitem__

格式:__setitem__(self, key, value)

功能:定义一个条目被赋值时的行为,self[key] = value,需要做好如何插入的代码

__delitem__

格式:__delitem__(self, key)

功能:一个条目被删除时的行为,del self[key],如果从对象中删除一个元素

__iter__

格式:__iter__(self)

功能:返回一个容器的迭代器,即返回对象本身,返回 self

why?

  • 可扩展的迭代器定义
  • 对列表迭代, 字典迭代带来了性能上的提升
  • 创建真正的迭代接口, 并且向后兼容
  • 迭代非序列集合时,能够提供更加简洁可读的代码

how?

  • 我的下一条数据在哪里? 请见– next 函数的传奇历史
  • 我怎么知道数据全部取完了? 请见– StopIteration 这个神奇的正义之法.

Using: enumerate(), reversed(), dict1.iteritems(), iter(object)

__reversed__

格式:__reversed__(self)

功能:reversed()调用时发生的行为,返回列表的反转版本

__contains__

格式:__contains__(self, item)

功能:调用 in, not in 来测试成员是否存在时需要做的操作,返回 true/false

__concat__

格式:__concat__(self, other)

功能:连接两个序列时需要做的操作,在+操作符调用触发

反射

控制 isinstance()和 issubclass()内置方法的反射行为

__instancecheck__

格式:__instancecheck__(self, instance)

功能:判断 instance 是否为 class 的一个实例

实现:isinstance(object, classinfo)时调用

__subclasscheck__

格式:__subclasscheck__(self, subclass)

功能:检查一个类是不是定义的类的子类

实现:issubclass(subclass, class)时调用

__dir__

格式:__dir__(object)

功能:返回对象的大部分属性字典,dir(object),一般不会 rewrite 该方法,并无太大意义

callable

让类的”实例”行为表现的像函数一样,从而实现将一个函数当成参数进行传递,实现各种功能

格式:__call__(self, [args…])

功能:调用 x()时触发, 其中 x 为实例对象

会话管理

会话控制器:包装 with 语句来设置和清理行为,类似 setUp 和 tearDown 的行为,事务性行为

__enter__

格式:__enter__(self)

功能:定义使用 with 语句时,应该初始块被创建时的行为,其中__enter__的返回值被 with 语句的目标或者 as 后的名字绑定

__exit__

格式:__exit__(self, exception_type, exception_value, traceback)

功能:定义一个代码块被执行时或者终止会话时,应该做的操作:处理异常、清除工作、收尾工作,如果需要直接抛出异常,则该函数返回 false。

参数:如果发生异常,通过三个参数中的栈信息返回

描述器(descriptor)

Introduction

参考:http://pyzh.readthedocs.io/en/latest/Descriptor-HOW-TO-Guide.html

内置的描述器有:property, staticmethod,instance method, classmethod, super。

descriptor 仅仅在新类中生效

Theory

一个描述器是一个有"绑定行为"的对象属性(attribute),通过`__get__`, `__set__`, `__del__`来控制某一个属性的get/set/delete操作。

默认查找

默认的属性 get/set/delete 操作,都是通过如下的查找链来对属性进行操作的,比如需要获取对象 x1 中的 name 值,其中类为 XClass:

  • 从 x1.__dict__中获取 name 值,如果存在,直接返回
  • 从 XClass.__dict__中获取 name 值,如果存在,直接返回
  • 从 XClass 父类的__dict__中获取 name 值,直到碰到 metaclass,抛出异常

但是, 一旦变为描述器, 则会根据类型(资料描述器/数据描述器, 参考 javascript 的数据属性, 存取器属性含义)来进行不同的查找操作.

descriptor

在 13.3 的查找过程中,一旦碰到 descriptor,则 python 解释器会调用描述器的方法来重写这个操作,返回所需的属性值。

整个描述器调用都是因为__getattribute__作为中转器:

  • rewrite 会覆盖默认行为
  • __getattribute__仅仅对新式类生效
  • object.__getattribute__和 type(object).__getattribute__的调用不一样

对象调用过程:

1
2
3
4
发现x1.name定义了__get__方法和__set__方法,启动descriptor的调用;
object.__getattribute__()(或者super(Class, self).__getattribute__())将x1.name变为:
type(x1).__dict__['x'].__get__(b, XClass),
最后返回某个值

类调用:

1
2
3
发现XClass.name定义了__get__方法,启动descriptor转换
XClass.name变为XClass.__dict__['x'].__get__(None, XClass)
返回某个值

super 调用:

1
2
3
super(XClass, obj).name()发现是__get__,启动
变为XClassBase.__dict__['name'].__get__(obj, XClassBase)
返回

协议

格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
descr.__get__(self, obj, type=None)
return --> None
self --> descriptor实例对象
obj --> descriptor装饰的attribute所在的实例
type --> obj的类名

descr.__set__(self, obj, value)
return --> None
self --> descriptor实例对象
obj --> descriptor装饰的attribute所在的实例
value --> 对self中内嵌的属性赋值,对外表现为对descriptor整体的赋值(修饰的attribute)

descr.__del__(self, obj) --> None

data descriptor(数据描述器):

对象同时定义了__get____set__,此时数据描述器的优先级 > 对象字典__dict__中的属性。

not data descriptor(资料描述器):
对象仅仅定义__get__,此时资料描述其的优先级小于__dict__中的属性,见下面的”函数方法”例子/django 中 cached_property 例子.

实现

基于 descriptor 协议的实现由:property、bound/unbound 方法,staticmethod, classmethod

property:

1
2
3
4
5
对某个属性进行包装,并传入get/set/del等rewrite方法,实现指定功能。
其中关于property.__get__和property.getter的区别见:
https://stackoverflow.com/questions/17330160/how-does-the-property-decorator-work
另外见代码:
python/magic-method/test_property.py的注释

函数和方法:

1
2
3
4
Python的面向对象基于函数,其中descriptor将function和method衔接在一起,其中所有函数都是non-data descriptor.
其中__getattribute__对于实例和类,采用不同的方式进行转换,Function内置__get__根据参数的不同判断是否为绑定方法。
见代码:
python/magic-method/test_function.py

类方法和静态方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
non-data descriptor的函数绑定方法变种机制:
Transformation
Called from an Object
Called from a Class
function
f(obj, *args)
f(*args)
staticmethod
f(*args)
f(*args)
classmethod
f(type(obj), *args)
f(klass, *args)

见代码:
python/magic-method/test_staticmethod.py

序列化

序列化数据,需要配合Pickle, CPickle, 自定义协议,magic method来共同实现。
默认情况下,pickle支持内建数据结构

__getinitargs__

格式:__getinitargs__(self)

说明:在序列化的同时调用__init__方法,仅仅适用于老式类

__getnewargs__

格式:__getnewargs__(self)

说明:在序列化的同时调用__new__方法,适用于新类

__getstate__

格式:__getstate__(self)

说明:定义当对象序列化后的返回状态,而不是使用__dict__

__setstate__

格式:__setstate__(self, state)

说明:逆序列化时,对象的状态通过该函数进行和__getstate__匹配的操作

魔术属性

参考:http://www.cnblogs.com/huxi/archive/2011/01/02/1924317.html
注意区别魔术方法和魔术属性的区别,不要搞混了。

__slots__

限定允许绑定的属性名称, 阻止在实例化类时为实例分配 dict, 其包含了当前可访问的属性, 相当于 C++中的成员变量声明. 在大量实例化的应用场景中,可以节省 30%的内容消耗, 命名元祖就是基于slotsproperty来实现的.

提出原因: 类实例化的时候, obj.__dict__其实并不保存类属性和函数, 而每次实例化都会分配一个__dict__, 存在空间浪费问题, 于是就是定义了__slots__(一个元祖), 包含当前能够访问到的属性.

1
2
3
4
5
6
7
8
9
10
11
class Student:
pass
class SlotStudent:
__slots__ = ('name', 'age')

student = Student()
student.name = 'bifeng'

slot = SlotStudent()
slot.name = 'bifeng'
slot.address = 'fujian' # 非法, 报错

该用法在很多公关项目的内部代码中广泛使用, 例如 flask, 另外__slots__也可以在父子类中进行继承, 不过其有一定的规则:

  • 子类未声明 slots, 不继承父类 slots, 此时子类可以随意赋值
  • 子类声明 slots, 继承父类 slots, 此时子类的 slots 为父子两者之和
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Parent:
__slots__ = ('name', 'age')

class NoSlotChild(Parent):
pass

not_slot = NoSlotChild()
not_slot.name = 'bifeng'
not_slot.address = 'fujian'

class SlotChild(Parent):
__slots__ = ('address', 'name')

slot_child = SlotChild()
slot_child.address = 'fujian'
slot_child.name = 'slot'
slot_child.phone = 123 # 报错

module

1
2
3
4
__doc__:python doc string
__name__: 定义时的模块名, 用于指示模块应该如何被加载,见< Python 核心编程-48页 >
__dict__:存储对象属性,key-属性名, value-属性值,该值包含其他三者
__file__: 模块的文件路径,内建的模块没有该属性

class

1
2
3
4
5
__doc__: 类的doc string
__name__: 定义时的类名
__dict__:类属性以及类方法,即可以使用Class.attr访问的所有值
__module__: 类所在的字符串形式的模块名,例如__main__
__bases__: 父类对象元祖,例如: (<type 'object'>,)

object

1
2
3
__doc__: 类的doc string(通过dir可以查看,包含很多上文所述的魔术方法)
__dict__: 对象属性,不包含类属性
__class__: 类对象,例如:<class 'test_staticmethod.ClassMethod'>

built-in method and functions

1
2
3
4
__doc__: 函数或者方法的文档
__name__: 定义时方法名或者函数名
__self__:仅仅在方法中存在
__module__: 所在模块

function

1
2
3
4
5
6
7
8
9
10
__doc__: 函数文档
__name__: 函数定义时的属性名
__module__: 包含该函数的模块名
__dict__: 函数的可用属性
func_dict: 等同于__dict__
func_name: 等同于__name__
func_defaults:参数默认值数组
func_code: 函数的code对象
func_globals:函数的全局命名空间
func_closure:闭包,详情见闭包说明

method

1
2
3
4
5
6
7
__doc__: 方法docstring
__name__: 方法定义时的属性名,其实function和method一样,具体见13节说明。
__module__:方法所在模块名,有时为空
__func__: 实际的函数对象引用
im_func: 见__func__
im_self: 绑定-实例,非绑定-类,见上面的descriptor说明
im_class: 实际调用该方法的类、该方法实例的类

code

这里仅仅指代函数代码块,代码块–由类源代码或者简单语句代码编译得到,可用使用 func_code 获取:

1
2
3
4
5
co_argcount:普通参数的总数,不包括*, **
co_names:所有参数名(包括*, **)和局部变量名的元组
co_varnames:所有局部变量名的元组
co_filename:源代码所在文件名
co_flags:某一个数值

frame

1
2
3
4
5
frame--程序运行时函数调用栈中的某一帧
f_back: 前一帧
f_code: 帧栈对应的code对象
f_locales: 如果是当前栈帧,则值==locals()
f_globals: 如果是当前栈帧,则值==globals()

traceback

1
2
3
tb_next: 下一个追踪对象
tb_frame: 当前追踪对象对应的栈帧
tb_lineno: 当前追踪的行号

引用: