from array import array
import reprlib
import math
import numbers
import functools
import operator
class Vector:
typecode = 'd'
shortcut_names = 'xyzt'
def __init__(self,components):
self._components = array(self.typecode, components)
def __iter__(self):
return iter(self._components)
def __repr__(self):
# 有限长度表现形式:
# array('d', [0.0, 1.0, 2.0, 3.0, 4.0, ...])
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))
if len(self) != len(other):
return False
for a, b in zip(self, other):
if a != b:
return False
return True
def __hash__(self):
# hashes = (hash(x) for x in self._components)
hashes = map(hash, self._components)
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):
# index 为数字或者 slice 对象
cls = type(self)
if isinstance(index, slice):
# index 为 slice 对象时,返回 Vector 对象
return cls(self._components[index])
elif isinstance(index, numbers.Integral):
return self._components[index]
else:
msg = '{cls.__name__} indices must be integers'
raise TypeError(msg.format(cls=cls))
def __getattr__(self, name):
""" 当使用如: v.x 获取属性值时,会调用此方法 """
""" obj.x 表达式, Python 会检查 my_obj 实例有没有名为 x 的属性;
如果没有, 到类(my_obj.__class__) 中查找; 如果还没有, 顺着继
承树继续查找。 如果依旧找不到, 调用 my_obj 所属类中定义的
__getattr__ 方法 """
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 __setattr__(self, name, value):
""" 当使用如: v.x = 10 赋值时,会调用此方法 """
cls = type(self)
if len(name) == 1:
if name in cls.shortcut_names:
error = 'readonly attribute {attr_name!r}'
elif name.islower():
error = 'can`t set attributes "a" to "z" in {cls_name!r}'
else:
error = ''
if error:
msg = error.format(cls_name=cls.__name__, attr_name=name)
raise AttributeError(msg)
super().__setattr__(name, value)
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())
out_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)
相关使用方法:
>>> import functools
>>> functools.reduce(lambda a,b: a*b, range(1, 6))
120
>>> n = 0
>>> for i in range(1, 6):
n ^= i
>>> n
1
>>> functools.reduce(lambda a,b: a^b, range(1, 6), 0)
1
>>> import operator
>>> functools.reduce(operator.xor, range(1, 6))
1
>>> enumerate('abc', start=1)
enumerate object at 0x000000000311BF78
>>> list(enumerate('abc', start=1))
[(1, 'a'), (2, 'b'), (3, 'c')]
>>> zip(range(3), 'ABC')
>>> list(zip(range(3), 'ABC'))
[(0, 'A'), (1, 'B'), (2, 'C')]
>>> list(zip(range(3), 'ABC', [0.0, 1.1, 2.2, 3.3]))
[(0, 'A', 0.0), (1, 'B', 1.1), (2, 'C', 2.2)]
>>> from itertools import zip_longest
>>> list(zip_longest(range(3), 'ABC', [0.0, 1.1, 2.2, 3.3], fillvalue=-1))
[(0, 'A', 0.0), (1, 'B', 1.1), (2, 'C', 2.2), (-1, -1, 3.3)]