《流畅的Python》读书笔记1(第一章:Python数据模型)

108 阅读3分钟

本书并不是一本完备的Python使用手册,而是会强调Python作为编程语言独有的特性,这些特性或者是只有Python才具备的,或者是在其他大众语言里很少见的。

1.1 一摞Python风格的纸牌

展示如何实现__getitem__和__len__这两个特殊方法,通过这个例子我们能见识到特殊方法的强大。

import collections

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

class FrenchDeck:
    ranks = [str(n) for n in range(2, 11)] + list('JQKA')

    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]

# 利用namedtuple,得到一个纸牌对象
beer_card = Card('7', 'diamonds')
print("利用namedtuple,得到一个纸牌对象")
print(beer_card)

# 查看一叠牌有多少张
deck = FrenchDeck()
print("查看一叠牌有多少张")
print(len(deck))

# 从一叠牌中抽取特定的一张牌,由__getitem__方法提供
# 抽取第一张
print("抽取第一张")
print(deck[0])
# 抽取最后一张
print("抽取最后一张")
print(deck[-1])

#随机抽取一张牌,不需要单独写一个方法
from random import choice
print("随机抽取一张牌")
print(choice(deck))
print(choice(deck))
print(choice(deck))

#因为__getitem__方法,deck类自动支持切片操作
# 最上面三张牌
print("最上面三张")
print(deck[:3])
# 牌面是A的牌
print("牌面是A的牌")
print(deck[12::13])

输出结果:

利用namedtuple,得到一个纸牌对象
Card(rank='7', suit='diamonds')
查看一叠牌有多少张
52
抽取第一张
Card(rank='2', suit='spades')
抽取最后一张
Card(rank='A', suit='hearts')
随机抽取一张牌
Card(rank='J', suit='clubs')
Card(rank='K', suit='diamonds')
Card(rank='J', suit='spades')
最上面三张
[Card(rank='2', suit='spades'), Card(rank='3', suit='spades'), Card(rank='4', suit='spades')]
牌面是A的牌
[Card(rank='A', suit='spades'), Card(rank='A', suit='diamonds'), Card(rank='A', suit='clubs'), Card(rank='A', suit='hearts')]

1.2如何使用特殊方法

特殊方法的存在是为了被Python解释器调用的,我们自己并不需要调用它们。没有my_object.__len__()这种写法,而应该使用len(my_object)

很多时候,特殊方法的调用是隐式的,比如 for i in x:这个语句,背后其实用的是iter(x),而这个函数的背后则是x.__iter__()方法。

1.2.1 模拟数值类型

实现一个二维向量(vector)类,演示特殊方法 __repr__、__abs__、__add__和__mul__。

from math import hypot


class Vector:
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

    def __repr__(self):
        return 'Vector(%r,%r)' % (self.x, self.y)

    def __abs__(self):
        return hypot(self.x, self.y)

    def __bool__(self):
        return bool(abs(self))

    def __add__(self, other):
        x = self.x + other.x
        y = self.y + other.y
        return Vector(x, y)

    def __mul__(self, other):
        return Vector(self.x * other, self.y * other)

可以进行向量的运算

>>> v1=Vector(2,4)
>>> v2=Vector(2,1)
>>> v1+v2
Vector(4,5)
>>> v = Vector(3,4)
>>> abs(v)
5.0
>>> v * 3
Vector(9,12)
>>> abs(v*3)
15.0

1.2.2 字符串的表示形式

Python有一个内置的函数叫repr,它能把一个对象用字符串的形式表达出来一边辨认,这就是“字符串的表示形式”。repr就是通过__repr__这个特殊的方法来得到一个对象的字符串表示形式的。

1.3 特殊方法一览表

表1-1:跟运算符无关的特殊方法

类别方法名
字符串字节序列表示形式__ repr __ , __ format __ , __ str __ , __ bytes __
数值转换__ abs __ , __ bool __ , __ complex __ , __ int __ , __ float__ , __ hash __ , __ index __
集合模拟__ len __ , __ getitem __ , __ setitem __ , __ delitem __ , __ contains __
迭代枚举__iter __ , __ reversed __ , __ next __
可调用模拟__ call __
上下文管理__ enter __ , __ exit __
实例创建和销毁__ new __ , __ init __ , __ del __
属性管理__ getattr __ , __ getattribute __ , __ setattr __ , __ delattr __ , __ dir __
属性描述符__ get __ , __ set __ , __ delete __
跟踪相关的服务__ prepare __ , __ instancecheck __ , __ subclasscheck __

表1-2:跟运算符相关的特殊方法

类别方法名和对应的运算符
一元运算符__ neg __ , __ pos __ , __ abs __ 
比较运算符__ lt __ <, __ le __ <=, __ eq __ ==, __ ne __ !=, __ gt __ > , __ ge __ >=,
算术运算符__ add __ + , __ sub __ - , __ mul __ * , __ truediv __ / , __ floordiv __ //  __ mod __ % , __ divmod __ divmode() , __ pow __ **或pow() __ round __ round()
反向算数运算符__ radd __ , __ rsub __ , __ rmul __ , __ rtruediv __ , __ rfloordiv __  __ rmod __ , __ rdivmod __ , __ rpow __
增量赋值运算符__ iadd __ , __ isub __ , __ imul __ , __ itruediv __ , __ ifloordiv __  __ imod __ , __ idivmod __ , __ ipow __
位运算符__ invert __ ~, __ lshift __ <<, __ rshift __ >>, __ adn __ &, __ or __
反向位运算符__ rlshift __ , __ rrshift __ , __ rand __ , __ rxor __ , __ ror __
增量赋值位运算符__ ilshift __ , __ irshift __ , __ iand __ , __ ixor __ , __ ior __