推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
woniu127
V2EX  ›  Python

python append 为何如此奇怪?

  •  
  •   woniu127 · Nov 20, 2016 · 5428 views
    This topic created in 3466 days ago, the information mentioned may be changed or developed.

    这是一段测试代码

    li1 = []
    li2 = []
    ki = [0]
    j = 0
    for i in range(10):
        ki[0] = ki[0]+1
        j = j+1
        li1.append(ki)
        li2.append(j)
    print(li1)
    print(li2)
    

    这是输出:

    [[10], [10], [10], [10], [10], [10], [10], [10], [10], [10]]
    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    

    为何 append()参数是列表类型时,每次循环都会改变 li1 中的值???

    Supplement 1  ·  Nov 20, 2016
    谢谢大家,今天又学到了很多,是我基础太薄弱了,对对象和引用理解的都不够。
    18 replies    2016-11-21 16:18:09 +08:00
    laoyur
        1
    laoyur  
       Nov 20, 2016   ❤️ 1
    li1 = []
    li2 = []
    ki = [0]
    j = 0
    for i in range(10):
    ki[0] = ki[0]+1
    j = j+1
    li1.append(list(ki))
    li2.append(j)
    print(li1)
    print(li2)
    kindjeff
        2
    kindjeff  
       Nov 20, 2016 via iPhone   ❤️ 1
    因为你只是 append 了 ki 对象的引用,而 list 是可变的,内容改变了,你 append 的进去的仍然是 ki 的引用。
    woniu127
        3
    woniu127  
    OP
       Nov 20, 2016
    @kindjeff 也就是说只要 append 的是不可变的对象就没问题了
    exoticknight
        4
    exoticknight  
       Nov 20, 2016   ❤️ 1
    明显是没搞清楚 python 是引用优先的
    估计楼主肯定还会在用默认参数是对象的时候出现问题
    woniu127
        5
    woniu127  
    OP
       Nov 20, 2016
    @exoticknight 学 python 还不够,认识还不够深,只能一个坑一个坑的踩了
    BiggerLonger
        6
    BiggerLonger  
       Nov 20, 2016 via Android
    應該是用 list.extend 方法
    wwqgtxx
        7
    wwqgtxx  
       Nov 20, 2016
    又一个被 C 的值传递坑的
    IanPeverell
        8
    IanPeverell  
       Nov 20, 2016   ❤️ 1
    因为 append(ki)的时候传递的是 ki 的地址,所以如果想不改变值可以这么些 li.append([x for x in ki])
    mornlight
        9
    mornlight  
       Nov 20, 2016 via iPhone   ❤️ 1
    第一个 list 里到最后是 10 个指向同一块内存的相同元素
    dtfm
        10
    dtfm  
       Nov 20, 2016   ❤️ 1
    ```
    li1 = []
    li2 = []
    ki = [0]
    j = 0
    for i in range(10):
    ki[0] = ki[0]+1
    j = j+1
    li1.append(ki[0])
    li2.append(j)
    print(li1)
    print(li2)
    ```
    在 ki 里面加一个 ki[0],这样每次都添加就好了
    lzhCoooder
        11
    lzhCoooder  
       Nov 20, 2016   ❤️ 1
    浅拷贝 你 li1 里的每个元素都是指向的或者说引用的同一个 ki
    li2 里之所以不这样 是因为 j 是不可变类型,没加一次创建一个新 j
    rebirth2
        12
    rebirth2  
       Nov 20, 2016 via Android   ❤️ 1
    可以用 list slice, ki[:]这样直接创建新 list ,不过比较黑科技。。
    onlyice
        13
    onlyice  
       Nov 20, 2016 via Android
    把代码粘贴到这里看看就懂了 http://www.pythontutor.com/visualize.html#py=2
    bravecarrot
        14
    bravecarrot  
       Nov 20, 2016
    @onlyice 这个有意思啊 哈哈哈哈 不用开 ide 看了
    bravecarrot
        15
    bravecarrot  
       Nov 20, 2016   ❤️ 1
    @woniu127 ls 的 @exoticknight 可能说的是这个

    ``` python
    def foo(x, l = []):
    l.append(x)
    return l
    y = foo(6)
    z = foo(8)

    p = foo(2, [3])
    q = foo(4, [5])
    print y
    print z
    print p
    print q
    ```

    output:
    [6, 8]
    [6, 8]
    [3, 2]
    [5, 4]
    *************
    同理,函数每次使用的默认参数的 list 都是同一个,每次都会操作它
    20015jjw
        16
    20015jjw  
       Nov 20, 2016
    lz 你显然没理解 reference 的概念..
    enenaaa
        17
    enenaaa  
       Nov 21, 2016
    我也觉得奇怪,其他语言标准库的 append 函数多半是值插入, python 非得是引用。
    然后用个不常见 extend 函数代替。 比较坑
    diydry
        18
    diydry  
       Nov 21, 2016   ❤️ 1
    对象变动(Mutation)
    Python 中可变(mutable)与不可变(immutable)的数据类型让新⼿很是头痛。 简单的说, 可
    变(mutable)意味着"可以被改动", ⽽不可变(immutable)的意思是“常量(constant)”。 想把脑
    筋转动起来吗? 考虑下这个例⼦:
    foo = ['hi']
    print(foo)
    # Output: ['hi']
    bar = foo
    bar += ['bye']
    print(foo)
    # Output: ['hi', 'bye']
    刚刚发⽣了什么? 我们预期的不是那样!我们期望看到是这样的:
    foo = ['hi']
    print(foo)
    # Output: ['hi']
    bar = foo
    bar += ['bye']
    print(foo)
    # Output: ['hi']
    print(bar)
    # Output: ['hi', 'bye']
    这不是⼀个 bug 。 这是对象可变性(mutability)在作怪。 每当你将⼀个变量赋值为另⼀个可
    变类型的变量时, 对这个数据的任意改动会同时反映到这两个变量上去。 新变量只不过是
    ⽼变量的⼀个别名⽽已。 这个情况只是针对可变数据类型。 下⾯的函数和可变数据类型让
    你⼀下就明⽩了:
    def add_to(num, target=[]):
    target.append(num)
    return target
    add_to(1)
    # Output: [1]
    add_to(2)
    # Output: [1, 2]
    add_to(3)
    # Output: [1, 2, 3]
    Python 进阶
    对象变动 Mutation 51 你可能预期它表现的不是这样⼦。 你可能希望, 当你调⽤add_to 时, 有⼀个新的列表被
    创建, 就像这样:
    def add_to(num, target=[]):
    target.append(num)
    return target
    add_to(1)
    # Output: [1]
    add_to(2)
    # Output: [2]
    add_to(3)
    # Output: [3]
    啊哈!这次又没有达到预期, 是列表的可变性在作怪。 在 Python 中当函数被定义时, 默认
    参数只会运算⼀次, ⽽不是每次被调⽤时都会重新运算。 你应该永远不要定义可变类型的
    默认参数, 除⾮你知道你正在做什么。 你应该像这样做:
    def add_to(element, target=None):
    if target is None:
    target = []
    target.append(element)
    return target
    现在每当你在调⽤这个函数不传⼊target 参数的时候, ⼀个新的列表会被创建。 举个例
    ⼦:
    add_to(42)
    # Output: [42]
    add_to(42)
    # Output: [42]
    add_to(42)
    # Output: [42]
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   3092 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 169ms · UTC 11:36 · PVG 19:36 · LAX 04:36 · JFK 07:36
    ♥ Do have faith in what you're doing.