
戏法办法(Magic Methods),望文生义,便是可认为代码中的类赋予某种“法力”的办法(methods)。有了它,你能够构造出十分奇妙、高雅的代码。
或许戏法办法这个词你还不是很熟悉,但你必定见过这样的用法:
class Complex: def __init__(self, realpart, imagpart): self.r = realpart self.i = imagpart
x = Complex(3.0, -4.5)x.r, x.i # 输出:(3.0, -4.5)
参阅:
https://docs.python.org/zh-cn/3.7/tutorial/classes.html#a-word-about-names-and-objects
_init_() 的效果想必你现已很熟悉了,其实,它便是一个戏法办法。在 Python 中,像 _init_() 这种姓名前后带双下划线的办法,被统称为戏法办法。它们能够给 Python 的类增加不同的行为,让类具有奇特的“法力”。
这儿举个简略的比方,让你感受一下戏法办法奇特的当地:
from collections import namedtuple
allcards = namedtuple('allcards', ['ranks', 'color'])
class Poker(object): numberRanks = [str(card) for card in range(2, 11)] charRanks = list('JQKA') allRanks = numberRanks + charRanks colors = 'heart spade diamond club'.split() jokes = 'red black'.split()
def __init__(self): self.cards = [allcards(r, s) for r in self.allRranks for s in self.colors] + [allcards('joke', s) for s in self.jokes]
# 开端发挥魔法 def __len__(self): return len(self.cards)
def __getitem__(self, p): return self.cards[p]
def __add__(self, other): return self.cards + list(other)
# 开端扮演戏法mycard = Poker()
print(f"扑克牌的数量是 {len(mycard)} 张")# 输出:扑克牌的数量是 54 张
print( list( i for i in mycard ))# 输出:[allcards(ranks='2', color='heart'), allcards(ranks='2', color='spade'), … … allcards(ranks='joke', color='black')]
print(f"第5张牌是 {mycard[4]} ")# 输出:第5张牌是 allcards(ranks='3', color='heart')
import randomprint(f"随机抽一张是 {random.choice(mycard)} ")# 输出:随机抽一张是 allcards(ranks='4', color='diamond')
mycard2 = Poker()newcard = mycard + mycard2
random.shuffle(newcard)print(f"洗牌后抽取一张 {newcard.pop()} ")# 输出:洗牌后抽取一张 allcards(ranks='3', color='club')
print(f"现在桌面上扑克牌的数量是 {len(newcard)} 张")# 输出:现在桌面上扑克牌的数量是 107 张
经过这个比方,相信你现已能看到所谓的戏法办法为类赋予的新才干了吧?接下来咱们来具体剖析一下。
除了常见的 _init_() 办法,上面的代码中还用到了_len_(), _getitem_() 和_add_() 这三个戏法办法。
先说 _len_() 办法,假设我没有在 Poker 类中运用它,那么代码运转到 len(mycard) 时就会报错:TypeError: object of type 'Poker' has no len()。因为 Python 内置的 len() 办法只适用于字符串、列表、字典、元组等序列类型的数据结构。那怎样让它支撑咱们自界说的 Poker 类目标呢?
就像上面那样,咱们运用 _len_() 这个魔法函数,并界说其具体完结:
def __len__(self): return len(self.cards)
这样,当咱们调用 len(mycard) 时,就会回来扑克牌的总数量。
这样写还有一个优点,你不用为求总数起一个新的办法名而头疼,比方说用 mycard.sum() 仍是 mycard.length() 好。你只需要沿袭咱们最常用的 len() 办法就能够,免除了解和回忆上的费事,这也是“不要重复造轮子”的另一种表现。
再来看 _getitem_() 办法,咱们在这儿经过_getitem_() 办法完结了对 Poker 的索引操作,并经过 random 模块来完结从扑克牌中随机抽取一张牌的功用。
和 len() 相似,假如不运用戏法办法,直接对 mycard 实例进行索引操作:mycard[4] ,这样的一种情况下相同也会报错:TypeError: 'Poker' object is not iterable。相同,咱们在 Poker 类中界说好 _getitem_() 办法对具体完结:
def __getitem__(self, p): return self.cards[p]
这样,咱们就能够终究靠 mycard[4] 得出第 5 张牌是 allcards(ranks='3', color='heart') ,也便是红桃 3。
第三个魔法办法 _add_() 和前两个相似,具体原理就不具体展开了,在这儿,咱们经过将两个 Poker 实例相加,把一副牌变成了两副牌,这样就能够玩四人斗地主了。
经过完结上面的这些特别办法,咱们为 Python 中自界说的类(或者说自界说的数据类型)完结了跟字符串、数组、字典等其他内置数据类型类型相同的功用。
因为过往构成的途径依靠,许多人在学习 Python 的时分,依据曩昔学习 Java 、C++ 等言语的经历,往往只重视类的封装、承继、多态等等,却疏忽掉了的魔法函数这个强壮的言语特性。
那么是不是每一种脚本言语都有这样的特性呢?这倒不必定,Python 之所以能够支撑戏法办法,是因为它是一门动态类型的言语,这儿的动态是指它只要在运转时才干确认目标的类型,而在运转之前,程序并不重视该目标究竟是什么类型。
意大利软件工程师、Python 软件基金会研究员 Alex Martelli 将这种程序设计的理念叫做“鸭子类型”(Duck typing)。
这个叫法其实来源于美国诗人 James Whitcomb Riley 的诗句:
When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.(一只鸟走起路来像鸭子、游起泳来像鸭子、叫起来也像鸭子,那么这只鸟就能够被称为鸭子。)
James Whitcomb Riley 对鸭子类型的解说是:
In other words, don't check whether it IS-a duck: check whether it QUACKS-like-a duck, WALKS-like-a duck, etc, etc, depending on exactly what subset of duck-like behaviour you need to play your language-games with.
换句话说,不需要去重视目标的类型(它究竟是不是一个鸭子),而是将留意力放在目标具体的行为表现上面(它的行为看起来像不像鸭子),这也是 Python 这类动态言语的类型揣度原理。
好了,论题扯得有点远了,咱们再说回戏法办法。
当你更深化地探究戏法的时分,你会发现它的威力远远不止这些,例如咱们在学习怎么编写 Python 风格(Pythonic)的代码时,网上许多教程的说法是,装修器便是函数功用的扩展,真的只要函数才干完结装修器吗?假如是自界说的类呢?
这就需要用一个叫做 _call_() 的特别办法,让类也能够像函数相同调用。如以下代码:
class Test(object): def __init__(self, func): self._func = func
def __call__(self): print("before running") self._func() print("after running")
经过运用 _call_() 这个戏法办法,Test 类就能够像函数相同被调用,当然也能够正常的运用语法糖 @Test 办法作为装修器来运用,如:
@Testdef myFunction(): print("function running")
myFunction()
# 输出:# before running# function running# after running
好,以上便是对 Python 戏法办法对一个简略剖析,你可能会问,哪里能找到这些戏法办法的完好出处呢?咱们一般参阅的是 Python 的官方文档:
https://docs.python.org/zh-cn/3.7/reference/datamodel.html
但不幸的是,它们没有在一个一致的当地进行汇总,而是依据实践完结的功用被区分到了不同的章节,所以咱们自己平常要多留意堆集,多调查他人的代码中运用了哪些好用的魔法函数,并在自己的项目里多进行实践。
除了戏法办法,其实还有许多能够有用进步咱们 Python 代码质量的语法技巧,比方装修器、笼统基类、列表推导式、字典推导式等等。
我常常发现许多 Python 工程师工作了好几年,仍是在用一些很根底的言语特性来写代码,这样就导致他们的完结总是不行高雅,运转功率也不高,很快就碰到了工作上升的天花板。所以说,平常多花一些时刻多学习一些言语底层方面的常识和比较高阶的言语特性,对你的技术提高和工作生长是很有协助的。
因而,我和极客时刻协作,开设了一期 Python 进阶训练营,用 4 个实战项目串联起悉数要害常识,学完即可建立起系统的 Python 开发进阶常识系统,并独立完结完结一个杂乱的实战项目。
我会从一个简略的单线程爬虫事例开端,逐渐扩展为多线程爬虫,紧接着对收集到的数据进行存储、清洗、分词以及情感剖析,最终再经过 Web 直观地展现出来。学完后你不仅能把握一系列高阶开发技术,也为后续转向 Web 开发、数据剖析与处理、NLP、人工智能等范畴打下了坚实的根底。具体课程纲要如下:
(上下滑动检查)
上下滑动检查课程纲要
训练营还配套了哪些服务?
1. 4 天线下教育 + 5 次线上直播 + 7 周故意操练 + 助教每日答疑
教师将最要害的常识解说、项目实战演示都放在了 4 天线下课,手把手、面对面进行教育,在高度集中的时刻、空间中让你快速了解和把握中心常识,再经过 7 周线上实战作业帮你进行稳固。教师还会进行 5 次直播,回答你在实战操练中的疑难点,也会有助教一同,随时对你的问题和发展进行反应。
2. 高效学习社群 + 班主任带班,跟优异的人一同学习
为了帮你坚持完结 50 天的学习,你的专属班主任会打造一个合作、互相监督的班级社群,让你和来自不同公司的优异同伴一同学习。班主任会每周催促你学习,重视你的学习体会,不定期安排我们进行线上共享活动,让你在 50 天内始终保持学习动力。
3. 打开一线大厂和 TGO 鲲鹏会 600 多家企业面试直通车
优异结业生结业一年内,随时可取得极客大学供给的两次企业界推服务,更有 TGO 鲲鹏会的 600 家企业引荐通道为你打开,取得更多的工作发展机会。
怎么报名?
首期 Python 训练营将在北京线下小范围举行,限制人数,12 月 21 日开学。原价 3600,早鸟特惠 2499,线下大课及直播视频均可回看,早鸟仅限 100 人。欢迎你参加进来,跟我一同进阶 Python 技术。扫码参加
开学之前我还预备了一系列的免费公开课,期望协助更多人在运用 Python 时,能够愈加高效有质。公开课名额有限,仅限 500 人参加,你能够扫码增加学习助理,请求参加。
点击阅览原文,进阶 Python 高手,等待与你碰头。









