《流暢的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

   也不說什么臟話了,反正沒什么成就感,這些東西耗費我整整一天時間啊,就這本書作者隨手扔出來的一些小玩意,搞得我很不堪,

這就是傳說當中的以我之短擊他之長吧,法克!但還是會有不服,別人能會,我為什么嫌難,人總是活在矛盾中。總之,以后盡量不搞這些東西了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM