魔法方法推开Python进阶学习大门
热爱Python
Python是Guido van Rossum设计出来的让使用者觉得如沐春风的一门编程语言。2020年11月12日,64岁的Python之父宣布由于退休生活太无聊,自己决定加入Microsoft的DevDiv Team,致力于“确保更好地使用Python”。尽管在国内有些声音在Diss着Python,认为它太简单,只是个脚本语言,但是它的发明者对Python的热情,仍然激励着我们坚持对Python的热爱。
龟叔是所有编程语言发明者当中头发最多的这位。
奇迹时刻
collection.len()
是面向对象语言的写法,len(collection)
是Python语言的写法,这种风格叫做Pythonic
。从前者到后者,就像变魔术一样,一瞬间让人眼前一亮。这个魔术就是Python魔法方法,或者叫双下方法,它是用双下划线开头和双下划线结尾的特殊方法,比如obj[key]
,Python解释器实际上会转换成obj.__getitem__(key)
来运行,但是使用者并无感知。
__getitem__
和__len__
__getitem__
用来获取数据,__len__
用来返回长度,这2个魔法方法是Python基础,我们通过一副扑克牌来了解:
import collections
# 定义一副牌
Card = collections.namedtuple('Card', ['rank', 'suit'])
class FrenchDeck:
# 大小
ranks = [str(n) for n in range(2, 11)] + list('JQKA')
# 花色
suits = 'spades diamonds clubs hearts'.split()
def __init__(self):
# 生成一副牌
self._cards = [Card(rank, suit) for suit in self.suits
for rank in self.ranks]
def __len__(self):
return len(self._cards)
def __getitem__(self, position):
return self._cards[position]
本来我们对这副牌什么都不能做,但是由于实现了__len__
,可以使用len()
函数查看有多少张牌:
>>> len(deck)
52
由于实现了__getitem__
,可以使用中括号索引取值:
>>> deck[0]
Card(rank='2', suit='spades')
能进行切片:
>>> deck[:3]
[Card(rank='2', suit='spades'), Card(rank='3', suit='spades'), Card(rank='4', suit='spades')]
>>> deck[12::13]
[Card(rank='A', suit='spades'), Card(rank='A', suit='diamonds'), Card(rank='A', suit='clubs'), Card(rank='A', suit='hearts')]
能迭代:
>>> for card in deck: # doctest: +ELLIPSIS
... print(card)
Card(rank='2', suit='spades')
Card(rank='3', suit='spades')
Card(rank='4', suit='spades')
...
发现没有,魔法方法是可以用来装B的!别人写个类只能get
、set
,你写个类还能花式炫技,666。
Python魔法方法是给Python解释器使用的,一般不需要直接调用,Python会自己去调,比如把
len(my_object)
写成my_object.__len__()
,就弄巧成拙了。
魔法方法实现运算符
前面例子实现了取值和长度,接着再看一个例子,使用__repr__
、__abs__
、__bool__
、__add__
、__mul__
,实现运算符:
from math import hypot
# 二维向量
class Vector:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
# 表达式
def __repr__(self):
return 'Vector(%r, %r)' % (self.x, self.y)
# 绝对值
def __abs__(self):
return hypot(self.x, self.y)
# 布尔值
def __bool__(self):
return bool(abs(self))
# 加法
def __add__(self, other):
x = self.x + other.x
y = self.y + other.y
return Vector(x, y)
#乘法
def __mul__(self, scalar):
return Vector(self.x * scalar, self.y * scalar)
__add__
实现了加法:
>>> v1 = Vector(2, 4)
>>> v2 = Vector(2, 1)
>>> v1 + v2
Vector(4, 5)
__abs__
实现了绝对值:
>>> v = Vector(3, 4)
>>> abs(v)
5.0
__mul__
实现了乘法:
>>> v * 3
Vector(9, 12)
__repr__
实现了对象的字符串表示:
Vector(4, 5)
否则得到的字符串可能是
<Vector object at 0x10e100070>
。
__bool__
实现了布尔值:
if Vector(4, 5):
return True
其他魔法方法
一篇文章是讲不完魔法方法的,我们会在后续文章中,继续探讨如何使用和实现它们。
Tips
本小节内容是我看《流畅的Python》第一遍时记录的知识点:
collections.namedtuple
可以用来创建只有少数属性但没有方法的对象,比如beer_card = Card('7', 'diamonds')
random.choice
和random.sample
不一样的地方在于,sample是返回序列,choice是返回元素,当使用sample(list, 1)[0]的时候,不如直接使用choice(list)。特殊方法的存在是为了被Python解释器调用的。
PyVarObject是表示内存中长度可变的内置对象的C语言结构体。
list
或str
或bytearray
的__len__实际上返回的PyVarObject.ob_size属性,这个比调用一个方法要快的多。len之所以不是一个普通方法,是为了让python自带的数据结构可以走后门,abs也是同理。
很多时候调用__init__方法的目的是,在你自己的子类的__init__方法中调用超类的构造器。
abs,如果输入是整数或者浮点数,它返回的是输入值的绝对值;如果输入是复数,那么返回这个复数的模。
__repr__和__str__二选一的话,__repr__更好,因为如果一个对象没有__str__函数,解释器会用__repr__作为替代。
python对象的一个基本要求就是它得有合理的字符串表示形式,这就是数据模型中存在特殊方法__repr__和__str__的原因。
为了判定一个值x为真还是为假,python会调用bool(x),它的背后是调用x.__bool__()。如果不存在,就会调用x.__len__(),返回0为Flase,非0为True。
python通过运算符重载这一模式提供了丰富的数值类型,除了内置那些,还有
decimal.Decimal
和fractions.Fraction
。
小结
本文是Python进阶系列开篇,参考《流畅的Python》序章改写而成。原书内容有深度有广度,我选择了其中的魔法方法知识点,作为切入,循序渐进学习。其实书中这一章节的副标题是“数据模型”,它是个什么概念呢?
系列文章会不定期同步到在线电子书中,欢迎访问查看:
参考资料:
《流畅的Python》
魔法方法推开Python进阶学习大门的更多相关文章
- Python进阶学习之特殊方法实例详析
Python进阶学习之特殊方法实例详析 最近在学习python,学习到了一个之前没接触过的--特殊方法. 什么是特殊方法?当我们在设计一个类的时候,python中有一个用于初始化的方法$__init_ ...
- python进阶学习笔记(一)
python进阶部分要学习的内容: 学习目标: 1.函数式编程 1.1,什么是函数式编程 函数式编程是一种抽象计算的编程模式 不同语言的抽象层次不同: 函数式编程的特点: python支持的函数式编程 ...
- [python]进阶学习之阅读代码
起因 最近在公司的任务是写一些简单的运营工具,因为是很小的工具,所以就用了github上面的一个开源项目flask-admin,可以省去很多的事情. 但是,这个开源项目是个人维护的项目,所以文档相对简 ...
- python进阶学习(二)
本节学习图形用户界面 ------------------------ 本节介绍如何创建python程序的图形用户界面(GUI),也就是那些带有按钮和文本框的窗口.这里介绍wxPython : 下载地 ...
- python进阶学习(一)
同样是<python基础教程(第二版)>的内容,只是后面内容学起来,相比前面会比较有趣,也更加实用,所以,将"基础"改为"进阶". python 电 ...
- Python进阶学习_连接操作Redis数据库
安装导入第三方模块Redis pip3 install redis import redis 操作String类型 """ redis 基本命令 String set(n ...
- python进阶学习笔记(四)--多线程thread
在使用多线程之前,我们首页要理解什么是进程和线程. 什么是进程? 计算机程序只不过是磁盘中可执行的,二进制(或其它类型)的数据.它们只有在被读取到内存中,被操作系统调用的时候才开始它们的生命期.进程( ...
- python进阶学习(四)
在使用多线程之前,我们首页要理解什么是进程和线程. 什么是进程? 计算机程序只不过是磁盘中可执行的,二进制(或其它类型)的数据.它们只有在被读取到内存中,被操作系统调用的时候才开始它们的生命期.进程( ...
- python进阶学习(三)
本节通过SQLite了解数据库操作 ------------------------- 数据库支持 使用简单的纯文本只能实现有退限的功能,所需要引入数据库,完成更强大的功能,本节使用的简单数据库SQL ...
随机推荐
- 题解-SDOI2013 淘金
题面 SDOI2013 淘金 有一个 \(X\).\(Y\) 轴坐标范围为 \(1\sim n\) 的范围的方阵,每个点上有块黄金.一阵风来 \((x,y)\) 上的黄金到了 \((f(x),f(y) ...
- 惊天秘密!如何在 Flutter 项目中实现操作引导
不要冒然评价我,你只知道我的名字,却不知道我的故事,你只是听闻我做了什么,却不知我经历过什么. 俗话说得好,产品有三宝,弹窗浮层加引导. 上图截图自我司 App 晓黑板中的口算模块,相信每个 App ...
- Android之Activity启动流程详解(基于api28)
前言 Activity作为Android四大组件之一,他的启动绝对没有那么简单.这里涉及到了系统服务进程,启动过程细节很多,这里我只展示主体流程.activity的启动流程随着版本的更替,代码细节一直 ...
- 20201214-1 Json与pickle数据序列化
1-1 文件处理时,只能存字符串或者二进制 info = { 'name':'alex', 'age':22 } f = open("test.text", "w&quo ...
- 磁盘inode节点被占满的解决方法
问题: Linux服务器,查看日志发现程序无法继续写文件,但是用df -h查看磁盘容量还有剩余. 排查思路:怀疑是机器的inode节点被占满,使用df -i查看磁盘inode节点使用情况,果然是ino ...
- 面试 03-DOM事件的总结
03-DOM事件的总结 #前言 要学习事件的基础内容,请看先本人之前的基础文章: <04-JavaScript基础/27-事件对象Event> <04-JavaScript基础/28 ...
- Django 3.x 原生支持websocket 配置
websocket.py 1 # websocket.py 2 async def websocket_application(scope, receive, send): 3 while True: ...
- Spark性能调优篇六之调节数据本地化等待时长
数据本地化等待时长调节的优化 在项目该如何使用? 通过 spark.locality.wait 参数进行设置,默认为3s,6s,10s. 项目中代码展示: new SparkConf().set(&q ...
- 为什么 HashMap 的容量大小要设置为2的N次方?
原文链接:https://www.changxuan.top/?p=1208 前两天,我在一位同学提交中看到了下面这样的一行代码,让我很是惊讶. Map<String, String> t ...
- tkinter + 爬虫 实现影视在线资源系统
应吾爱朋友现公布代码如下: import tkinter as tk import requests,re,sys,asyncio from tkinter import scrolledtext,E ...