序列的修改、散列和切片

import operator
import operator
import itertools
import numbers

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):
        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.comonents))
    
    def __eq__(self, other):
#         return tuple(self) == tuple(other)

        if len(self) != len(other):
            return Flase
#         for a, b in zip(self, other):
#             if a != b:
#                 return False
#         return True
#       使用all
        return (len(self) == len(other) and all(a == b for a, b in zip(self, other)))
    
    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 = '{cls.__name__} indices must be integers'
            raise TypeError(msg.format(cls=cls))
            
    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 __setattr__(self, name, value):
        cls = type(self)
        if len(name) == 1:
            if name in cls.shortcut_name:
                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 __hash__(self):
#         hashes = (hash(x) for x in self._components)
#         return functools.reduce(operator.xor, hashes, 0)
#       使用map
        hashes = map(hash, self._components)
        return functools.reduce(operator.xor, hashes)
    
    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(cotets[0])
        memv = memoryview(octets[1:]).cast(typecode)
        return cls(memv)

序列协议

  • 只需实现__len__和__getitem__两个方法
···
    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 = '{cls.__name__} indices must be integers'
            raise TypeError(msg.format(cls=cls))
···
v1 = Vector([3, 4, 5]) # 可传入序列参数
# 同时支持切片
v7 = Vector(range(7))
v7[1:4]

切片原理

class MySeq:
    def __getitem__(self, index):
        return index

s = MySeq()
s[1:4] # 返回slice:slice(1, 4, None)
s[1:4:2]# 返回slice: slice(1, 4, 2)
print(slice) # <class 'slice'>
dir(slice)
  • slice是内置的类型

动态存取属性

  • 使用‘xyzt’获取前四位
···
    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))
···
v = Vector(range(5))
print(v)
print(v.x)
v.x = 10
print(v.x) # 此时为:10
print(v) # 此时v没有变,产生了行为不一致
···
    def __setattr__(self, name, value):
        cls = type(self)
        if len(name) == 1:
            if name in cls.shortcut_name:
                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)
···
# 实现__setattr__方法在对实例属性赋值时抛出AttributeError异常

散列

  • zip内置函数:并行迭代两个或多个可迭代对象
list(zip(range(3), 'ABC', [0.0, 1.1, 2.2, 3.3]))
  • 使用reduce实现hasg
    def __hash__(self):
#         hashes = (hash(x) for x in self._components)
#         return functools.reduce(operator.xor, hashes, 0)
#       使用map
        hashes = map(hash, self._components)
        return functools.reduce(operator.xor, hashes)

总结

  • zip的使用,其会在最短的那个操作数耗尽时停止
  • 切片原理,slice内置类型在__getitem__中的作用
  • 序列协议,实现__len__和__getitem__方法,有时__len__方法不是必须的,实现__getitem__方法,同时支持切片
  • 通常定义了__getattr__方法,也要定义__setattr__方法,避免行为不一致

流畅的Python2015

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值