美文网首页
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