流畅的Python总览

第一部分 序幕

通过特殊方法,实现一个 13*4 52张的纸牌。

第二部分 数据结构

列表、字典和集合、文本和字节序列,用于放置数据的序列,包括了容器序列(list、tuple、collections.depue,可放置任何类型对象的引用)、扁平序列(基础类型:字符、字节、数值)

第三部分 把函数视作对象

将函数作为一等对象使用。python基础函数(高阶函数、匿名函数、函数式编程)、策略模式与命令模式实现的代码简化、函数的装饰器与闭包的实现。

第四部分 面向对象惯用法

从以下章节(对象引用、可变性和垃圾回收、符合python风格的对象、序列的修改、散列和切片、接口:从协议到抽象基类、继承的优缺点、正确重载运算符)讲解了引用(reference)的原理、“可变性”的概念、实例的生命周期、如何构建自定义的集合类型和ABC、多重继承该怎么理顺、什么时候应该使用操作符重载及其方法。

第五部分 控制流程

通过章节(可迭代的对象、迭代器和生成器、上下文管理器和 else 块、协程、使用期物处理并发、 使用 asyncio 包处理并发),介绍除去如条件判断、循环和子程序之类的顺序控制流程外,如何通过生成器(generator)、上下文管理器(context manager)和协程(coroutine)来介绍这些控制流程的构造,以及处理并发性(collections.futures)和面向事件的 I/O (asyncio)这些类库。

第六部分 元编程

通过动态属性和特性、属性描述符、类元编程,介绍到如何动态创建带属性的类,用以处理诸如JSON 这类半结构化的数据。

本章主要记录 第一部分 序幕,也就是第一章 python数据模型。

python数据模型

python的魔法方法

介绍了python的魔术方法, 它们经常是两个下划线包围来命名的(比如__init__ , __lt__, __len__). 这些特殊方法是为了被python解释器调用的, 这些方法会注册到他们的类型中方法集合中, 相当于为cpython提供抄近路. 这些方法的速度也比普通方法要快, 当然在自己不清楚这些魔术方法的用途时, 不要随意添加。

关于字符串的表现形式是两种, __str__与__rep__ . python的内置函数 repr 就是通过 __repr__这个特殊方法来得到一个对象的字符串表示形式. 这个在交互模式下比较常用, 如果没有实现 __repr__ , 当控制台打印一个对象时往往是 <A object at 0x000> . 而 __str__ 则是 str() 函数时使用的, 或是在 print 函数打印一个对象的时候才被调用, 终端用户友好。

两者还有一个区别, 在字符串格式化时, “%s” 对应了 __str__ . 而 “%r” 对应了 __repr__. __str__和 __repr__ 在使用上比较推荐的是,前者是给终端用户看,而后者则更方便我们调试和记录日志

python风格代码实现的纸牌

import collections 
Card = collections.namedtuple('Card',['rank','suit'])
class FrenchDeck:
    ranks = [str(n) for n in range(2,11)] + list('JQKA')
    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):
        return len(self._cards)
        
    def __getitem__(self,position):
        return self._cards[position]
  • collections.namedtuple
    Python 2.6 开始,namedtuple 就加入到 Python 里,用以构建只有少数属性但是没有方法的对象。如以上代码构建了类名为:Card,属性包含:rank 、suit的一个简单类。
  • __ getitem__ 方法
    因为__ getitem__ 方法把 [] 操作交给了 self._cards 列表,然后就把这个[]索引操作给设定下来了,因为self._cards是列表,所以列表支持索引、支持反向索引、还支持迭代 如果没有实现这个方法就会报错:‘FrenchDeck’ object does not support indexing
  • 随机抽牌 random.choice
>>> from random import choice
	>>> choice(deck)
	Card(rank='3', suit='hearts')
	>>> choice(deck)
	Card(rank='K', suit='spades')
	>>> choice(deck)
	Card(rank='2', suit='clubs')

纸牌排序

import collections 
Card = collections.namedtuple('Card',['rank','suit'])

# 定义花色大小
suit_values = dict(spades=3, hearts=2, diamonds=1, clubs=0)

class FrenchDeck:
    ranks = [str(n) for n in range(2,11)] + list('JQKA')
    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):
        return len(self._cards)
        
    def __getitem__(self,position):
        return self._cards[position]

    # 排序函数
    def spades_high(card):
		rank_value = FrenchDeck.ranks.index(card.rank)
		return rank_value * len(suit_values) + suit_values[card.suit]
  • 纸牌排序
    FrenchDeck 是不能洗牌的,因为这摞牌是不可变的(immutable):卡牌和它们的位置都是固定的,除非我们破坏这个类的封装性,直接对 _cards 进行操作。