自定义序列​

自定义序列​

自定义序列 ​有很多方法可以让你的 Python 类表现得像内置序列(dict、tuple、list、str等)。这些是迄今为止我在 Python 中最喜欢的魔法方法,因为它们为您提供了荒谬的控制程度,以及它们神奇地使整个全局函数数组在您的类实例上完美运行的方式。但在我们开始讨论好东西之前,先简单介绍一下需求。

要求 ​现在我们正在讨论在 Python 中创建自己的序列,是时候讨论 协议 了。协议有点类似于其他语言中的接口,因为它们为您提供了一组您必须定义的方法。然而,在 Python 中,协议是完全非正式的,不需要显式声明来实现。相反,它们更像是指导方针。

为什么我们现在谈论协议?因为在 Python 中实现自定义容器类型涉及使用其中一些协议。首先,有定义不可变容器的协议:要创建不可变容器,您只需要定义__len__ 和__getitem__(稍后会详细介绍)。可变容器协议需要不可变容器所需的一切,__setitem__加上__delitem__. 最后,如果你希望你的对象是可迭代的,你必须定义__iter__ ,它返回一个迭代器。该迭代器必须符合迭代器协议,该协议要求迭代器具有称为__iter__(返回自身)和next.

容器背后的魔力 ​以下是容器使用的神奇方法:

__len__(self)

返回容器的长度。不可变和可变容器协议的一部分。

__getitem__(self, key)

使用符号 定义访问项目时的行为self[key]。这也是可变和不可变容器协议的一部分。它还应该引发适当的异常:TypeError 如果键的类型错误并且键KeyError没有对应的值。

__setitem__(self, key, value)

使用符号 定义分配项目时的行为self[nkey] = value。这是可变容器协议的一部分。KeyError同样,你应该TypeError在适当的时候加注。

__delitem__(self, key)

定义删除项目时的行为(例如del self[key])。这只是可变容器协议的一部分。使用无效密钥时,您必须引发适当的异常。

__iter__(self)

应该返回容器的迭代器。迭代器在许多上下文中返回,最显着的是通过iter()内置函数以及使用 form 循环容器时for x in container:。迭代器是它们自己的对象,它们还必须定义一个__iter__返回的方法self。

__reversed__(self)

调用以实现reversed()内置函数的行为。应该返回序列的反转版本。仅当序列类是有序的时才实现这一点,例如列表或元组。

__contains__(self, item)

__contains__ in使用和定义成员资格测试的行为 not in 。你问,为什么这不是序列协议的一部分?因为当 __contains__ 没有定义,Python 只是遍历序列并 True 在遇到它正在寻找的项目时返回。

__missing__(self, key)

__missing__用于 的子类 dict 。它定义了每当访问字典中不存在的键时的行为(例如,如果我有一本字典 d 并说 d["george"] 何时 "george" 不是字典中的键,d.__missing__("george")将被调用)。

一个例子

对于我们的示例,让我们看一个列表,该列表实现了一些您可能习惯于从其他语言(例如 Haskell)中使用的功能结构。

pythonclass FunctionalList:

"""A class wrapping a list with some extra functional magic, like head, tail, init, last, drop, and take."""

def __init__(self, values=None):

if values is None:

self.values = []

else:

self.values = values

def __len__(self):

return len(self.values)

def __getitem__(self, key):

# if key is of invalid type or value, the list values will raise the error

return self.values[key]

def __setitem__(self, key, value):

self.values[key] = value

def __delitem__(self, key):

del self.values[key]

def __iter__(self):

return iter(self.values)

def __reversed__(self):

return reversed(self.values)

def append(self, value):

self.values.append(value)

def head(self):

# get the first element

return self.values[0]

def tail(self):

# get all elements after the first

return self.values[1:]

def init(self):

# get elements up to the last

return self.values[:-1]

def last(self):

# get last element

return self.values[-1]

def drop(self, n):

# get all elements except first n

return self.values[n:]

def take(self, n):

# get first n elements

return self.values[:n]123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354你有它,一个(勉强)有用的例子,说明如何实现你自己的序列。当然,自定义序列还有更多有用的应用,但其中不少已经在标准库中实现,如Counter、OrderedDict 和NamedTuple.

案例:自定义扑克牌 ​python# 定义扑克牌

class Card:

def __init__(self, rank, suit):

self.rank = rank

self.suit = suit

def __repr__(self):

return f'<{self.suit}-{self.rank}>'

# 定义一幅扑克牌

class FrenchDeck:

ranks = [str(n) for n in range(2, 11)] + list('JQKA')

suits = '黑桃 方块 梅花 红桃'.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]

if __name__ == '__main__':

beer_card = Card('7', '方块')

print(beer_card.rank, beer_card.suit)

deck = FrenchDeck()

print(len(deck))

print(deck[0].suit, deck[0].rank)

print(deck[0:-1])

from random import choice

print(choice(deck))12345678910111213141516171819202122232425262728293031323334353637383940

相关推荐

魔兽世界9.0鸟德橙装怎么选择
365被限制了让提款

魔兽世界9.0鸟德橙装怎么选择

08-25 👁️ 3326
探索《地下城与勇士》中,哪款龙系宠物外观设计最吸睛且独具魅力
蹬自行车动作最能减哪个部位,一文解析
365商城官网

蹬自行车动作最能减哪个部位,一文解析

07-23 👁️ 4741
甘蔗是碳水化合物,有哪些营养价值?(探究甘蔗的营养成分及健康益处)
中国人民银行公告〔2024〕第12号
365彩票还能玩吗

中国人民银行公告〔2024〕第12号

08-28 👁️ 6560
换主板要多久?详细拆解及实用指南
365被限制了让提款

换主板要多久?详细拆解及实用指南

07-11 👁️ 4854
屏风 的起源可以追溯到西周初期
365商城官网

屏风 的起源可以追溯到西周初期

07-10 👁️ 5979
cad怎么随意缩放?cad设置自由无限缩放教程
365彩票还能玩吗

cad怎么随意缩放?cad设置自由无限缩放教程

09-04 👁️ 3684
人物服装草帽绘画教程
365彩票还能玩吗

人物服装草帽绘画教程

07-24 👁️ 1647