特殊方法的使用
什么是特殊方法,在Python中,如果一个方法的前后各有两个_
横线,例如__len__
方法,这样的方法就被称为特殊方法,有时也称为魔术方法
或双下划线方法
。
请不要将任意方法设置为特殊方法,不要乱用
__*__
。
文章介绍
- 通过一个纸牌对象介绍了__len__方法和__item__方法的使用
- 通过一个向量对象介绍了__repr__方法和一些数值操作的特殊方法
- 主要说明了特殊方法的一个执行方式和特殊方法一览表。
1. 一摞Python风格的纸牌
通过纸牌类来详细说明__getitem__和__len__特殊方法的使用
python
import collections
# 函数工厂,实现一个Card类,里面只用rank和suit这两个属性,没有其他方法
Card = collections.namedtuple('Card', ['rank', 'suit'])
class FrenchDeck:
# 生成2-10加JQKA的十四章牌面
rank = [str(n) for n in range(2, 11)] + list('JQKA')
# 生成 '黑桃','红桃','方片','梅花' 四种花色
# split没有参数时将以空格作为分隔符
suits = "heart spade diamonds clubs".split()
def __init__(self):
# 生成不包含大小王在内的52张卡片
self._cards = [Card(rank, suit) for rank in self.rank for suit in self.suits]
def __len__(self):
print("__len__方法被调用了")
return 52 # 必须返回一个integer,整数类型
def __getitem__(self, item):
# print(item)
print("__getitem__方法被调用了")
return self._cards[item] # 返回索引对应的内容
if __name__ == "__main__":
deck = FrenchDeck()
print("1.打印扑克牌数量")
print("扑克牌数量是:",len(deck))
print("-"*11)
print("2.获取牌堆中的第一张扑克牌")
print("牌堆中的第一张是:", deck[0])
print("-"*11)
print("3.获取牌堆中的第三到第十张牌")
# 如果此时打印__getitem__的item参数,会发现是slice(3, 11, None)这样的,这是在序列中进行切片操作的一个类
print("第三到第十张牌分别是:", deck[3:11])
print("-"*11)
print("4.遍历整个牌库")
for card in deck:
print(card)
print("-"*11)
print("5.判断某张牌是否在牌堆中")
card = Card('7', "heart")
# 会遍历牌堆,如果发现对应数据则终止,否则遍历完
print(card in deck)
cmd
1.打印扑克牌数量
__len__方法被调用了
扑克牌数量是: 52
-----------
2.获取牌堆中的第一张扑克牌
__getitem__方法被调用了
牌堆中的第一张是: Card(rank='2', suit='heart')
-----------
3.获取牌堆中的第三到第十张牌
__getitem__方法被调用了
第三到第十张牌分别是: [Card(rank='2', suit='clubs'), Card(rank='3', suit='heart'), Card(rank='3', suit='spade'), Card(rank='3', suit='diamonds'), Card(rank='3', suit='clubs'), Card(rank='4', suit='heart'), Card(rank='4', suit='spade'), Card(rank='4', suit='diamonds')]
-----------
4.遍历整个牌库
__getitem__方法被调用了
Card(rank='2', suit='heart')
__getitem__方法被调用了
Card(rank='2', suit='spade')
__getitem__方法被调用了
Card(rank='2', suit='diamonds')
__getitem__方法被调用了
Card(rank='2', suit='clubs')
__getitem__方法被调用了
Card(rank='3', suit='heart')
__getitem__方法被调用了
....
-----------
5.判断某张牌是否在牌堆中
__getitem__方法被调用了
__getitem__方法被调用了
__getitem__方法被调用了
__getitem__方法被调用了
__getitem__方法被调用了
__getitem__方法被调用了
__getitem__方法被调用了
....
True
2. 模拟数值类型之向量类
通过向量类来介绍关于数值操作的特殊方法和打印对象的__repr__特殊方法
python
import math
class Vector:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __repr__(self):
print("__repr__方法被调用了")
return f"Vector({self.x}, {self.y})" # 这里的返回内容必须是字符串
def __abs__(self):
print("__abs__方法被调用了")
return math.hypot(self.x, self.y)
def __bool__(self):
print("__bool__方法被调用了")
return self.x != 0 or self.y != 0
def __add__(self, other):
print("__add__方法被调用了")
print(f"self:{self}")
print(f"other:{other}")
return Vector(self.x + other.x, self.y + other.y)
def __mul__(self, other):
print("__mul__方法被调用了")
print(f"self:{self}")
print(f"other:{other}")
return Vector(self.x * other, self.y * other)
if __name__ == "__main__":
v1 = Vector(4, 8)
v2 = Vector(3, 4)
v3 = Vector(0, 0)
print("1.打印v1对象")
print(v1)
print("-"*20)
print("2.获取v2对象的绝对值")
print(abs(v2))
print("-"*20)
print("3.获取v3对象的真假")
print(bool(v3))
print("-"*20)
print("4.v1+v2")
v1 += v2
print(v1)
print("-"*20)
print("5.v2 * 5")
v2 *= 5
print(v2)
cmd
1.打印v1对象
__repr__方法被调用了
Vector(4, 8)
--------------------
2.获取v2对象的绝对值
__abs__方法被调用了
5.0
--------------------
3.获取v3对象的真假
__bool__方法被调用了
False
--------------------
4.v1+v2
__add__方法被调用了
__repr__方法被调用了
self:Vector(4, 8)
__repr__方法被调用了
other:Vector(3, 4)
__repr__方法被调用了
Vector(7, 12)
--------------------
5.v2 * 5
__mul__方法被调用了
__repr__方法被调用了
self:Vector(3, 4)
other:5
__repr__方法被调用了
Vector(15, 20)
3.关于特殊方法的一些细节
1.特殊方法是如何执行的
首先要明确一点,特殊方法是给python的
解释器
调用的,例如一般不会通过my_objct.__len__
的方式来调用__len__
方法,而是通过len(my_object)
来调用的(这里的my_object是你创建的对象)。其次特殊方法的调用是
隐式
的,例如,for i in my_object
,其实首先找的是my_object
对象中的__iter__
这个特殊方法,当对象中没有这个特殊方法时,才会去找__getitem__
这个特殊方法,只不过解释器将这个流程隐藏起来了,导致看起来遍历会直接调用__getitem__
一样。__str__
与__repr__
也是如此。
2. 特殊方法一览表
1. 特殊方法名称(不包含运算符)
分类 | 方法名称 |
---|---|
字符串(字节)表示形式 | repr str format bytes fspath |
转换为数值 | bool complex int float hash index |
模拟容器 | len getitem setitem delitem contains |
迭代 | iter aiter next anext reversed |
可调用对象或执行协程 | call await |
上下文管理 | enter exit aexit aenter |
构造和析构实例 | new init del |
属性管理 | getattr getattribute setattr delattr dir |
属性描述符 | get set delete set_name |
抽象基类 | instancecheck subclasscheck |
类元编程 | prepare init_subclass class_getitem mro_entries |
2.运算符的符合和背后的特殊方法
运算符分类 | 符号 | 特殊方法 |
---|---|---|
一元数值运算符 | - + abs | neg pos abs |
比较运算符 | < <= == != > >= | lt le eq ne gt ge |
算数运算符 | + - * / // % @ divmod() round() ** pow() | add sub mul truediv floordiv mod matmul divmod round pow |
反向算数运算符 | (交换算术运算符的操作数) | radd ... |
增量赋值算术运算符 | += -= *= /= //= %= @= **= | iadd isub imul itruediv ifloordiv imod imatmul ipow |
按位运算符 | & | ^ << >> ~ | and or xor lshift rshift invert |
反向按位运算符 | (交换按位运算符的操作数) | rand ... |
增量按位运算符 | &= ... | iand ... |