Python的魔法函数

什么是魔法函数?

在Python中魔法函数以__开始以__结束

我们下面看看魔法函数有什么用

1
2
3
4
5
6
7
8
9
10
class Company(object):
def __init__(self, employee_list):
self.employee = employee_list

company = Company(["tom", "bob", "jane"])

emploee = company.employee

for em in emploee:
print(em)

我们创建一个实例,然后使用for 循环遍历实例的属性employee。

有没有什么方法可以直接遍历实例,而不需要遍历属性employee就能获得属性值呢?

答案是魔法函数。

1
2
3
4
5
6
class Company(object):
def __init__(self, employee_list):
self.employee = employee_list

def __getitem__(self, item):
return self.employee[item]

我们在类中增加魔法函数(魔法函数不是单属于一个类的单独方法)

现在我们可以直接对实例变量。

1
2
3
4
company = Company(["tom", "bob", "jane"])

for em in company:
print(em)

实现原理是:当我们变量类实例的时候回去类里边查找是否存在__getitem__魔法函数,当存在时候,会逐个查找变量直至报错。也就是说当我们定义了函数__getitem__之后,对象就变成了可迭代对象。

Python数据模型以及数据模型对Python的影响

魔法函数就是Python数据模型的一种理念。

Python中魔法函数会影响我们的Python语法的,我们在类中定义了魔法函数并不需要显示的调用。魔法函数是单独存在的,不是类继承与什么的父类得到的。我们在类中加入魔法函数之后会增加类的类型(上文说到的一些类型)。例如我们实现了__getitem__魔法函数之后就实现了序列类型,可以进行切片操作。

1
2
3
4
5
6
company = Company(["tom", "bob", "jane"])

company1 = company[:2]

for em in company1:
print(em)

如果没有实现__getitem__直接切片会报错的。

我们通过实现__getitem__使得能够切片,体现了魔法函数对Python的影响,我们大致可以认为Python一些模型的实现原理,例如切片正是实现了__getitem__

例如我们想直接统计长度,在一个类的实例中。

1
2
3
print(len(company))

TypeError: object of type 'Company' has no len()

则会报没有len()方法,下面我们通过魔法函数实现。

1
2
3
4
5
6
7
8
9
class Company(object):
def __init__(self, employee_list):
self.employee = employee_list

def __len__(self):
return len(self.employee)

company = Company(["tom", "bob", "jane"])
print(len(company))

这时会打印输出长度3.

Python魔法函数一览

非数学运算

字符串表示

1
__repr__, __str__

集合,序列相关

1
__len__, __getitem__, __setitem__, __delitem__, __contains__

迭代相关

1
__iter__, __next__

可调用

1
__call__

with上下文管理器

1
__enter__, __exit__

数值转换

1
__abs__, __bool__, __int__, __float__, __hash__, __index__

元类相关

1
__new__, __init__

属性相关

1
__getattr__, __setattr__, __getattribute__, __setattribute__, __dir__

属性描述符

1
__get__, __set__, __delete__

协程

1
__await__, __aiter__, __anext__, __aenter__, __aexit__
数学运算

一元运算符

1
__neg__(-), __pos__(+), __abs__

二元运算符

1
__lt__(<), __le__(<=), __eq__(=), __ne__(!=), __gt__(>), __ge__(>=)

算术运算符

1
__add__ + 、 __sub__ - 、 __mul__ * 、 __truediv__ / 、 __floordiv__ // 、 __mod__ % 、 __divmod__ divmod() 、 __pow__ ** 或 pow() 、 __round__ 、round()

反向算术运算符

1
__radd__ 、 __rsub__ 、 __rmul__ 、 __rtruediv__ 、 __rfloordiv__ 、 __rmod__ 、 __rdivmod__ 、 __rpow__

增量赋值算术运算符

1
__iadd__ 、 __isub__ 、 __imul__ 、 __itruediv__ 、 __ifloordiv__ 、 __imod__ 、 __ipow__

位运算符

1
__invert__ ~ 、 __lshift__ << 、 __rshift__ >> 、 __and__ & 、 __or__ | 、 __xor__ ^

反向未运算符

1
__rlshift__ 、 __rrshift__ 、 __rand__ 、 __rxor__ 、 __ror__

增量赋值位运算符

1
__ilshift__ 、 __irshift__ 、 __iand__ 、 __ixor__ 、 __ior__

我们看第一个__repr____str__,当我们打印输出一个对象的时候是默认调用str方法(对应魔法函数__str__)当我们未定义魔法函数只会打印出对象地址。__repr__是开发状态调用的(运行时)。魔法函数定义之后会隐式调用。

len函数的特殊性

当我们使用len()方法统计set,list,dict的长度的时候,会直接通过cpython解释器获得长度,性能非常高,而不是遍历对象去获得长度。

知识就是财富
如果您觉得文章对您有帮助, 欢迎请我喝杯水!