进阶:Python序列的修改、散列和切片

本文介绍了如何基于Python的序列协议和鸭子类型,构建一个表示多维向量的Vector类。该类实现了序列的切片、动态存取属性、可散列和快速等值测试。详细探讨了切片原理、__getattr__和__setattr__方法、__hash__和__eq__方法的实现,以及Vector类的格式化方法__format__。示例代码展示了如何处理多维向量的属性访问和散列,以及如何提高效率和兼容性。
摘要由CSDN通过智能技术生成

将以上一篇文章中二维向量Vector2d类为基础(文章链接:进阶:编写符合Python风格的对象_lijiachang8的博客-CSDN博客),定义表示多维向量的Vector类。这个类的行为与Python中标准的不可变扁平序列一样。

Vector类:多维向量类

多维向量要让__init__方法接收任意个参数,但是,序列类型的构造方法最好接收可迭代对象为参数,因为所有的内置序列类型都是这样的(接收可迭代对象)。

示例,一个多维向量类

import math
from array import array
import reprlib


class Vector:
    type_code = 'd'  # 类属性。在于实例和字节序列转换时使用。

    def __init__(self, components):
        self.__components = array(self.type_code, components)

    def __iter__(self):
        return iter(self.__components)

    def __repr__(self):
        class_name = type(self).__name__  # 自省类名
        components = reprlib.repr(self.__components)  # 数据过多时,省略号展示 array('d', [0.0, 1.0, 2.0, 3.0, 4.0, ...])
        components = components[components.find('['):-1]  # 截取字符串成 [0.0, 1.0, 2.0, 3.0, 4.0, ...]
        return '{}({})'.format(class_name, components)

    def __str__(self):
        return str(tuple(self))  # 从可迭代对象可以轻松得到一个元组,然后转成字符串。tuple( iterable ),所以会调用__iter__

    def __eq__(self, other):
        return tuple(self) == tuple(other)  # 构建成元组,可以快速比较所有分量。

    def __bytes__(self):
        """向量类转换为bytes"""
        return bytes([ord(self.type_code)]) + bytes(self.__components)  # b'd' + b'\x00...'

    def __abs__(self):
        return math.sqrt(sum(x * x for x in self.__components))  # math.sqrt()求平方根。先用sum求每个分量的平方之和,然后开平方

    def __bool__(self):
        return bool(abs(self))  # 判断模是否为0

    @classmethod
    def from_bytes(cls, bytes_):
        """bytes转换为向量类"""
        type_code = chr(bytes_[0])
        memy = memoryview(bytes_[1:]).cast(type_code)
        return cls(memy)

知识点:

  1. reprlib.repr() 这个函数用于生产大型结构或递归结构的安全表示形式,它会限制输出字符串的长度,用省略号...表示截断的部分。
  2. components[components.find('['):-1] 中的components.find('[')是找到第一个[符号的索引,从而构成components[10:-1] 的切片。 其实可以简单是使用reprlib.repr(list(self.__compoents))返回,但是这样有点浪费性能,这样会产生一个新的列表,没必要的。
  3. 调用repr()函数(__repr__方法)的目的是为了调试,因此绝对不能出现异常,让用户能够识别目标对象。
  4. from_bytes方法的返回return cls(memy)中memy已经不需要拆包了,因为Vector类可以接受可迭代对象。

协议和鸭子类型

什么是协议:

在面向对象编程中,协议是非正式的接口,只在文档中定义,在代码中不定义。

例如,Python的序列协议只需要实现__len__和__getitem__方法。任何类,只要使用标准的签名和语义实现这两个方法,那么就是序列类型。也就是说,想实现一个序列类型,是不是序列的子类无关紧要,重要提供了所需的方法即可。

示例,实现纸牌类,是序列,但是没有继承。

import collections

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


class FrenchDeck:
    
    ranks = [str(x) for x in range(2, 11)] + list('JQKA')
    suits = 'spades diamonds clubs hearts'.split()  # 花色

    def __init__(self):
        self._cards = [Card(rank, suit) for rank in self.ranks for suit in self.suits]

    def __len__(self):
        return len(self._cards)

    def __getitem__(self, item):
        return self._cards[item]

    def __repr__(self):
        return str(self._cards)


cards = FrenchDeck()
print(cards)
print(len(cards))
print(cards[0])
打印结果
[Card(rank='2', suit='spades'), Card(rank='2', suit='diamonds'), Card(rank='2', suit='clubs'), Card(rank='2', suit='hearts'), Card(rank='3', suit='spades'), Card(rank='3', suit='diamonds'), Card(rank='3', suit='clubs'), Card(rank='3', suit='hearts'), Card(rank='4', suit='spades'), Card(rank='4', suit='diamonds'), Card(rank='4', suit='clubs'), Card(rank='4', suit='hearts'), Card(rank='5', suit='spades'), Card(rank='5', suit='diamonds'), Card(rank='5', suit='clubs'), Card(rank='5', suit='hearts'), Card(rank='6', suit='spades'), Card(rank='6', suit='diamonds'), Card(rank='6', suit='clubs'), Card(rank='6', suit='hearts'), Card(rank='7', suit='spades'), Card(rank='7', suit='diamonds'), Card(rank='7', suit='clubs'), Card(rank='7', suit='hearts'), Card(rank='8', suit='spades'), Card(rank='8', suit='diamonds'), Card(rank='8', suit='clubs'), Card(rank='8', suit='hearts'), Card(rank='9', suit='spades'), Card(rank='9', suit='diamonds'), Card(rank='9', suit='clubs'), Card(rank='9', suit='hearts'), Card(rank='10', suit='spades'), Card(rank='10', suit='diamonds'), Card(rank='10', suit&
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值