前言
前面已经总结了关键字, 运算符与魔法方法的对应关系, 下面总结 python 内置函数对应的魔法方法.
魔法方法
数学计算
abs(args): 返回绝对值, 调用__abs__;
round(args): 返回四舍五入的值, 调用__round__;
math.floor(): 向下取整, 调用__floor__;
math.ceil(): 向上取整, 调用__ceil__;
math.trunc(): 求一个值距离 0 最近的整数, 调用__trunc__;
divmod(a,b): 返回商和余, 调用__divmod__;
pow(a,b): 返回幂, 调用__pow__;
sum(): 返回和, 调用__sum__;
float(): 转换小数, 调用__float__;
int(): 转换整数, 调用__int__;
str(): 转换字符串, 调用__str__;
sys.getsizeof(): 对象占内存的大小, 调用__sizeof__;
bin(*args, **kwargs): 调用参数的__bin__方法, 返回整数的二进制表示形式, 只支持一个参数, 只支持 int 类型
hash(): 调用__hash__方法, 获取一个对象的散列值, 相等的两个数哈希值相等, 反过来不一定成立
hex(*args, **kwargs): 调用__hex__方法, 求整数的十六进制表示形式, 只支持 Int 类型
oct(*args, **kwargs): 调用__oct__方法, 求整数的八进制表示形式, 只支持 Int 类型
访问控制
__getattr__(self, name):getattr 方法触发, 仅对对象未定义的属性有效, 即如果视图获取一个没有的属性时会调用该方法, 前提是该对象未定义__getattribute__(self, name) 方法;
__getattribute__(self, name):getattr 方法触发, 如果对象定义了该方法, 一定触发,__getattr__方法将不会被调用; 它也可以被 self.name 语法糖触发;
__setattr__(self, name, value):setattr 方法触发, 设置一个对象的属性; 也可以被 self.name = ''语法糖触发.
__delattr__(self, name):delattr 方法触发, 删除一个对象的属性, 或由 del self.name 形式触发;
容器类型
在 Python 中实现自定义容器类型需要用到一些协议. 不可变容器类型有如下协议:
不可变容器, 需要定义 _len_ 和 _getitem_ ;
可变容器, 需要定义 _len_ ,_getitem_,_setitem_,_delitem_ ;
容器可迭代, 需要定义 _iter_ ;
迭代器, 必须遵守迭代器协议, 需要定义 _iter_ 和 _next_ 方法.
索引语法糖与魔法方法
__len__(self): 返回容器的长度;
__getitem__(self, key): 使用 self[key] 形式语法糖获取元素会触发;
__setitem__(self, key): 使用 self[key] = 'xxx'形式复制会触发;
__delitem__(self, key): 使用 del self[key] 语法糖触发
__reversed__(self):reversed(self) 触发, 反转容器;
__missing__(self, key): 字典结构使用 self[key] 形式获取元素, 如果元素不存在触发;
分片语法糖与魔法方法
切片在底层的原理, py2 和 py3 有很大的不同, py2 中使用_getslice_,_setslice_,__delslice__三个魔法方法控制, py3 中将索引和切片统一由_getitem_,_setitem_,__delitem__控制.
- # py2 中
- ls = [1,2,3,4]
- print(ls[1:3]) # py2 中该语法糖调用__getslice__方法, py3 中废弃
- del ls[1:3] # py2 中该语法糖调用__delslice__方法, py3 中废弃
- ls[1:3] = [1,2,2] # py2 中该语法糖调用__setslice__方法, py3 中废弃
- # py3 中
- class Person(object):
- def __getitem__(self, item):
- print(item)
- return 'getitem'
- def __setitem__(self, key, value):
- print(key, value)
- return 'setitem'
- def __delitem__(self, key):
- print(key)
- return 'delitem'
- if __name__ == "__main__":
- person = Person()
- print(person[0]) # person[0] ==> person.__getitem__(0)
- print(person[0:2]) # person[0:2] ==> person.__getitem__(slice(0,2,None))
- person[0:2] = 'test' # ==> person.__setitem__(slice(0,2,None), 'test')
- del person[0:2] # ==> person.__delitem__(slice(0,2,None))
- # 结果
- 0
- getitem
- slice(0, 2, None)
- getitem
- slice(0, 2, None) test
- slice(0, 2, None)
python 在处理索引语法糖的时候, 将索引当做参数传入相关 getitem,setitem,delitem 的魔法方法; 在处理切片语法糖的时候先调用 slice 方法得到 slice 实例对象, 将其作为参数调用相关的魔法方法.
拷贝
__copy__(self): 如果对象定义了该方法, copy.copy() 就会调用该方法返回拷贝对象;
__deepcopy__(self, x): 如果对象定义了该方法, copy.deepcopy() 就会调用该方法返回拷贝对象;
序列化
序列化我们可以简单理解成对任何数据的一种描述方法, 如果多种平台遵循了相同的序列化协议, 数据之间的传递就会变得方便. python 默认的序列化模块为 pickle.
序列化的简单例子
- class Person(object):
- def __init__(self):
- self.name = 'cai'
- if __name__ == "__main__":
- import pickle
- person = Person()
- with open('./person.txt', 'wb') as f:
- # 序列化后存储
- pickle.dump(person,f)
- with open('./person.txt', 'rb') as f:
- # 反序列化
- per = pickle.load(f)
- print(per.name)
- # 我们可以把一个类保存起来, 后续读取它直接使用.
相关的魔法方法
__getinitargs__(self): 该魔法方法在 py3 中似乎被废弃, 原本的功能是在序列化时获取实例化参数, 应该返回一个元组;
__getnewargs__(self): 对新式类, 通过这个方法改变类在反 pickle 时传递给__new__ 的参数; 应该返回一个参数元组.
__getstate__(self): 定义对象被序列化时的状态, 而不使用对象的 __dict__ 属性, 必须返回一个字典, 他会去替代 __dict__ 属性, 在序列化时被调用;
__setstate__(self,state): 当一个对象被反 pickle 时, 如果定义了 __setstate__ , 对象的状态会传递给这个魔法方法, 而不是直接应用到对象的 __dict__ 属性, state 参数是序列化前的__dict__属性.
- class Person(object):
- def __init__(self,name):
- print('init')
- self.name = name
- def __getinitargs__(self):
- print('initargs')
- return 'zhao',
- def __getnewargs__(self):
- print('newargs')
- return 'wang',
- def __getstate__(self):
- print('getstate')
- return {'name':'xiao'}
- def __setstate__(self, state):
- print('setstate')
- print(state)
- self.__dict__ = state
- if __name__ == "__main__":
- import pickle
- person = Person('cai')
- with open('./person.txt', 'wb') as f:
- # 序列化后存储
- pickle.dump(person,f)
- with open('./person.txt', 'rb') as f:
- # 反序列化
- per = pickle.load(f)
- print(per.name)
- # 结果
- __new__
- init
- newargs
- getstate
- __new__
- setstate
- {'name': 'xiao'}
- xiao
说明:
pickle 序列化对象之前, 先执行__getnewargs__或__new__方法的参数;
然后执行__getstate__方法, 返回的值替代对象的__dict__属性值;
反序列化时调用 new 方法, 以 getnewargs 返回的值作为参数创建实例;
最后调用__setstate__方法, 将 getstate 方法的返回值作为 state 参数;
所以由于反序列化时不会调用 init 方法初始化, getinitargs 和 getnewargs 方法的作用都变得不大;
其他
__instancecheck__(self, instance):instance 触发, 判断对象的类型
__subclasscheck__(self, subclass):issubclass 触发, 判断一个对象是另一个对象的子类;
__call__:callable 触发, 判断一个对象是否可调用;
__dir__(self):dir() 触发, 获取对象的所有属性, 方法的名字组成的列表;
__str__和__repr__
调用 str 触发_str_, 调用 repr() 触发_repr_, 但是 print() 也可以触发__str__和__repr__, 如果对象定义了_str_, 则 print() 一般触发_str_, 否则触发_repr_; 但列表以及字典等容器总是会使用_repr_ 方法.
__str__和__repr__的区别
一般来说,_str_ 的返回结果在于强可读性, 而 _repr_ 的返回结果在于准确性;
默认情况下, 在需要却找不到 __str__方法的时候, 会自动调用 _repr_ 方法.
总结
熟悉了 python 语法糖, 内置函数与魔法方法之间的关系后, 显然对于如何写好一个优雅易用的类有很大的帮助.
来源: https://www.cnblogs.com/cwp-bg/p/9856309.html