记录博客 ZH-BLOG

Python 对象的切片和散列

时间:2018-08-14 22:43:39分类:python

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)]