《流畅的Python》一副扑克牌中的难点
1.现在在看《流畅的Python》这本书,看了三页就发现,这本书果然不是让新手来入门的,一些很常见的知识点能被这个作者玩出花来,
唉,我就在想,下面要分析的这些的代码,就算我费劲巴拉的看懂了,又有什么用呢,我其实不想靠着技术吃饭,但是现在在这个岗位上,
就得在其位谋其职,悲哀。我在敲代码方面也没什么天赋,也没什么热情,老话说得好,‘女怕嫁错郎,男怕入错行’,
所以我得从IT行业抽出身来。anyway,进入正题。
2.python解释器碰到特殊的句法时,会使用特殊方法去激活一些基本的对象操作,这些特殊方法的名字以两个下划线开头,
以两个下划线结尾(例如__getitem__)。比如obj[key]的背后就是__getitem__方法,为了求得my_collection[key]的值,解释器实际上会调用my_collection.__getitem__(key),双下划线这种特殊方法也叫双下方法(dunder method).
import collections
Card = collections.namedtuple('Card',['rank','suit'])
class FrenchDeck:
ranks = [str(n) for n in range(2,11)] + list('JQKA')
# ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']
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):
# print("用的是我写的lenth")
# 如果不写这个方法,用len(deck)时会报错:TypeError: object of type 'FrenchDeck' has no len()
return len(self._cards)
def __getitem__(self, position):
return self._cards[position]
>>> beer_card = Card('7','hearts') >>> beer_card Card(rank='7', suit='hearts') 这张牌就是红心7
FrenchDeck类中的self._cards生成了一个有52张'牌'的列表 [Card(rank='2', suit='spades'), Card(rank='3', suit='spades'), ...共52个] suits = ['spades', 'diamonds', 'clubs', 'hearts'] namedtuple的作用: >>> beer_card = Card('7','hearts') >>> beer_card Card(rank='7', suit='hearts') FrenchDeck类支持的方法: >>> deck = FrenchDeck() >>> len(deck) 52 >>> from random import choice >>> choice(deck) 支持分片:只看牌面是A的牌--先抽出索引是12的那张牌,然后每隔13张拿一张 >>> deck[12::13] [Card(rank='A', suit='spades'), Card(rank='A', suit='diamonds'), Card(rank='A', suit='clubs'), Card(rank='A', suit='hearts')] 这个我觉得作者的用法很好 仅仅用__getitem__方法,使这一摞牌变成可迭代 >>> for card in deck: ... print(card) 反向迭代 >>> for card in reversed(deck): ... print(card) 一个集合类型如果没有实现__contains__方法,in运算符就会按顺序做一次迭代搜索 >>> Card(rank='A', suit='spades') in deck True
下面这段就厉害了,弄得我眼黑头晕的,弄了一早上才搞明白,怎么对这些扑克牌进行排序?
首先你会想到sorted,但我连它的key参数都不会用,何谈去排序这些扑克。sorted用法:
sorted最简单用法是对元素全部是数字的列表排序,另一种用法是对元素是元组或字典的列表排序
用到的参数是key,比如:对学生的分数进行排序:
list1 = [('david', 90), ('mary',90), ('sara',80),('lily',95)]
sorted(list1, key=lambda x: x[0]) # 按照元组中第一个元素排序
sorted(list1, key=lambda x: x[1]) # 按照元组中第二个元素排序
array = [{"age":20,"name":"a"},{"age":25,"name":"b"},{"age":10,"name":"c"}]
array1 = sorted(array,key=lambda x:x["age"]) # 按照年龄排序
下面要讲的例子,先抛个砖:
1.需求:将列表中的元素按照绝对值大小进行升序排列
list1 = [3,5,-4,-1,0,-2,-6]
sorted(list1, key=lambda x: abs(x))
当然,也可以如下:
list1 = [3,5,-4,-1,0,-2,-6]
def get_abs(x):
return abs(x)
sorted(list1,key=get_abs)
只不过这种方式的代码看起来不够Pythonic
2、应用在闭包中
def get_y(a,b):
return lambda x:ax+b
y1 = get_y(1,1)
y1(1) # 结果为2
当然,也可以用常规函数实现闭包,如下:
def get_y(a,b):
def func(x):
return ax+b
return func
y1 = get_y(1,1)
y1(1) # 结果为2
只不过这种方式显得有点啰嗦。
那么是不是任何情况下lambda函数都要比常规函数更清晰明了呢?
Python之禅中有这么一句话:Explicit is better than implicit(明了胜于晦涩),就是说那种方式更清晰就用哪一种方式,不要盲目的都使用lambda表达式。
扑克牌排序代码:
suit_values = dict(spades=3, hearts=2, diamonds=1, clubs=0)
# {'clubs': 0, 'diamonds': 1, 'spades': 3, 'hearts': 2}
def spades_high(card):
rank_values = FrenchDeck.ranks.index(card.rank)
return rank_values * len(suit_values) + suit_values[card.suit]
for card in sorted(deck,key=spades_high):
print(card)
一、这段代码我看了很长时间,这本书的作者竟然将这段代码放在书的第二页,我也是醉了,我觉得是挺难的,但不管怎样,硬着头皮上吧!
扑克牌排序规则:
用点数来判断扑克牌的大小,2最小,A最大;同时还要加上对花色的判断:
黑桃最大,红桃次之,方块再次,梅花最小。那么梅花2的大小是0,黑桃A是51.
分析一下这个函数:sorted(deck,key=spades_high),我在这里卡了很长时间,所以就有了上面的那块砖,先说一下这个函数是怎么运行的吧:
通过sorted将deck这一摞牌和排序函数spades_high()联系起来,每张牌都有数字,先获取牌面的数字在FrenchDeck.ranks中的索引,即:2的索引0,
3的索引1,4的索引2,类推3,4,5,6,7,8,9,10,11,A的索引12,这张牌多大取决于它的花色,即索引 乘 4 加 花色值,
二、spades_high(card),这里的card就是传进来的每一张牌,rank_values = FrenchDeck.ranks.index(card.rank),
获取这张纸牌(即纸牌的数字)在FrenchDeck.ranks中的索引,['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']
"最后返回这个牌的得分",rank_values * len(suit_values) + suit_values[card.suit],此时就讲完了么?
灵光一闪,这个函数返回了所有得分有什么用?sorted()拿到这些得分,怎么把牌排好序?我觉得是这样:
在得到了[所有得分]这个列表之后,进行排序,得到:[0,1,2,3,4,...51],
然后将原扑克列表中的元素,一一对应,是怎么对应的?函数在处理的时候应该记录了每张卡的得分,所以能对应上。
如下(原来的扑克列表可不是这样的,排好序的应该是重新开辟了一个内存空间):
[Card(rank='2', suit='clubs'),Card(rank='2', suit='diamonds'),Card(rank='2', suit='hearts'),...Card(rank='A', suit='spades')]
三、比如上面第一块砖:
list1 = [3,5,-4,-1,0,-2,-6]
def get_abs(x):
return abs(x)
sorted(list1,key=get_abs)
函数先返回[3,5,4,1,0,2,6],然后排序[0,1,2,3,4,5,6],接着将list1中的每个元素,与排序后的列表对应,
0不变,1、2变成-1、-2,3不变,4变成-4,5不变,6变成-6,输出[0,-1,-2,3,-4,5,-6]
四、看不懂的lambda,只是把这两道题放在在这里提醒我,不要迷进去:
一道面试题: list1=[7, -8, 5, 4, 0, -2, -5] 要求1.正数在前负数在后 2.整数从小到大 3.负数从大到小 解题思路:先按照正负排先后,再按照大小排先后 list1=[7, -8, 5, 4, 0, -2, -5] print(sorted(list1,key=lambda x:(x<0,abs(x)))) [7, 5, 4, 0, -8, -2, -5] [0, 4, 5, 7, -2, -5, -8] 一个经典的复杂例子 这是一个字符串排序,排序规则:小写<大写<奇数<偶数 s = ‘asdf234GDSdsf23’ #排序:小写-大写-奇数-偶数 print(“”.join(sorted(s, key=lambda x: (x.isdigit(),x.isdigit() and int(x) % 2 == 0,x.isupper(),x)))) 原理:先比较元组的第一个值,FALSE
也不说什么脏话了,反正没什么成就感,这些东西耗费我整整一天时间啊,就这本书作者随手扔出来的一些小玩意,搞得我很不堪,
这就是传说当中的以我之短击他之长吧,法克!但还是会有不服,别人能会,我为什么嫌难,人总是活在矛盾中。总之,以后尽量不搞这些东西了。