Python笔记001-类的特殊方法
Python笔记001-类的特殊方法
以下是我学习《流畅的Python》后的个人笔记,现在拿出来和大家共享,希望能帮到各位Python学习者。
首次发表于: 微信公众号:科技老丁哥,ID: TechDing,敬请关注。
本篇主要知识点:
类的特殊方法(一般都在前后带有两个下划线,比如__len__和__getitem__),其存在的目的是被Python解释器调用,而不是类的对象来调用。
对于自定义的类,一般无法体现出Python语言的核心特性,比如迭代和切片等,但是可以通过在自定义类中复写__len__和__getitem__等特殊方法来实现这些核心功能。对象调用len()方法时实际上Python解释器调用的是自定义类中的__len__,而对某个对象进行切片获取元素,或者排序时,Python解释器调用的是复写的__getitem__。
在自定义类中复写__getitem__至少可以获得1.类对象的切片和获取元素,2.对类对象进行迭代,可以是顺序迭代也可以是逆序迭代,3.对类对象进行重新排序,4.对类对象进行随机抽样等多种功能。
类的特殊方法有多达100种,作为示例,此处仅仅总结了加减乘除等的复写方法,并将所有特殊方法都整理成相关表格,便于后续复写某些特殊方法时作为参考。
1. 特殊方法示例
这些特殊方法长得比较奇怪,那是因为它提醒用户,不要轻易调用这些方法,因为它基本上是Python解释器专用。
比如下面先建立一整副纸牌,它包含有一个属性_cards,这个一个list,包含有52个Card对象。在复写了__len__和__getitem__方法之后,就可以直接获取到这个_cards属性的总长度和对其进行排序,切片来获取某几张纸牌。
from collections import namedtuple Card=namedtuple('Card', ['rank', 'suit']) # 单张纸牌类 class FrenchDeck: # 一副纸牌,包含4种花色,每种13种数字 ranks = [str(n) for n in range(2, 11)] + list('JQKA') # 13种数字 suits = 'spades diamonds clubs hearts'.split() # 四种不同花色:黑桃,方块,梅花,红桃 def __init__(self): self._cards = [Card(rank, suit) for suit in self.suits for rank in self.ranks] # _cards属性是所有纸牌的集合,一个list, 包含4种花色共52张牌 def __len__(self): # 类FrechDeck的特殊方法:会计算所有纸牌的张数,即52 return len(self._cards) def __getitem__(self, position): # 类FrechDeck的特殊方法:从52张牌获取第position张牌 return self._cards[position]
构建一副纸牌后,我们想知道里面有多少张牌,或者想知道第1张,第10张,最后一张纸牌分别是什么:
## 通过len()获取deck中一共有多少张牌 deck=FrenchDeck() print(len(deck)) # 52 ## 通过切片方法获取某个位置的牌 print(deck[0]) # Card(rank='2', suit='spades') print(deck[10]) # Card(rank='Q', suit='spades') print(deck[-1]) # Card(rank='A', suit='hearts')
如果没有复写__len__函数,在
len(deck)时会报错:
TypeError: object of type 'FrenchDeck' has no len().
同理,如果没有复写__getitem__方法,在
deck[0]时报错:
TypeError: 'FrenchDeck' object does not support indexing
可以对deck进行索引来获取某一个位置的纸牌,那么也就可以通过切片来获取一个批次,符合一定条件的纸牌,比如下面获取最后三张牌和牌面全是A的牌。
print(deck[-3:]) # 获取最后三张牌 print(deck[12::13]) # 获取全是A的牌,间隔型切片
同理,复写了__getitem__方法后,就可以对deck对象进行迭代,不管是顺序迭代还是逆序迭代都可以。
# 顺序迭代 for card in deck[:10]: # 只打印最前面的10张牌 print(card) # 逆序迭代 for card in reversed(deck[-10:]): # 只打印最后面的10张牌 print(card)
还可以判断某张牌是否存在于该副牌内:
## 判断某张牌是否存在于该副牌内: print(Card('Q', 'hearts') in deck) # True print(Card('Z', 'clubs') in deck) # False
有了__getitem__方法,就可以对所有纸牌进行排序操作,此处我们排序的规定是:梅花2最小,方块2次之,红桃2,黑桃2,梅花3.。。这种形式,所以要自定义一个排序方法,这个方法返回一个整数,代表每张牌的大小,那么最小的梅花2就是0,最大的黑桃A就是51。
## 使用自定义方法对所有纸牌进行重新排序 suit_values = dict(spades=3, hearts=2, diamonds=1, clubs=0) # 将花色转换为数值 def spades_high(card): rank_value = FrenchDeck.ranks.index(card.rank) # 获取某张牌的数字 return rank_value * len(suit_values) + suit_values[card.suit] # 将某张牌的数字和花色换成整数返回 for card in sorted(deck, key=spades_high): # 按照自定义方法spades_high得到的整数进行排序 print(card)
小结一下:自定义类通过复写__getitem__方法,可以获得多种原来不存在的功能:比如切片功能,索引功能,还可以判断某个对象是否存在于类对象列表中,还可以对类对象进行迭代操作和排序操作。
2. 特殊方法的使用
对于自定义的类型,使用这些特殊方法可以使得他们的表现具有某些Python核心功能,在你对这些自定义类型进行操作时,比如
len(deck)时,Python解释器会去找deck类的__len__方法。
而Python内置的类型,比如list, str, tuple等,Python解释器则直接抄近路,会直接返回PyVarObject里的Ob_size属性,这是一个长度可变的C语言结构体,所以速度要比调用__len__方法快得多。
你自己对这些特殊方法的使用频率会很低,因为都是Python解释器自动调用,只有类定义时的__init__方法例外。
2.1 自定义加减乘除功能
class Person: def __init__(self,name,age,score): self.name=name self.age=age self.score=score def __add__(self,other): # 此处仅仅是将得分相加 return self.score+other.score def __sub__(self,other): # 此处将得分相减 return self.score-other.score def __mul__(self,other): # 此处将两个的年龄相乘 return self.age*other.age def __truediv__(self,other): # 将两个的得分相除 return self.score/other.score
这个自定义类Person复写了加减乘除方法,根据需要对里面的属性进行算术操作,那么就可以用符号
+,-,*,/等进行操作,比如:
P1=Person('Jack',20,90) P2=Person('Rose',18,60) print(P1+P2) # 150 print(P1-P2) # 30 print(P1*P2) # 360 print(P1/P2) # 1.5
2.2 自定义print后的形式
还有一个非常常用的特殊函数:
__repr__,它决定了print被直接调用后结果表现形式。
class Person: def __init__(self,name,age,score): self.name=name self.age=age self.score=score def __repr__(self): return 'Person(name={},age={},score={})'.format(self.name,self.age,self.score) P1=Person('Jack',20,90) print(P1) # Person(name=Jack,age=20,score=90)
如果没有复写
__repr__,在用
print(P1)时,会得到内存地址信息,人眼无法判断出具体内容,复写之后,就可以按照我们想要的形式直接print。
3. 特殊方法汇总
Python内置的特殊方法有将近一百种,其中有很多是实现算数运算,位运算和比较操作的,下面将这些方法的意义总结如下:
下面的整理于:CSDN: Python中特殊方法的分类与总结
所以,如果自定义类想实现某方面的功能,可以参考上面的表格来逐一实现即可。
首次发表于: 微信公众号:科技老丁哥,ID: TechDing,敬请关注。
本文所有代码都已经上传到我的github,欢迎下载
参考资料:
《流畅的Python》,Luciano Ramalho (作者) 安道 , 吴珂 (译者)。
CSDN:Python中特殊方法的分类与总结
- Python学习笔记1:数据模型和特殊方法(魔术方法)
- Python深入学习笔记1-特殊方法与多范式
- Python学习笔记——特殊方法
- Python学习笔记(二)——特殊方法(续)
- Python学习笔记(二)——特殊方法
- Python学习笔记1:数据模型和特殊方法(魔术方法)
- python深入笔记--特殊方法\上下文管理\__dict__\property()
- 【python学习笔记】Python自带特殊方法一览
- Python学习笔记(九)——Python _init_特殊方法和模块
- 15. Python脚本学习笔记十五特殊方法、属性和迭代器
- Python学习笔记五(面向对象-类-方法-属性)
- python中实现定制类的特殊方法总结
- NetworkX学习笔记-3-Python中输出Gephi文件格式的方法
- <<Python基础教程>>学习笔记 | 第09章 | 魔法方法、属性和迭代器
- python对象特殊方法列表
- Python类的__getitem__和__setitem__特殊方法
- Python(私有变量)类中的特殊方法
- Python学习笔记--子类调用父类方法
- 简明python教程学习笔记之九-str类方法简单介绍
- Python学习笔记——部分常用/特殊用法