Python - 自定义向量类

Vector2d v0 版本:

# vector2d_v0.py

import math
from array import array


class Vector2d:
    typecode = 'd'  # 转换为字节时的存储方法,d 代表8个字节的双精度浮点数

    def __init__(self, x, y):
        self.x = float(x)
        self.y = float(y)

    # 使对象可迭代
    def __iter__(self):
        return (i for i in (self.x, self.y))

    # 面向开发者的对象表示形式
    def __repr__(self):
        class_name = type(self).__name__
        return '{}({!r}, {!r})'.format(class_name, *self)

    def __str__(self):
        return str(tuple(self))

    # 对象的字节表现形式
    def __bytes__(self):
        return (bytes([ord(self.typecode)]) +  # 将typecode转为字节序列
                bytes(array(self.typecode, self)))

    def __eq__(self, other):
        return tuple(self) == tuple(other)

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

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

测试:

>>> from vector2d_v0 import Vector2d
>>> v1 = Vector2d(3,4) 
>>> print(v1.x,v1.y)
3.0 4.0
# 因为v1 可迭代,所以可以进行拆包
>>> x, y = v1 
>>> x, y
(3.0, 4.0)
# repr 函数调用Vector2d实例,得到的结果类似于构建实例的源码 
>>> v1                  
Vector2d(3.0, 4.0)
# 使用eval函数,表明repr函数调用Vector2d实例得到的是对构造方法的准确表述
>>> v1_clone = eval(repr(v1)) 
>>> v1 == v1_clone
True
>>> print(v1)
(3.0, 4.0)
# 生成实例的二进制表示形式
>>> octets = bytes(v1)
>>> octets
b'd\x00\x00\x00\x00\x00\x00\x08@\x00\x00\x00\x00\x00\x00\x10@'
# 返回v1的模
>>> abs(v1)
5.0
>>> bool(v1), bool(Vector2d(0, 0))
(True, False)

Vector2d v1 版本: v0基础上,加入如下代码

    # 目标: 将字节序列转化为实例
    @classmethod
    def frombytes(cls, octets):
        """
        :param octets:  Vector2d 实例的字节序列
        :return:  实例
        """
        typecode = chr(octets[0])
        memv = memoryview(octets[1:]).cast(typecode)
        print(*memv)  # 构造函数的参数
        return cls(*memv)  # 返回一个新的Vector2d  实例
if __name__ == '__main__':
    v1 = Vector2d(3, 4)
    octets = bytes(v1)
    v1_obj = Vector2d.frombytes(octets)
    print(v1_obj is v1)  
    print(v1_obj == v1) 

# out:
"""
3.0 4.0
False
True
"""

Vector2d v2版本 可散列的Vector2d

为了把Vector2d 实例变成可散列的,必须使用__hash__方法,还需__eq__方法已经实现
此外还需要向量不可变

import math
from array import array


class Vector2d:
    typecode = 'd'  # 转换为字节时的存储方法,d 代表8个字节的双精度浮点数

    def __init__(self, x, y):
        self.__x = float(x)
        self.__y = float(y)

    @property
    def x(self):
        return self.__x  # 此处写self.x 会造成递归

    @property
    def y(self):
        return self.__y

    def __hash__(self):
        return hash(self.x) ^ hash(self.y)

    # 使对象可迭代
    def __iter__(self):
        return (i for i in (self.x, self.y))

    # 面向开发者的对象表示形式
    def __repr__(self):
        class_name = type(self).__name__
        return '{}({!r}, {!r})'.format(class_name, *self)

    def __str__(self):
        return str(tuple(self))

    # 对象的字节表现形式
    def __bytes__(self):
        return (bytes([ord(self.typecode)]) +  # 将typecode转为字节序列
                bytes(array(self.typecode, self)))

    def __eq__(self, other):
        return tuple(self) == tuple(other)

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

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

    @classmethod
    def frombytes(cls, octets):
        """
        :param octets:  Vector2d 实例的字节序列
        :return:  实例
        """
        typecode = chr(octets[0])
        memv = memoryview(octets[1:]).cast(typecode)
        print(*memv)  # out:  3.0 4.0  构造函数的参数
        return cls(*memv)

if __name__ == '__main__':
    v1 = Vector2d(3, 4)
    v2 = Vector2d(3.1, 4.2)
    print(hash(v1), hash(v2))
    print(set([v1, v2]))  # set 中的元素必须要可散列

# out:
"""
7 384307168202284039
{Vector2d(3.1, 4.2), Vector2d(3.0, 4.0)}
"""

Vector类: 用户定义的序列类型

基本的序列协议:

  • __len__
  • __getittem__

任何类,只要使用标准的签名和语义实现了这两个方法,就能用在任何期待序列的地方

import math
import reprlib
from array import array


class Vector:
    typecode = 'd'
    def __init__(self, components):
        # arrya(typecode, components): components 为任何可以迭代的类型
        self._components = array(self.typecode, components)

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

    def __repr__(self):
        """
        reprlib: 当Vector实例的分量超过6个,repr()生成的字符串就会使用...省略一部分。
        :return:
        """
        components = reprlib.repr(self._components)

        # __repr__ run. components:array('d', [0.0, 1.0, 2.0, 3.0, 4.0, ...]), type(components):<class 'str'>
        # print(f"__repr__ run. components:{components}, type(components):{type(components)}")
        # 去除'[' 和 ')'
        components = components[components.find('['):-1]
        return f'Vector({components})'

    def __str__(self):
            return str(tuple(self))

    def __bytes__(self):
        return bytes([ord(Vector.typecode)]) + bytes(self._components)

    def __eq__(self, other):
        return tuple(self) ==  tuple(other)

    def __abs__(self):
        return math.sqrt(sum(x * x for x in self ))

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

    @classmethod
    def frombytes(cls, octets):
        typecode = chr(octets[0])
        memv = memoryview(octets[1:]).cast(typecode)
        print(f'memv: {memv}')
        return cls(memv)  # memv 不用拆包,是一个数组

测试:

>>> Vector([3.1, 4.2])
Vector([3.1, 4.2])                                                             
>>> Vector((3.1, 4.2))                                                         
Vector([3.1, 4.2])                                                             
>>> Vector(range(10))                                                          
Vector([0.0, 1.0, 2.0, 3.0, 4.0, ...])  
posted @ 2022-02-05 23:25  chuangzhou  阅读(309)  评论(0编辑  收藏  举报