当前位置: 首页 > news >正文

Python 序列的修改、散列和切 片(Vector类第5版:格式化)

Vector类第5版:格式化

Vector 类的__format__ 方法与 Vector2d 类的相似,但是不使用极
坐标,而使用球面坐标(也叫超球面坐标),因为 Vector 类支持 n 个
维度,而超过四维后,球体变成了“超球体”。 因此,我们会把自定义
的格式后缀由 ‘p’ 变成 ‘h’。

9.5 节说过,扩展格式规范微语言
(https://docs.python.org/3/library/string.html#formatspec)时,最好避
免重用内置类型支持的格式代码。这里对微语言的扩展还会用到浮
点数的格式代码 ‘eEfFgGn%’,而且保持原意,因此绝对要避免重
用代码。整数使用的格式代码有 ‘bcdoxXn’,字符串使用的是
‘s’。在 Vector2d 类中,我选择使用 ‘p’ 表示极坐标。使用 ‘h’
表示超球面坐标(hyperspherical coordinate)是个不错的选择。

例如,对四维空间(len(v) == 4)中的 Vector 对象来说,‘h’ 代码
得到的结果是这样:<r, Φ1, Φ2, Φ3>。其中,r 是模(abs(v)),余
下三个数是角坐标 Φ1、Φ2 和 Φ3。

下面几个示例摘自 vector_v5.py 的 doctest(参见示例 10-16),是四维
球面坐标格式:

>>> format(Vector([-1, -1, -1, -1]), 'h')
'<2.0, 2.0943951023931957, 2.186276035465284, 3.9269908169872414>'
>>> format(Vector([2, 2, 2, 2]), '.3eh')
'<4.000e+00, 1.047e+00, 9.553e-01, 7.854e-01>'
>>> format(Vector([0, 1, 0, 0]), '0.5fh')
'<1.00000, 1.57080, 0.00000, 0.00000>'

在小幅改动__format__ 方法之前,我们要定义两个辅助方法:一个是
angle(n),用于计算某个角坐标(如 Φ1);另一个是 angles(),返回由所有角坐标构成的可迭代对象。我们不会讲解其中涉及的数学原
理,如果你好奇的话,可以查看维基百科中的“n 维球体”词条
(https://en.wikipedia.org/wiki/N-sphere),那里有几个公式,我就是使
用它们把 Vector 实例分量数组内的笛卡儿坐标转换成球面坐标的。

示例 10-16 是 vector_v5.py 脚本的完整代码,包含自 10.2 节以来实现的
所有代码和本节实现的自定义格式。

示例 10-16  vector_v5.py:Vector 类最终版的 doctest 和全部代
码;带标号的那几行是为了支持__format__ 方法而添加的代码

"""
A multidimensional ``Vector`` class, take 5
A ``Vector`` is built from an iterable of numbers::
>>> Vector([3.1, 4.2])
Vector([3.1, 4.2])
>>> Vector((3, 4, 5))
Vector([3.0, 4.0, 5.0])
>>> Vector(range(10))
Vector([0.0, 1.0, 2.0, 3.0, 4.0, ...])
Tests with two dimensions (same results as ``vector2d_v1.py``)::
>>> v1 = Vector([3, 4])
>>> x, y = v1
>>> x, y
(3.0, 4.0)
>>> v1
Vector([3.0, 4.0])
>>> 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@'
>>> abs(v1)
5.0
>>> bool(v1), bool(Vector([0, 0]))
(True, False)
Test of ``.frombytes()`` class method:
>>> v1_clone = Vector.frombytes(bytes(v1))
>>> v1_clone
Vector([3.0, 4.0])
>>> v1 == v1_clone
True
Tests with three dimensions::
>>> v1 = Vector([3, 4, 5])
>>> x, y, z = v1
>>> x, y, z
(3.0, 4.0, 5.0)
>>> v1
Vector([3.0, 4.0, 5.0])
>>> v1_clone = eval(repr(v1))
>>> v1 == v1_clone
True
>>> print(v1)
(3.0, 4.0, 5.0)
>>> abs(v1) # doctest:+ELLIPSIS
7.071067811...
>>> bool(v1), bool(Vector([0, 0, 0]))
(True, False)
Tests with many dimensions::
>>> v7 = Vector(range(7))
>>> v7
Vector([0.0, 1.0, 2.0, 3.0, 4.0, ...])
>>> abs(v7) # doctest:+ELLIPSIS
9.53939201...
Test of ``.__bytes__`` and ``.frombytes()`` methods::
>>> v1 = Vector([3, 4, 5])
>>> v1_clone = Vector.frombytes(bytes(v1))
>>> v1_clone
Vector([3.0, 4.0, 5.0])
>>> v1 == v1_clone
True
Tests of sequence behavior::
>>> v1 = Vector([3, 4, 5])
>>> len(v1)
3
>>> v1[0], v1[len(v1)-1], v1[-1]
(3.0, 5.0, 5.0)
Test of slicing::
>>> v7 = Vector(range(7))
>>> v7[-1]
6.0
>>> v7[1:4]
Vector([1.0, 2.0, 3.0])
>>> v7[-1:]
Vector([6.0])
>>> v7[1,2]
Traceback (most recent call last):
...
TypeError: Vector indices must be integers
Tests of dynamic attribute access::
>>> v7 = Vector(range(10))
>>> v7.x
0.0
>>> v7.y, v7.z, v7.t
(1.0, 2.0, 3.0)
Dynamic attribute lookup failures::
>>> v7.k
Traceback (most recent call last):
...
AttributeError: 'Vector' object has no attribute 'k'
>>> v3 = Vector(range(3))
>>> v3.t
Traceback (most recent call last):
...
AttributeError: 'Vector' object has no attribute 't'
>>> v3.spam
Traceback (most recent call last):
...
AttributeError: 'Vector' object has no attribute 'spam'
Tests of hashing::
>>> v1 = Vector([3, 4])
>>> v2 = Vector([3.1, 4.2])
>>> v3 = Vector([3, 4, 5])
>>> v6 = Vector(range(6))
>>> hash(v1), hash(v3), hash(v6)
(7, 2, 1)
Most hash values of non-integers vary from a 32-bit to 64-bit CPython build::
>>> import sys
>>> hash(v2) == (384307168202284039 if sys.maxsize > 2**32 else 357915986)
True
Tests of ``format()`` with Cartesian coordinates in 2D::
>>> v1 = Vector([3, 4])
>>> format(v1)
'(3.0, 4.0)'
>>> format(v1, '.2f')
'(3.00, 4.00)'
>>> format(v1, '.3e')
'(3.000e+00, 4.000e+00)'
Tests of ``format()`` with Cartesian coordinates in 3D and 7D::
>>> v3 = Vector([3, 4, 5])
>>> format(v3)
'(3.0, 4.0, 5.0)'
>>> format(Vector(range(7)))
'(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0)'
Tests of ``format()`` with spherical coordinates in 2D, 3D and 4D::
>>> format(Vector([1, 1]), 'h') # doctest:+ELLIPSIS
'<1.414213..., 0.785398...>'
>>> format(Vector([1, 1]), '.3eh')
'<1.414e+00, 7.854e-01>'
>>> format(Vector([1, 1]), '0.5fh')
'<1.41421, 0.78540>'
>>> format(Vector([1, 1, 1]), 'h') # doctest:+ELLIPSIS
'<1.73205..., 0.95531..., 0.78539...>'
>>> format(Vector([2, 2, 2]), '.3eh')
'<3.464e+00, 9.553e-01, 7.854e-01>'
>>> format(Vector([0, 0, 0]), '0.5fh')
'<0.00000, 0.00000, 0.00000>'
>>> format(Vector([-1, -1, -1, -1]), 'h') # doctest:+ELLIPSIS
'<2.0, 2.09439..., 2.18627..., 3.92699...>'
>>> format(Vector([2, 2, 2, 2]), '.3eh')
'<4.000e+00, 1.047e+00, 9.553e-01, 7.854e-01>'
>>> format(Vector([0, 1, 0, 0]), '0.5fh')
'<1.00000, 1.57080, 0.00000, 0.00000>'
"""
from array import array
import reprlib
import math
import numbers
import functools
import operator
import itertools ➊
class Vector:
typecode = 'd'
def __init__(self, components):
self._components = array(self.typecode, components)
def __iter__(self):
return iter(self._components)
def __repr__(self):
components = reprlib.repr(self._components)
components = components[components.find('['):-1]
return 'Vector({})'.format(components)
def __str__(self):
return str(tuple(self))
def __bytes__(self):
return (bytes([ord(self.typecode)]) +
bytes(self._components))
def __eq__(self, other):
return (len(self) == len(other) and
all(a == b for a, b in zip(self, other)))
def __hash__(self):
hashes = (hash(x) for x in self)
return functools.reduce(operator.xor, hashes, 0)
def __abs__(self):
return math.sqrt(sum(x * x for x in self))
def __bool__(self):
return bool(abs(self))
def __len__(self):
return len(self._components)
def __getitem__(self, index):
cls = type(self)
if isinstance(index, slice):
return cls(self._components[index])
elif isinstance(index, numbers.Integral):
return self._components[index]
else:
msg = '{.__name__} indices must be integers'
raise TypeError(msg.format(cls))
shortcut_names = 'xyzt'
def __getattr__(self, name):
cls = type(self)
if len(name) == 1:
pos = cls.shortcut_names.find(name)
if 0 <= pos < len(self._components):
return self._components[pos]
msg = '{.__name__!r} object has no attribute {!r}'
raise AttributeError(msg.format(cls, name))
def angle(self, n): ➋
r = math.sqrt(sum(x * x for x in self[n:]))
a = math.atan2(r, self[n-1])
if (n == len(self) - 1) and (self[-1] < 0):
return math.pi * 2 - a
else:
return a
def angles(self):return (self.angle(n) for n in range(1, len(self)))
def __format__(self, fmt_spec=''):
if fmt_spec.endswith('h'): # 超球面坐标
fmt_spec = fmt_spec[:-1]
coords = itertools.chain([abs(self)],
self.angles()) ➍
outer_fmt = '<{}>'else:
coords = self
outer_fmt = '({})' ➏
components = (format(c, fmt_spec) for c in coords)return outer_fmt.format(', '.join(components))@classmethod
def frombytes(cls, octets):
typecode = chr(octets[0])
memv = memoryview(octets[1:]).cast(typecode)
return cls(memv)

❶ 为了在__format__ 方法中使用 chain 函数,导入 itertools 模
块。
❷ 使用“n 维球体”词条(http://en.wikipedia.org/wiki/N-sphere)中的公式
计算某个角坐标。
❸ 创建生成器表达式,按需计算所有角坐标。
❹ 使用 itertools.chain 函数生成生成器表达式,无缝迭代向量的模
和各个角坐标。
❺ 配置使用尖括号显示球面坐标。
❻ 配置使用圆括号显示笛卡儿坐标。
❼ 创建生成器表达式,按需格式化各个坐标元素。
❽ 把以逗号分隔的格式化分量插入尖括号或圆括号。

我们在__format__、angle 和 angles 中大量使用了生成器
表达式,不过我们的目的是让 Vector 类的__format__ 方法与
Vector2d 类处在同一水平上。第 14 章讨论生成器时会使用
Vector 类中的部分代码举例,然后详细说明生成器的技巧。

相关文章:

  • <4>, Qt窗口
  • Redis最佳实践——安全与稳定性保障之访问控制详解
  • 5月31日day41打卡
  • 极大似然估计例题——正态分布的极大似然估计
  • 类FNAF游戏后续
  • 青少年编程与数学 02-020 C#程序设计基础 15课题、异常处理
  • Flask-Login使用示例
  • Redis最佳实践——安全与稳定性保障之连接池管理详解
  • 5.31 数学复习笔记 22
  • AI Agent智能体:底层逻辑、原理与大模型关系深度解析·优雅草卓伊凡
  • 计算机网络之路由表更新
  • MySQL数据库复合查询
  • 2024 CKA模拟系统制作 | Step-By-Step | 19、题目搭建-升级集群
  • LLm中 float16和 float32 区别,为什么训练不能采用float16--梯度消失
  • MySQL中COUNT(*)、COUNT(1)和COUNT(字段名)的深度剖析与实战应用
  • JVM——云原生时代JVM的演进之路
  • Oracle中EXISTS NOT EXISTS的使用
  • Qt -下载Qt6与OpenCV
  • C++深入类与对象
  • Spring Boot启动慢?Redis缓存击穿?Kafka消费堆积?——Java后端常见问题排查实战
  • 小程序加盟代理前景/aso优化排名违法吗
  • 番禺做网站的公司/app运营方案策划
  • 做网站要切图吗/百度竞价包年推广是怎么回事
  • 南京做机床的公司网站/百度旧版本下载
  • w做网站诈骗/苏州网站建设优化
  • 媒体查询做响应式网站/沈阳seo合作