第一章 python数据模型

第一章 python数据模型

1.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]

上述代码中, namedtuple 可以用来创建一张卡牌, 且其属性可以直接获取. 例如

beer_card = Card('7', 'diamond')
beer_card
>>> Card(rank='7', suit='diamond')

重写__len__可以使用len()来获取一叠牌的张数

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

重写__getitem__ 可以使用索引取出特定的牌, 例如

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

利用 random.choice 可以实现随机选择一张牌, 因为它已经是一个序列.

# randomly choose a card
from random import choice
choice(deck)
>>> Card(rank='7', suit='diamonds')

也可以使用切片

deck[:3]
>>> [Card(rank='2', suit='spades'),
 Card(rank='3', suit='spades'),
 Card(rank='4', suit='spades')]

可以进行迭代和反向迭代

for card in deck:
    print(card)
>>> Card(rank='2', suit='spades')
Card(rank='3', suit='spades')
Card(rank='4', suit='spades')
...
# reverse iteration
for card in reversed(deck):
    print(card)
>>> Card(rank='A', suit='hearts')
Card(rank='K', suit='hearts')
...

可以用in判断是否属于

Card('Q', 'hearts') in deck
>>> True

可以对其进行升序排序, 这里花色按照 c,d,h,s, 点数从2到A, 也就是2C, 2D, … , AH, AS.

suit_values = dict(spades=3, hearts=2, diamonds=1, clubs=0)
def spades_high(card):
    """a function to get card's value, 2C to AS"""
    rank_value = FrenchDeck.ranks.index(card.rank)
    return rank_value * len(suit_values) + suit_values[card.suit]

# ascending sort
for card in sorted(deck, key=spades_high):
    print(card)
>>> Card(rank='2', suit='clubs')
Card(rank='2', suit='diamonds')
...
Card(rank='A', suit='hearts')
Card(rank='A', suit='spades')

1.2 如何使用特殊方法

特殊方法是被Python解释器调用的, 而不需要主动去调用. 通过内置的函数(len, iter, str)来使用特殊方法是最好的选择.

1.2.1 模拟数值类型

使用一个类Vector来实现向量

from math import hypot


class Vector:
    """class vector per example 1.2"""

    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):
        # hypot calculates 2-norm of vector
        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):
        # multiply with a scalar
        return Vector(self.x * scalar, self.y * scalar)

虽然代码中有6个特殊方法, 但这些方法除了__init__并不会在类自身的代码中使用.

1.2.2 字符串表现形式

Python的内置函数repr 能把一个对象用字符串的形式表现出来以便辨认, 其通过__repr__这个特殊方法来获得一个对象的字符串.

__repr__实现中, 使用了%r来获取对象各个属性的标准字符串表示格式. __repr__返回的字符串应该准确, 无歧义.

__repr____str__的区别在于, 后者是在str()函数被使用, 或是print打印时, 它返回的字符串对终端用户更友好.

如果只想实现两个方法中的一种, __repr__是更好的选择, 因为没有__str__时, 解释器会用__repr__替代.

1.2.3 算术运算符

__add____mul__为向量类提供了+*这两个算术运算符, 这两个方法的返回值都是新创建的向量对象, 符合中缀运算符不改变操作对象, 而是产出一个新的值的基本原则.

1.2.4 自定义的布尔值

bool(x)的背后其实是调用x.__bool__()的结果.

我们对__bool__的实现很简单, 向量模为0则为False, 其他情况为True.

还有一种更高效的写法

def __bool__(self):
    return bool(self.x or self.y)

1.3 特殊方法一览

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

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

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

类别方法名和对应的运算符
一元运算符__neg__: -, __pos__: + ,__abs__: abs()
众多比较运算符__lt__: <,__le__: <=, __eq__: ==,__ne__: !=,__gt__: >,__ge__: >=
算术运算符__add__: +,__sub__: -,__mul__: *,__truediv__: /,__floordiv__: //,__mod__: %,__divmod__: divmod(),__pow__: pow(), **,__round__: round()
反向算术运算符__radd__,__rsub__,__rmul__,rtruediv__,__rfloordiv__,__rmod__,__rdivmod__,rpow__
增量赋值算术运算符__iadd__,__isub__,__imul__,__itruediv__,__ifloordiv__,__imod__,__ipow__
位运算符__invert__: ~,__lshift__: <<,__rshift__: >>,__and__: &,`or:
反向位运算符__rlshift__,__rrshift__,__rand__,__rxor__,__ror__
增量赋值位运算符__ilshift__,__irshift__,__iand__,__ixor__,__ior__

1.4 为什么len不是普通方法

len之所以不是一个普通方法, 是为了让Python自带的数据结构可以走后门, abs也是同理. 多亏了他是特殊方法, 我们也可以把len用于自定义数据类型.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值