标准库中的 random.shuffle 函数用法如下:
![![在这里插入描述]失https://败(imblog.csdnig-mg.cn/20190819232644152.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01aUF9tYW4=,size_16,color_FFFFFF,t_70)这https://im,g-blog.csdnimg.cn/20190819232644152.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01aUF9tYW4=,size_16,color_FFFFFF,t_70)]上](https://i-blog.csdnimg.cn/blog_migrate/f2e7bdce3214b9349921b0317bf5093f.png)
上面是对python内置的list 进行打乱的做法,下面我们来说明用户自定义的类如何实现打乱对象的做法呢,其实很简单只要自定义的类满足对应(列表)的接口的协议就可以啦!
import collections
from random import shuffle
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]
if __name__ == "__main__":
deck = FrenchDeck()
print(shuffle(deck))
运行结果:

这个问题的原因是,shuffle 函数要调换集合中元素的位置,而 FrenchDeck 只实现了不可变的序列协议。可变的序列还必须提供 __setitem__ 方法。
下面代码中添加了__setitem__ 方法。
import collections
from random import shuffle
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]
def __setitem__(self, key, value):
self._cards[key] = value
if __name__ == "__main__":
deck = FrenchDeck()
shuffle(deck)
for i in deck:
print(i)
运行结果:

这里就可以实现对自定义的类顺序进行打乱的做法。
当然python是动态语言,因此可以在运行的过程中进行中添加代码,比如我们先定义一个函数:
def set_card(deck, position, card):
deck._cards[position] = card
然后将函数赋值给 FrenchDeck 类的 __setitem__ 属性。
FrenchDeck.__setitem__ = set_card
这样也能实现了,语言 参考中使用的参数是 self、key 和 value,而这里使用的是 deck、position 和 card。这么 做是为了告诉你,每个 Python 方法说到底都是普通函数,把第一个参数命名为 self 只是 一种约定。在控制台会话中使用那几个参数没问题,不过在 Python 源码文件中最好按照文 档那样使用 self、key 和 value。
这里的关键是,set_card 函数要知道 deck 对象有一个名为 _cards 的属性,而且 _cards 的 值必须是可变序列。然后,我们把 set_card 函数赋值给特殊方法 __setitem__,从而把它 依附到 FrenchDeck 类上。这种技术叫猴子补丁:在运行时修改类或模块,而不改动源码。 猴子补丁很强大,但是打补丁的代码与要打补丁的程序耦合十分紧密,而且往往要处理隐 藏和没有文档的部分。
除了举例说明猴子补丁之外,示例还强调了协议是动态的:random.shuffle 函数不关 心参数的类型,只要那个对象实现了部分可变序列协议即可。即便对象一开始没有所需的方法也没关系,后来再提供也行。
本文介绍如何使用Python的random.shuffle函数对自定义类的对象列表进行随机打乱,通过在自定义类中实现__setitem__方法或使用猴子补丁技术实现。同时,文章解释了shuffle函数对对象的要求及其实现原理。
1万+

被折叠的 条评论
为什么被折叠?



