1. 什么是魔法函数?

  • Python里面的魔法函数,是以双下划线开头和结尾
  • 魔法函数不依赖任何类,并且可以随时调用
  • 一旦类里面加上一些特定的魔法函数,整个类就被附加了一些特定的功能.
  • 魔法函数定义了我们不需要显式的调用它,Python解释器自己会知道什么情况下会调用,我们在使用相应的语法的时候就会自动调用。
  • 魔法函数和本身的类没有关系,和类的父类,object也没有关系。魔法函数可以写到任意一个类中,跟继不继承没有必然的关系。

2. 实例

  • for循环的是可迭代对象, 首先通过__iter__得到一个迭代器, 然后不断调用迭代器的__next__, 但是如果对象没有实现 __iter__或__next__迭代器协议,Python的解释器就会去寻找__getitem__来迭代对象,如果连__getitem__都没有定义,这解释器就会报对象不是可迭代对象的错误.
  • 魔法函数是能影响到语法本身的, 本来company是无法进行切片操作, 但是由于实现了__getitem__, 所以在python的语法上我们可以对实例化的对象进行切片操作.(列表也是list这个类实例化得到的一个对象)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Company(object):
def __init__(self, employee__list):
self.employee = employee__list
# item将传入0、1、2...
def __getitem__(self, item):
return self.employee[item]

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

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

for em in company: # __getitem__
print(em)

for em in company[1:]: # __getitem__
print(em)

print(len(company)) # __len__

3. 常用魔法函数(非数学运算类型)

3.1 字符串表示

  • __repr__
  • __str__

3.2 集合序列相关

  • __len__
  • __getitem__
  • __setitem__
  • __delitem__
  • __contains__

3.3 迭代相关

  • __iter__
  • __next__

3.4 可调用

  • __call__

3.5 with上下文管理器

  • __enter__
  • __exit__

3.6 数值转换

  • __abs__
  • __bool__
  • __int__
  • __float__
  • __hash__
  • __index__

3.7 元类相关

  • __new__
  • __init__

3.8 属性相关

  • __getattr__
  • __setattr__
  • __getattribute__
  • __setattribute__
  • __dir__

3.9 属性描述符

  • __get__
  • __set__
  • __delete__

3.10 协程

  • __await__
  • __aiter__
  • __anext__
  • __aenter__
  • __aexit__

4. 常用魔法函数(数学运算类型)

4.1 一元运算符

  • __neg__(-)
  • __pos__(+)
  • __abs__

4.2 二元运算符

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

4.3 算术运算符

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

4.4 反向算术运算符

  • __radd__
  • __rsub__
  • __rmul__
  • __rtruediv__
  • __rfloordiv__
  • __rmod__
  • __rdivmod__
  • __rpow__

4.5 增量赋值算术运算符

  • __iadd__
  • __isub__
  • __imul__
  • __itruediv__
  • __ifloordiv__
  • __imod__
  • __ipow__

4.6 位运算符

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

4.7 反向位运算符

  • __rlshift__
  • __rrshift__
  • __rand__
  • __rxor__
  • __ror__

4.8 增量赋值位运算符

  • __ilshift__
  • __irshift__
  • __iand__
  • __ixor__
  • __ior__

5. 调试工具

notebook

首先使用pip install -i https://douban.com/simple notebook安装然后运行ipython notebook

6. 字符串表示

  • __str__

在打印一个实例化对象的时候, python默认会调用str(对象), 对应的魔法函数是__str__

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"])
print(company)
print(str(company))

<__main__.Company object at 0x000001CFA60BE748>
<__main__.Company object at 0x000001CFA60BE748>
  • __repr__

__repr__是在开发模式下调用的

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"])
print(company)
company

<__main__.Company object at 0x0000020A9D7672B0>
<__main__.Company at 0x20a9d7672b0>

再次强调, __repr__不是因为该类继承了某一个对象才能去写这个方法, 魔法函数可以写到任何一个定义的类中去, 然后python解释器就是识别出这个对象有该特性, 然后再调试模式下company会被解释器转换为repr(company), 然后再去调用company.__repr__().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Company(object):
def __init__(self, employee__list):
self.employee = employee__list

def __str__(self):
return ','.join(self.employee)

def __repr__(self):
return '.'.join(self.employee)

company = Company(["tom", "bob", "jane"])
print(company) # str 输出
company # repr输出

tom,bob,jane # 打印对象
tom.bob.jane # 调试模式

7. len函数

之前提到len函数式会调用对象的__len__, 如果是使用遍历的方式求长度那么效率会很低, 但是在求python内置类型的长度比如len(list)、len(dict)、len(set)等的时候会很快, 因为在cpython中的list、dict、set等是通过c语言实现的, 在其内部会维护一个数据表示长度,所以就不需要进行遍历,能大大提高效率, 所以尽量使用python原生的数据类型