• 2.6 序列的增量赋值


    我们把讨论集中在增量加法(+=)上,但是这些概念对 *= 和其他增量运算符来说都是一样的。

    += 背后的特殊方法是 __iadd__ (用于“就地加法”)。但是如果一个类 没有实现这个方法的话,Python 会退一步调用 __add__ 。考虑下面这 个简单的表达式:

    >>> a += b

    如果 a 实现了 __iadd__ 方法,就会调用这个方法。同时对可变序列 (例如 list、bytearray 和 array.array)来说,a 会就地改动,就 像调用了 a.extend(b) 一样。但是如果 a 没有实现 __iadd__ 的话,a += b 这个表达式的效果就变得跟 a = a + b 一样了:首先计算 a + b,得到一个新的对象,然后赋值给 a。也就是说,在这个表达式中, 变量名会不会被关联到新的对象,完全取决于这个类型有没有实现 __iadd__ 这个方法。 上面所说的这些关于 += 的概念也适用于 *=,不同的是,后者相对应的 是 __imul__。

    >>> l = [1, 2, 3] 
    >>> id(l) 
    4311953800>>> l *= 2 
    >>> l 
    [1, 2, 3, 1, 2, 3] 
    >>> id(l) 
    4311953800>>> t = (1, 2, 3) 
    >>> id(t) 
    4312681568>>> t *= 2 
    >>> id(t) 
    4301348296  

    对不可变序列进行重复拼接操作的话,效率会很低,因为每次都有一个 新对象,而解释器需要把原来对象中的元素先复制到新的对象里,然后再追加新的元素。只有str 是一个例外,因为对字符串做 += 实在是太普遍了,所以 CPython 对它做了优化。为 str 初始化内存的时候,程序会为它留出额外的可扩展空间,因此进行增量操作的时候,并不会涉及复制原有字符串到新位置这类操作。

    一个谜题:

    >>> t = (1, 2, [30, 40]) 
    >>> t[2] += [50, 60]

    结果会发生什么呢?没人料到的结果:t[2] 被改动了,但是也有异常抛出。

    以下两张截图,分别代表示例中 t 的初始和最终状态:

    初始:

    最终:

    首先将 s[a] 的值存入 TOS(Top Of Stack,栈的顶端),然后计算 TOS += b。这俩步能够完成,是因为 TOS 指向的是一个可变对象(也就是上图中的列表)。但s[a] = TOS 赋值这一步失败,是因为 s 是不可变的元组。

    至此我得到了 2 个教训:
    1.不要把可变对象放在元组里面。
    2.增量赋值不是一个原子操作。它虽然抛出了异常,但还是完成了操作。

  • 相关阅读:
    周末总结
    大数据开源框架技术汇总
    oracle迁移mysql总结
    梯度下降
    BFC的概念
    元素类型
    window10安装tensorflow
    学习使用git
    设计模式中的关系
    拟合圆
  • 原文地址:https://www.cnblogs.com/wjw2018/p/10640155.html
Copyright © 2020-2023  润新知