笔记:序列的修改、散列和切片

[复制链接]
发表于 2022-12-3 12:39:48 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
  1. from array import array
  2. import reprlib, math, numbers
  3. from functools import reduce
  4. from operator import xor
  5. from itertools import chain
复制代码
  1. # Vector_v1
  2. class Vector:
  3.     typecode = 'd'
  4.     def __init__(self, components) -> None:
  5.         self._componeents = array(self.typecode, components)
  6.     def __iter__(self):
  7.         return iter(self._componeents)
  8.     def __repr__(self):
  9.         components = reprlib.repr(self._componeents)  # array('d', [1, 2, 3, 4, ...])
  10.         components = components[components.find('['):-1]  # [1, 2, 3, 4, ...]
  11.         return 'Vector({})'.format(components)  # Vector([1, 2, 3, 4])
  12.    
  13.     def __str__(self):
  14.         return str(tuple(self))
  15.    
  16.     def __bytes__(self):
  17.         return (bytes([ord(self.typecode)]) + bytes(self._componeents))
  18.     def __eq__(self, other):
  19.         return tuple(self) == tuple(other)
  20.     def __abs__(self):
  21.         return math.sqrt(sum(x * x for x in self))
  22.     def __bool__(self):
  23.         return bool(abs(self))
  24.    
  25.     def __len__(self):
  26.         return len(self._componeents)
  27.    
  28.     def __getitem__(self, index):
  29.         return self._componeents[index]
  30.     @classmethod
  31.     def frombytes(cls, octets):
  32.         typecode = chr(octets[0])
  33.         memv = memoryview(octets[1:]).cast(typecode)
  34.         return cls(memv)
复制代码
reprlib.repr() 获取对象的有限长度表现形式, 多的用 ... 表示, eg. array('d', [0, 1, 2, 3, 4, ...])
  1. # Test
  2. v1 = Vector([3, 4, 5])
  3. print(len(v1))
  4. print(v1[0], v1[-1])
  5. v7 = Vector(range(7))
  6. print(v7[1:4])
复制代码
  1. 3
  2. 3.0 5.0
  3. array('d', [1.0, 2.0, 3.0])
复制代码
切片原理
  1. class Seq:
  2.     def __getitem__(self, index):
  3.         return index
  4. s = Seq()
  5. print(1, s[1])
  6. print(2, s[1:4])
  7. print(3, s[1:4:2])
  8. print(4, s[1:4:2, 9])
  9. print(5, s[1:4:2, 7:9])
复制代码
  1. 1 1
  2. 2 slice(1, 4, None)
  3. 3 slice(1, 4, 2)
  4. 4 (slice(1, 4, 2), 9)
  5. 5 (slice(1, 4, 2), slice(7, 9, None))
复制代码
  1. print(slice)
  2. print(dir(slice))
  3. print(help(slice.indices))
复制代码
  1. <class 'slice'>
  2. ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'indices', 'start', 'step', 'stop']
  3. Help on method_descriptor:
  4. indices(...)
  5.     S.indices(len) -> (start, stop, stride)
  6.    
  7.     Assuming a sequence of length len, calculate the start and stop
  8.     indices, and the stride length of the extended slice described by
  9.     S. Out of bounds indices are clipped in a manner consistent with the
  10.     handling of normal slices.
  11. None
复制代码
S.indices(len) -> (start, stop, stride)
自动将 slice 适配到 长度为 len 的对象上
  1. print(slice(None, 10, 2).indices(5))
  2. print(slice(-3, None, None).indices(5))
复制代码
  1. (0, 5, 2)
  2. (2, 5, 1)
复制代码
  1. print('ABCDE'[:10:2])  # 等价于
  2. print('ABCDE'[0:5:2])
  3. print('ABCDE'[-3:])  # 等价于
  4. print('ABCDE'[2:5:1])
复制代码
  1. ACE
  2. ACE
  3. CDE
  4. CDE
