zoukankan      html  css  js  c++  java
  • 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.增量赋值不是一个原子操作。它虽然抛出了异常,但还是完成了操作。

  • 相关阅读:
    SSL证书安装指引
    腾讯云中ssL证书的配置安装
    微信小程序:微信登陆(ThinkPHP作后台)
    TPshop学习(8)微信支付
    LNMP安装Let’s Encrypt 免费SSL证书方法:自动安装与手动配置Nginx
    ruby文档
    tesseract-ocr图片识别开源工具
    Python读写文件
    百度贴吧的网络爬虫(v0.4)源码及解析
    中文分词库
  • 原文地址:https://www.cnblogs.com/wjw2018/p/10640155.html
Copyright © 2011-2022 走看看