decimal.Macimal设置算术运算上下文的精度
- 精度变化可能导致x不等于+x
import decimal
ctx = decimal.getcontext()
# 默认是28位精度
ctx.prec = 40
ont_third = decimal.Decimal('1') / decimal.Decimal('3')
one_third
one_third == +one_third
# 设为28位
# 这里如果是在notebook中,同一行算一个上下文,ctx.prec只对同一上下文起作用
ctx.prec = 28
one_third == +one_third # 此时不相等
+one_third
- +Counter仅保留大于零的计数器
ct = Counter('abracadabra')
ct
ct['r'] = -3
ct['d'] = 0
ct
+ct # 只保留大于零的计数
重载向量加法运算符
···
def __add__(self, other):
try:
pairs = itertools.zip_longest(self, other, fillvalue=0.0)
return Vector(a + b for a, b in pairs)
except TypeError:
return NotImplemented
def __radd__(self, other):
return self + other
···
# 正常情况下是:v1 + (10, 20, 30)
# 而(10, 20, 30) + v1 调用的是tuple的__add__方法,由于没有Vector,会报TypeError
# 左操作数首先调用__add__方法,如果返回NotImplemented表明它不知道如何处理右操作数,那么就会调用__radd__方法
# NotImplemented是特殊的单例值,不是异常
# __radd__可以实现(10, 20, 30) + v1
重载标量乘法运算符
···
def __mul__(self, scalar):
if isinstance(scalar, numbers.Real):
return Vector(n * scalar for n in self)
else:
return NotImplemented
def __rmul__(self, scalar):
return self * scalar
···
v1 = Vector([1.0, 2.0, 3.0])
14 * v1
改进的Vector.__eq__方法
···
def __eq__(self, other):
if isinstance(other, Vector):
return (len(self) == len(other) and all(a == b for a, b in zip(self, other)))
else:
return NotImplemented
···
va = Vector([1.0, 2.0, 3.0])
vb = Vector(range(1, 4))
va == vb
vc = Vector([1, 2])
t3 = (1, 2, 3)
va == t3
增量赋值运算符
- 增量赋值不会修改不可变目标,而是新建实例,然后重新绑定
如果类没有实现__iadd__()方法,a += b 与 a = a + b 一样,对不可变类型是预期的行为。
v1 = Vector([1, 2, 3])
v1_alias = v1 # 保留v1副本v1_alias
id(v1)
v1 += Vector([4, 5, 6])
v1
id(v1) # 此时的v1 id改变了
v1_alias # 原来的实例没有改变
v1 *= 11
v1
id(v1) # 与增量加法一样
v1_alias
自增运算符
import random
import abc
class Tombola(abc.ABC):
@abc.abstractmethod
def load(self, iterable):
""""""
@abc.abstractmethod
def pick(self):
""""""
def loaded(self):
return bool(self.inspect())
def inspect(self):
items = []
while True:
try:
items.append(self.pick())
except LookupError:
break
self.load(items)
return tuple(sorted(items))
class BingoCage(Tombola):
def __init__(self, items):
self._randomizer = random.SystemRandom()
self._items = []
self.load(items)
def load(self, items):
self._items.extend(items)
self._randomizer.shuffle(self._items)
def pick(self):
try:
return self._items.pop()
except IndexError:
raise LookupError('pick from empty BingoCage')
def __call__(self):
self.pick()
import itertools
class AddableBingoCage(BingoCage):
def __add__(self, other):
if isinstance(other, Tombola):
return AddableBingoCage(self.inspect() + other.inspect())
else:
return NotImplemented
def __iadd__(self, other):
if isinstance(other, Tombola):
other_iterable = other.inspect()
else:
try:
other_iterable = iter(other)
except TypeError:
self_cls = type(self).__name__
msg = "right operand in += must be {!r} or an iterable"
raise TypeError(msg.format(self_cls))
self.load(other_iterable)
return self
# 加运算符
vowels = 'AEIOU'
globe = AddableBingoCage(vowels)
globe.inspect()
globe2 = AddableBingoCage('XYZ')
globe3 = globe + globe2
len(globe3.inspect())
void = globe + [10, 20] # AddableBingoCage实例无法与列表相加
# 自加运算符
globe += globe2
globe.inspect()
globe += ['M', 'N']
globe.inspect()
globe is globe_orig
globe += 1 # AddableBingoCage实例不能与非可迭代对象相加
总结
- decimal.Decimal设置上下文精度
- 重加载__add__方法,NotImplemented单例值,以及__radd__方法的作用
- 使用isinstance判断重载标量乘法运算符的参数检查numbers.Real抽象基类,numbers.Real涵盖类我们所需的全部类型
- __eq__方法中使用isinstance判断类型
- 增量赋值运算符对可变或不可变实例的行为
- 加运算符和自加运算符的差别
流畅的Python