复制代码
  1. # Vector_v2
  2. class Vector:
  3.     typecode = 'd'
  4.     def __init__(self, components) -> None:
  5.         self._componeents = array(self.typecode, components)
  6.     def __iter__(self):
  7.         return iter(self._componeents)
  8.     ################################# NEW ######################################
  9.     def __getitem__(self, index):
  10.         cls = type(self)
  11.         if isinstance(index, slice):  # slice 切片
  12.             return cls(self._componeents[index])
  13.         elif isinstance(index, numbers.Integral):  # int 索引
  14.             return self._componeents[index]
  15.         else:  # 抛出异常
  16.             msg = '{cls.__name__} indices must be integers'
  17.             raise TypeError(msg.format(cls=cls))
  18.     def __repr__(self):
  19.         components = reprlib.repr(self._componeents)  # array('d', [1, 2, 3, 4, ...])
  20.         components = components[components.find('['):-1]  # [1, 2, 3, 4, ...]
  21.         return 'Vector({})'.format(components)  # Vector([1, 2, 3, 4])
  22.    
  23.     def __str__(self):
  24.         return str(tuple(self))
  25.    
  26.     def __bytes__(self):
  27.         return (bytes([ord(self.typecode)]) + bytes(self._componeents))
  28.     def __eq__(self, other):
  29.         return tuple(self) == tuple(other)
  30.     def __abs__(self):
  31.         return math.sqrt(sum(x * x for x in self))
  32.     def __bool__(self):
  33.         return bool(abs(self))
  34.    
  35.     def __len__(self):
  36.         return len(self._componeents)
  37.     @classmethod
  38.     def frombytes(cls, octets):
  39.         typecode = chr(octets[0])
  40.         memv = memoryview(octets[1:]).cast(typecode)
  41.         return cls(memv)
复制代码
  1. v7 = Vector(range(7))
  2. print(1, v7[-1])
  3. print(2, v7[1:4])
  4. print(3, v7[-1:])
  5. print(4, v7[1, 2])  # 报误,不支持多维切片
复制代码
  1. 1 6.0
  2. 2 (1.0, 2.0, 3.0)
  3. 3 (6.0,)
  4. ---------------------------------------------------------------------------
  5. TypeError                                 Traceback (most recent call last)
  6. Cell In [28], line 5
  7.       3 print(2, v7[1:4])
  8.       4 print(3, v7[-1:])
  9. ----> 5 print(4, v7[1, 2])
  10. Cell In [22], line 19, in Vector.__getitem__(self, index)
  11.      17 else:  # 抛出异常
  12.      18     msg = '{cls.__name__} indices must be integers'
  13. ---> 19     raise TypeError(msg.format(cls=cls))
  14. TypeError: Vector indices must be integers
复制代码
  1. # Vector_v3 动态存取属性
  2. class Vector:
  3.     typecode = 'd'
  4.     shortcut_numes = 'xyzt'
  5.     def __init__(self, components) -> None:
  6.         self._componeents = array(self.typecode, components)
  7.     def __iter__(self):
  8.         return iter(self._componeents)
  9.    
  10.     def __getitem__(self, index):
  11.         cls = type(self)
  12.         if isinstance(index, slice):  # slice 切片
  13.             return cls(self._componeents[index])
  14.         elif isinstance(index, numbers.Integral):  # int 索引
  15.             return self._componeents[index]
  16.         else:  # 抛出异常
  17.             msg = '{cls.__name__} indices must be integers'
  18.             raise TypeError(msg.format(cls=cls))
  19.     ################################# NEW ######################################
  20.     def __getattr__(self, name):
  21.         cls = type(self)
  22.         if len(name) == 1:
  23.             pos = cls.shortcut_numes.find(name)
  24.             if 0 <= pos < len(self._componeents):
  25.                 return self._componeents[pos]
  26.         
  27.         msg = '{.__name__!r} object has no attribute {!r}'
  28.         raise AttributeError(msg.format(cls, name))
复制代码
通过 __setattr__ 方法防止其修改部分属性
  1. v5 = Vector(range(5))
  2. print(1, v5)
  3. print(2, v5.x)
  4. v5.x = 10
  5. print(3, v5.x)  # v5[0] 变了?
  6. print(4, v5)  # v5 实际上并没有变
复制代码
  1. 1 (0.0, 1.0, 2.0, 3.0, 4.0)
  2. 2 0.0
  3. 3 10
  4. 4 (0.0, 1.0, 2.0, 3.0, 4.0)
复制代码
散列 & 快速等值测试

__hash__
需要依次计算 v[0] ^ v[1] ^ v[2] ...
reduce()
__eq__
  1. # 改进
  2. # Vector_v3 动态存取属性
  3. class Vector:
  4.     typecode = 'd'
  5.     shortcut_numes = 'xyzt'
  6.     def __init__(self, components) -> None:
  7.         self._componeents = array(self.typecode, components)
  8.     def __getattr__(self, name):
  9.         cls = type(self)
  10.         if len(name) == 1:
  11.             pos = cls.shortcut_numes.find(name)
  12.             if 0 <= pos < len(self._componeents):
  13.                 return self._componeents[pos]
  14.         
  15.         msg = '{.__name__!r} object has no attribute {!r}'
  16.         raise AttributeError(msg.format(cls, name))
  17.     ################################# NEW ######################################
  18.     def __setattr__(self, name, value) -> None:
  19.         cls = type(self)
  20.         if len(name) == 1:
  21.             if name in cls.shortcut_numes:
  22.                 error = 'readonly attribute {attr_name!r}'
  23.             elif name.islower():
  24.                 error = "can't set attributes 'a' to 'z' in {cls_name!r}"
  25.             else:
  26.                 error = ''
  27.             if error:
  28.                 msg = error.format(cls_name=cls.__name__, attr_name=name)
  29.                 raise AttributeError(msg)
  30.         super().__setattr__(name, value)  
