You'll miss 100% of shots you don't take.

一、魔术方法简介

Python 中所有以双下划线“__”包起来的方法,统称为“Magic Method”(魔术方法)。

魔术方式是 Python 的内置方法,不需要主动调用,当我们对实例调用某些特定函数或运算符时,会自动触发。例如最常见的 __new__ 和 __init__:当调用 p = Person() 时,首先触发 __new__ 创建一个 instance 并返回,然后触发 __init__ 用传入的参数对 instance 进行初始化。

Python 的魔术方法有很多,下面介绍一些常见的魔术方法。

二、常见的魔术方法及代码示例

1.实例生命周期、允许实例像函数一样被调用

__new__(cls)、__init__(self):创建实例、实例初始化的时候触发

__del__(self):销毁实例的时候触发

__call__(self):实例像函数一样被调用时触发

代码示例:

class Person(object):
def __new__(cls, name, age, city="beijing"):
print('__new__()方法,"创建实例"时触发。')
instance = super(Person, cls).__new__(cls)
# instance = object.__new__()
print(instance, id(instance))
return instance
def __init__(self, name, age, city="beijing"):
print('__init__()方法,"初始化实例"时触发。')
# __init__()的输入参数self,就是__new__()的返回值instance
print(self, id(self))
self.name = name
self.age = age
self.city = city
# 普通方法
def cook(self, menu):
print('这是一个普通函数:cook()')
print(f"{self.name}正在煮{menu}...")
def __call__(self, course):
print('__call__()方法,"实例像函数一样被调用"时触发。')
print(f"{self.name}正在自习室学习{course}...")
def __del__(self):
print('__del__()方法,"实例销毁"时触发。')
# 先触发__new__()创建一个instance,然后自动调用__init__()对实例进行初始化
p = Person('Tom', 16, city='shanghai')
print("----------")
p.cook('coffee') # 调用普通函数
print("----------")
p('Maths') # 允许实例像函数一样被调用,触发__call__()方法
print("----------")
del p # 实例被销毁,触发__del__()方法

运行结果:

__new__()方法,"创建实例"时触发。

<__main__.person object at> 4362083472

__init__()方法,"初始化实例"时触发。

<__main__.person object at> 4362083472

----------

这是一个普通函数:cook()

Tom正在煮coffee...

----------

__call__()方法,"实例像函数一样被调用"时触发。

Tom正在自习室学习Maths...

----------

__del__()方法,"实例销毁"时触发。

2.实例的比较

__eq__(self, other):定义相等符号的行为,==

__ne__(self, other):定义不等符号的行为,!=

__lt__(self, other):定义小于符号的行为,<

__gt__(self, other):定义大于符号的行为,>

__le__(self, other):定义小于等于符号的行为,<=

__ge__(self, other):定义大于等于符号的行为,>=

代码示例:

class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
def __eq__(self, other):
print('实例比较"=="时触发__eq__()')
return self.age == other.age
def __ne__(self, other):
print('实例比较"!="时触发__ne__()')
return self.age != other.age
def __gt__(self, other):
print('实例比较">"时触发__gt__()')
return self.age > other.age
def __lt__(self, other):
print('实例比较"
return self.age < other.age
def __ge__(self, other):
print('实例比较"<="时触发__ge__()')
return self.age >= other.age
def __le__(self, other):
print('实例比较"<="时触发__le__()')
return self.age <= other.age
p1, p2 = Person('Tom', 16), Person("Json", 16)
print(p1 == p2) # 触发__eq__()
print("----------")
print(p1 != p2) # 触发__ne__()
print("----------")
print(p1 > p2) # 触发__gt__()
print("----------")
print(p1 < p2) # 触发__lt__()
print("----------")
print(p1 >= p2) # 触发__ge__()
print("----------")
print(p1 <= p2) # 触发__le__()

运行结果:

实例比较"=="时触发__eq__()

True

----------

实例比较"!="时触发__ne__()

False

----------

实例比较">"时触发__gt__()

False

----------

实例比较"

False

----------

实例比较"<="时触发__ge__()

True

----------

实例比较"<="时触发__le__()

True

3.容器相关

__len__(self):len(instance)时触发

__getitem__(self, key):instance[key]时触发

__setitem__(self, key, value):instance[key]=value时触发

__delitem__(self, key, value):del instance[key]时触发

代码示例:

class Book(object):
def __init__(self, title, page_size=100):
self.title = title
self.page_size = page_size
self.li = [10, 20, 30]
def __len__(self):
print('调用"len(instance)"时触发__len__()方法')
return self.page_size
def __getitem__(self, item):
print('"调用instance[key]"时触发__getitem__()方法')
return self.li[item]
def __setitem__(self, key, value):
print('"调用instance[key]=value"时触发__setitem__()方法')
self.li[key] = value
def __delitem__(self, key):
print('"调用del instance[key]"时触发__delitem__()方法')
del self.li[key]
book = Book('maths', page_size=288)
print("这本书的页数:", len(book)) # 触发__len__()方法
print("----------")
print(book[0]) # 触发__getitem__()
print("----------")
print("before book.li:", book.li)
book[0] = 8 # 触发__setitem__()方法
print("middle book.li:", book.li)
print("----------")
del book[0] # 触发__delitem__()
print("finally book.li:", book.li)

运行结果:

调用"len(instance)"时触发__len__()方法

这本书的页数: 288

----------

"调用instance[key]"时触发__getitem__()方法

10

----------

before book.li: [10, 20, 30]

"调用instance[key]=value"时触发__setitem__()方法

middle book.li: [8, 20, 30]

----------

"调用del instance[key]"时触发__delitem__()方法

finally book.li: [20, 30]

4.迭代器

定义一个迭代器,需实现__iter__()和__next__()。

__iter__(self):调用iter(instance)时触发,返回一个迭代器

__next__(self):调用next(instance)时或对instance进行迭代时触发

代码示例:

# 自定义迭代器
class MyIter(object):
def __init__(self, total, start=0):
self.total = total
self.start = start
def __iter__(self):
print('"调用iter(instance)"时触发__iter__()')
return self
def __next__(self):
print('"调用next(instance)"时触发__next__()')
if self.start < self.total:
self.start += 1
return self.start
else:
raise StopIteration
myIter = MyIter(5)
print(next(myIter)) # 触发__next__()
print("----------")
it = iter(myIter) # 触发__iter__()
print("----------")
print(next(it)) # 触发__next__()
print("----------")
for value in it: # 进行迭代操作,触发__next__()
print(value)

运行结果:

"调用next(instance)"时触发__next__()

1

----------

"调用iter(instance)"时触发__iter__()

----------

"调用next(instance)"时触发__next__()

2

----------

"调用iter(instance)"时触发__iter__()

"调用next(instance)"时触发__next__()

3

"调用next(instance)"时触发__next__()

4

"调用next(instance)"时触发__next__()

5

"调用next(instance)"时触发__next__()