美文网首页
fluent python-第 1 章 1.1 纸牌计算/nam

fluent python-第 1 章 1.1 纸牌计算/nam

作者: 时间之友 | 来源:发表于2017-11-30 16:01 被阅读64次

    纸牌计算-巧妙的利用类

    基础知识 内建模块 collections

    collections是Python内建的一个集合模块,提供了许多有用的集合类。
    namedtuple

    我们知道tuple可以表示不变集合,例如,一个点的二维坐标就可以表示成:

    >>> p = (1, 2)
    

    但是,看到(1, 2),很难看出这个tuple是用来表示一个坐标的。

    定义一个class又小题大做了,这时,namedtuple就派上了用场:

    >>> from collections import namedtuple
    >>> Point = namedtuple('Point', ['x', 'y'])
    >>> p = Point(1, 2)
    >>> p.x
    1
    >>> p.y
    2
    

    namedtuple是一个函数,它用来创建一个自定义的tuple对象,并且规定了tuple元素的个数,并可以用属性而不是索引来引用tuple的某个元素。

    这样一来,我们用namedtuple可以很方便地定义一种数据类型,它具备tuple的不变性,又可以根据属性来引用,使用十分方便。

    可以验证创建的Point对象是tuple的一种子类:

    >>> isinstance(p, Point)
    True
    >>> isinstance(p, tuple)
    True
    

    类似的,如果要用坐标和半径表示一个圆,也可以用namedtuple定义:

    # namedtuple('名称', [属性list]):
    Circle = namedtuple('Circle', ['x', 'y', 'r'])
    


    deque

    使用list存储数据时,按索引访问元素很快,但是插入和删除元素就很慢了,因为list是线性存储,数据量大的时候,插入和删除效率很低。

    deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈:

    >>> from collections import deque
    >>> q = deque(['a', 'b', 'c'])
    >>> q.append('x')
    >>> q.appendleft('y')
    >>> q
    deque(['y', 'a', 'b', 'c', 'x'])
    

    deque除了实现list的append()pop()外,还支持appendleft()popleft(),这样就可以非常高效地往头部添加或删除元素。

    除此之外,在collections里还包含以下常用的模块,如:
    defaultdict(用于dict,当key不存在时,能返回一个默认值,而不是报错)
    OrderedDict(如果要保持Key的顺序,可以用OrderedDict)
    Counter(是一个简单的计数器,例如,统计字符出现的个数)



    设计一副扑克牌

    # 1.1 python风格的纸牌
    import collections
    from random import choice   # 内置的随机选元素模块
    
    Card = collections.namedtuple('Card', ['rank', 'suit'])  # 一个元组(点数,花色)
    
    class FrenchDeck:
        ranks = [str(n) for n in range(2,11) ] + list('JQKA')   # list('123')结果就是['1','2','3'],本质上为2个list相加
        suits = 'spades diamonds clubs hearts'.split()   # 若split('s') 结果 ['', 'pade', ' diamond', ' club', ' heart', '']
        # ranks从2~A,suits:红桃,红方,黑方,黑花
        def __init__(self):    # __init__ 用法值得关注,类实例的值就是cardss,实例的属性
            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]
    
    # 有了 spades_high 函数, 就能对这摞牌进行升序排序了
    suit_values = dict(spades=3, hearts=2,diamonds=1,clubs=0)
    def spades_high( card):
        rank_value = FrenchDeck.ranks.index(card.rank)
        return rank_value * len(suit_values) + suit_values[card.suit]
    
    
    deck = FrenchDeck()
    print(  len(deck)  )  # 结果52;上方的self._cards在for in 中选,4*(9+4)=52种组合
    
    print(  deck.__getitem__(0)  )   # 结果:Card(rank='2', suit='spades')
    print( deck[0] )    # 结果:同上,红桃2是第一张牌
    print(  choice(deck)  )   # 表示随机选牌,如 Card(rank='3', suit='clubs')
    
    # 查看一副牌最上面的3张
    print( deck[:3] )
    # >>>[Card(rank='2', suit='spades'), Card(rank='3', suit='spades'), Card(rank='4', suit='spades')]
    # 从第12张牌开始拿,后面每隔13抽一张,开头有个0,实际就是隔13抽一张,结果抽了4个A
    print(  deck[12::13]  )   # <class '__main__.FrenchDeck'>;该类无须外部输入参数,封装好,自己调用自己的参数就可以实现模拟扑克,高手
    # >>> [Card(rank='A', suit='spades'), Card(rank='A', suit='diamonds'), Card(rank='A', suit='clubs'), Card(rank='A', suit='hearts')]
    print(type(FrenchDeck()))
    # 按花色排序,打印出所有的牌
    for card in deck:
        print(card)
    
    # 对牌进行升序排序
    for card in sorted(deck, key = spades_high):
        print(card)
    # >>> 结果是2,2,2,2,3,3,3,3...这样排序下去的
    

    虽然 FrenchDeck 隐式地继承了 object 类, 但功能却不是继承而来的。 我们通过数据模型和一些合成来实现这些功能。 通过实现 len__getitem__这两个特殊方法, FrenchDeck 就跟一个 Python 自有的序列数据类型一样, 可以体现出 Python 的核心语言特性( 例如迭代和切片) 。 同时这个类还可以用于标准库中诸如random.choice、 reversed 和 sorted 这些函数。 另外, 对合成的运用使得__len____getitem__ 的具体实现可以代理给 self._cards这个 Python 列表( 即 list 对象)


    python中的find、rfind、index、rindex

    find()从左向右寻找子序列的位置,如存在多个相同子序列只返回第一个查找到的位置,如果子序列不存在返回-1

    rfind()从右向左寻找子序列的位置.....

    index()从左向右寻找子序列的位置,如果子序列不存在报错,所以一般我们用find()更好一些

    rindex()从右向左寻找子序列的位置.....

     a = "hello world"
     a1 = a.find("l")
     a2 = a.rfind("v")
     print(a1)
     print(a2)
    # 输出结果:
    2
    -1
    
     b = "hello world"
     b1 = b.index("l")
     b2 = b.rindex("v")
     b3 = b.index('e')
     print(b1)
     print(b2)
     print(b3)
    # 输出结果:
    Traceback (most recent call last):
      File "lianxi.py", line 3, in <module>
        b2 = b.index("v")
    ValueError: substring not found
     1
    

    Python中__init__方法注意点

    注意1__init__并不相当于C#中的构造函数,执行它的时候,实例已构造出来了。

    class A(object):
        def __init__(self,name):
            self.name=name
        def getName(self):
            return 'A '+self.name
    

    当我们执行

    a=A('hello')
    

    时,可以理解为

    a=object.__new__(A)
    A.__init__(a,'hello')
    

    __init__作用是初始化已实例化后的对象。

    注意2、子类可以不重写__init__,实例化子类时,会自动调用超类中已定义的__init__

    class B(A):    # 🐱B继承了A后,虽然类B表面上看无须传参,实际上必须传参,因为继承了A类
        def getName(self):
            return 'B '+self.name
     
    if __name__=='__main__':
        b=B('hello')
        print b.getName()
    # 打印结果: B hello 
    

    但如果重写了__init__,实例化子类时,则不会隐式的再去调用超类中已定义的__init__

    class C(A):
        def __init__(self):
            pass
        def getName(self):
            return 'C '+self.name
     
    if __name__=='__main__':
        c=C()
        print c.getName()
    

    则会报"AttributeError: 'C' object has no attribute 'name'”错误,所以如果重写了__init__,为了能使用或扩展超类中的行为,最好显式的调用超类的__init__方法

    class C(A):
        def __init__(self,name):
            super(C,self).__init__(name)
        def getName(self):
            return 'C '+self.name
     
    if __name__=='__main__':
        c=C('hello')    
        print c.getName()
    

    相关文章

      网友评论

          本文标题:fluent python-第 1 章 1.1 纸牌计算/nam

          本文链接:https://www.haomeiwen.com/subject/wzqvbxtx.html