复制代码
  1. v6 = Vector(range(6))
  2. print(1, v6)
  3. print(2, v6.x)
  4. v6.x = 10
复制代码
  1. 1 <__main__.Vector object at 0x000001BD0AD009A0>
  2. 2 0.0
  3. ---------------------------------------------------------------------------
  4. AttributeError                            Traceback (most recent call last)
  5. Cell In [35], line 5
  6.       2 print(1, v6)
  7.       3 print(2, v6.x)
  8. ----> 5 v6.x = 10
  9. Cell In [34], line 35, in Vector.__setattr__(self, name, value)
  10.      33     if error:
  11.      34         msg = error.format(cls_name=cls.__name__, attr_name=name)
  12. ---> 35         raise AttributeError(msg)
  13.      36 super().__setattr__(name, value)
  14. AttributeError: readonly attribute 'x'
复制代码
  1. # 复习一下 reduce
  2. print(reduce(lambda a, b: a * b, range(1, 6)))
复制代码
  1. 120
复制代码
注:
使用 reduce() 最好提供三个参数 reduce(function, iterable, initializer)
通常, 对于 +, |, ^ initializer = 0
通常, 对于 *, &      initializer = 1
__eq__ 要和 __hash__ 在一起哦~
  1. # 计算多个数异或与
  2. print(reduce(lambda a, b: a ^ b, range(233)))
  3. print(reduce(xor, range(233)))
  4. n = 0
  5. for i in range(1, 233):
  6.     n ^= i
  7. print(n)
复制代码
all() 只要有一个是 False , 整个都是 False
上面两种 __eq__ 效果相等
  1. 232
  2. 232
  3. 232
复制代码
  1. # Vector_v3 加入 __hash__
  2. class Vector:
  3.     typecode = 'd'
  4.     def __init__(self, components) -> None:
  5.         self._componeents = array(self.typecode, components)
  6.     def __iter__(self):
  7.         return iter(self._componeents)
  8.     ################################# OLD ######################################
  9.     def __eq__(self, other):
  10.         return tuple(self) == tuple(other)
  11.     ################################# NEW ######################################
  12.     def __hash__(self):
  13.         hashes = (hash(x) for x in self._componeents)
  14.         return reduce(xor, hashes, 0)
  15.    
复制代码
zip_longest() 按照最长的 iter 迭代, 空的用 fillvalue 补齐
format 格式化
目标: 得到球面坐标
  1. # 使用【映射归约】实现 __hash__ (map, reduce)
  2. # Vector_v3 加入 __hash__
  3. class Vector:
  4.     typecode = 'd'
  5.     def __init__(self, components) -> None:
  6.         self._componeents = array(self.typecode, components)
  7.     def __iter__(self):
  8.         return iter(self._componeents)
  9.     ################################# NEW ######################################
  10.     def __hash__(self):
  11.         hashes = map(hash, self._componeents)  # 计算各个元素的hash
  12.         return reduce(xor, hashes, 0)
  13.     ################################# NEW ######################################
  14.     def __eq__(self, other):
  15.         if len(self) != len(other):  # 长度不相等, 直接 False
  16.             return False
  17.         for a, b in zip(self, other):  # 判断每个对应元素是否相等
  18.             if a != b:
  19.                 return False
  20.         return True
  21.     ################################# NEW ######################################
  22.     # 另一种方法
  23.     def __eq__(self, other):
  24.         return len(self) != len(other) and all(a == b for a, b in zip(self, other))
复制代码
  1. # 回忆一下 zip()
  2. print(1, zip(range(3), 'ABC'))
  3. print(2, list(zip(range(3), 'ABC')))
  4. print(3, list(zip(range(3), 'ABC', [0, 1, 2, 3])))  # 什么???一个可迭代对象迭代完了, 就不迭代了
  5. from itertools import zip_longest
  6. print(4, list(zip_longest(range(3), 'ABC', [0, 1, 2, 3], fillvalue=1)))  # 按照最长的iter迭代, 空的用 fillvalue 补齐
复制代码
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表