《Fluent Python》读书笔记——第一章Python数据模型

摘要

这一章主要通过几个例子向读者展示了怎么通过定义特殊方法,使自定义的数据类型可以表现的跟内置类型一样,写出更具有Python风格的代码。

示例1 一摞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]

由于定义了__len__方法,所以,当实例化一摞纸牌后,可以len()来查看这个摞纸牌有多少张。

	>>> deck = FrenchDeck()
	>>> len(deck)
	52

如果没有定义__len__方法,运行上述代码则会报错TypeError: object of type 'FrenchDeck' has no len()

同样的,由于定义了__getitem__这个特殊方法,可以使用deck[0]的方式从这摞扑克中抽取任意一张。

	>>> deck[0]
	Card(rank='2', suit='spades')
	>>> deck[1]
	Card(rank='3', suit='spades')

如果没有定义__getitem__个特殊方法,同样也会报错:TypeError: 'FrenchDeck' object is not subscriptable
同时由于实现了__getitem__方法,这摞扑克也变成可迭代的了,并且也支持切片操作。

>>> for card in deck: 
        print(card)
Card(rank='2', suit='spades')
Card(rank='3', suit='spades')
Card(rank='4', suit='spades')
...
>>> deck[:3]
[Card(rank='2', suit='spades'), Card(rank='3', suit='spades'),
Card(rank='4', suit='spades')]

通过实现 __len____getitem__这两个特殊方法, FrenchDeck就跟一个 Python 自有的序列数据类型一样,可以体现出 Python 的核心语言特性(例如迭代和切片)。同时这个类还可以用于标准库中诸如random.choicereversedsorted 这些函数。

示例2 二维向量类

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, scalar):
        return Vector(self.x * scalar, self.y * scalar)

__repr__

Python有一个内置的函数叫 repr,它能把一个对象用字符串的形式表达出来以便辨认,这
就是“字符串表示形式”。 repr 就是通过 __repr__ 这个特殊方法来得到一个对象的字符串
表示形式的。如果没有实现 __repr__,当我们在控制台里打印一个向量的实例时,得到的
字符串可能会是 。通过定义__repr__方法,我们就可以让这个对象的字符串表示形式显示成我们想要任何样子。

__repr____str__ 的区别在于,后者是在 str() 函数被使用,或是在用 print 函数打印一个对象的时候才被调用的,并且它返回的字符串对终端用户更友好。如果你只想实现这两个特殊方法中的一个, __repr__ 是更好的选择,因为如果一个对象没有 __str__ 函数,而 Python 又需要调用它的时候,解释器会用 __repr__ 作为替代。

__add____mul__

通过__add____mul__,该向量类的实例化对象可以实现 + 和 * 。

更多的特殊方法可以参考python文档中的Data Model部分https://docs.python.org/3/reference/datamodel.html

类别 方法名
字符串/字节序列表示形式 __repr____str____format____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__

你可能感兴趣的:(Fluent,Python,python,开发语言)