zoukankan      html  css  js  c++  java
  • Python笔记005-神奇的+=

    Python笔记005-神奇的+=

    以下是我学习《流畅的Python》后的个人笔记,现在拿出来和大家共享,希望能帮到各位Python学习者。

    首次发表于: 微信公众号:科技老丁哥,ID: TechDing,敬请关注。

    本篇主要知识点:

    1. 序列的+=操作其本质上是类实现了__iadd__方法,而*=运算是实现了__imul__方法,这种运算叫做“就地修改”。

    2. 对可变序列进行+=或*=运算非常方便快捷,但对不可变序列进行进行这些操作却有些问题,特别是多次频繁的增量运算,会极大降低运算效率。

    3. 不要把可变序列放到元组里,要不然对里面的可变序列进行操作会得到意想不到的结果。

    1. 序列的就地修改

    在Python中,常见的增量赋值运算符有+=和*=,这两者的行为方式一样,所以可以用+=来做演示。

    一个实例能够进行+=运算,是因为该实例的类实现了__iadd__方法,同理,能够进行*=运算,是因为实现了__imul__方法,这两种方法都叫做“就地修改”方法。

    就地修改是指,eg,a+=b会直接在原来的变量a所在内存空间上进行修改赋值,而不像a=a+b一样,先计算出a+b的值,然后在赋值给一个新的变量a,这个新变量a虽然名称一致,但是内存空间和原来的a不一样。

    对于可变序列,一般都实现了__iadd__方法,即都支持+=操作,但不可变序列却没有实现该方法,对不可变序列进行+=操作,本质上是进行a=a+b一样的操作。如下代码可见一斑。

    # 可变序列使用就地运算法
    alist=[1,2,3]
    print(id(alist)) # 查看变量alist的内存地址 # 2258694188232
    alist *=2 # 复制两份
    print(alist) # [1, 2, 3, 1, 2, 3]
    print(id(alist)) # 2258694188232 #内存地址一样
    
    # 不可变序列无法就地运算
    blist=(1,2,3)
    print(id(blist)) # 2258694935104
    blist*=2
    print(blist) # (1, 2, 3, 1, 2, 3)
    print(id(blist)) # 2258693897864,此处的内存地址不一样
    

    可以看出,可变序列支持就地修改,会在原来的内存空间上修改变量值,而不可变序列无法就地修改,会新开辟一部分内存空间,用于新变量的赋值。

    了解这种特性有什么用了?由于不可变序列每次运算会重新开辟内存空间,完成赋值再追加新元素,所以非常耗时,效率非常低,对少量数据觉察不出来,但大量数据操作情况下,运行速度越来越慢,而且这种情况难以debug,因为可变序列时运算速度正常,所以尽量不要对不可变序列进行这种操作。

    有一个例外:str类型是不可变序列,但是字符串拼接操作非常常见,所以Cpython对其进行了特别优化,因此进行增量操作时,不会涉及到复制原字符串到新内存位置的操作。

    2. 一个+=的谜题

    如果不可变序列中包含有可变序列,并且对可变序列进行增量赋值操作,那么会得到什么?

    a=(10,20,[30,40])
    a[2]+=[50,60]
    # 会直接报错:TypeError: 'tuple' object does not support item assignment
    

    上面的代码会直接报错:TypeError: 'tuple' object does not support item assignment,说明tuple是不可变序列,不支持修改,但是打印出a会发现:

    print(a) # (10, 20, [30, 40, 50, 60])

    说明不仅报错,而且还正确执行了增量赋值操作。很神奇吧。

    所以这个谜题得到的经验是:不要把可变序列放到元组里,要不然对里面的可变序列进行操作会得到意想不到的结果。

    所以上面的代码可以修改为:

    b=[10,20,[30,40]]
    b[2]+=[50,60]
    print(b) # [10, 20, [30, 40, 50, 60]]
    

    运行正常,所以啊,以后多用list,少用tuple。

    首次发表于: 微信公众号:科技老丁哥,ID: TechDing,敬请关注。

    本文所有代码都已经上传到我的github,欢迎下载

    参考资料:

    1. 《流畅的Python》,Luciano Ramalho (作者) 安道 , 吴珂 (译者)。
  • 相关阅读:
    git学习(一)
    访问注解(annotation)的几种常见方法
    对于元数据的理解
    常用注解介绍
    深入理解Java的接口和抽象类
    POI依据类型设置导出格式
    java泛型
    java 向上转型和向下转型
    cell设置背景颜色为啥不起作用
    Java反射获取对象VO的属性值(通过Getter方法)
  • 原文地址:https://www.cnblogs.com/RayDean/p/11634363.html
Copyright © 2011-2022 走看看