zoukankan      html  css  js  c++  java
  • 《流畅的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
    

       也不说什么脏话了,反正没什么成就感,这些东西耗费我整整一天时间啊,就这本书作者随手扔出来的一些小玩意,搞得我很不堪,

    这就是传说当中的以我之短击他之长吧,法克!但还是会有不服,别人能会,我为什么嫌难,人总是活在矛盾中。总之,以后尽量不搞这些东西了。

  • 相关阅读:
    Luogu3952 NOIP2017D1T2 时间复杂度
    Luogu4933 大师
    Luogu1966 火柴排队
    Luogu2881 排名的牛Ranking the Cows
    Luogu1439 最长公共子序列(LCS)
    Liferay7 BPM门户开发之20: 理解Asset Framework
    提高Liferay7的启动和运行速度
    liferay中jsonws的认证方法
    让Liferay的Service Builder连接其他数据库
    Liferay表结构介绍(四):Portlet相关表
  • 原文地址:https://www.cnblogs.com/fawaikuangtu123/p/9768130.html
Copyright © 2011-2022 走看看