函数外修改列表的反射问题

0 投票
2 回答
872 浏览
提问于 2025-04-28 19:37

我最近在edx上参加麻省理工学院的Python入门课程。教授正在讲解如何把函数当作对象来使用。我在一个代码示例中有些困惑,那个代码定义了一个函数,并且接收两个参数,一个是列表,另一个是另一个函数。

def applyToEach(l,f):
    for i in range(len(l)):
        l[i]=f(l[i])

l=[1,-2,3.4]

applyToEach(l,abs)
print(l)

我想问的是:在函数内部对列表的修改,为什么在函数外部也能看到这些变化?我认为,当我打印这个列表的时候,它不应该有任何变化,因为我所有的修改都是在函数内部完成的。我觉得不同的函数会创建不同的环境,而之前的练习中也是这样,所以在我们的主环境中不会反映出变化。请纠正我哪里错了,因为当我运行这个代码时,修改后的列表确实被打印出来了。那么我在这个逻辑上哪里出错了呢?

暂无标签

2 个回答

0

这个列表是作为参数传递给函数的。当你在Python中创建一个列表时,系统会为它分配一个内存位置来存储数据。现在,当你传递这个列表(或者对它进行任何操作)时,实际上是传递了这个内存位置。因此,无论你对列表进行什么修改,都会直接反映在这个列表上。而这个内存位置是不会改变的。所以,不管你是在函数外部还是内部访问这个列表,它指向的都是同一个内存位置。因此,在函数内部做的更改在外部也是可见的。

假设这个列表是

Fruit = ['Mango', 'Apple', 'Orange']

当我们把列表赋值给变量Fruit时,系统为它分配了一个内存位置(比如说是4886),所以 id(Fruit) = 4886。现在,假设列表'Fruit'被传递给一个函数,在这个函数内部对它进行如下修改:

def fun(li=[]):
    li[1] = 'Grape'
    li.extend(['Apple', 'Banana'])

在幕后发生的事情是,当列表作为参数传递给函数时,实际上是传递了这个列表的引用(或者说地址)。所以,即使你在函数内部访问并修改这个列表,它也会修改原始列表,因为它指向的是同一个列表的引用。因此,列表的引用会相应地更新。

即使在修改之后,列表的内容也会显示为在函数内部的样子。但是,内存位置仍然是一样的,也就是 id(Fruit) = 4886

即使你把列表赋值给一个新的列表,比如说 temp = Fruit,情况也是一样的。temp会指向和Fruit相同的内存位置,两个列表都会指向同一个地方。

为了避免这种情况,你需要做的是:

temp = list(Fruit)

这样会创建一个新的列表,并分配一个新的内存位置,因此 id(Fruit)id(temp) 会不同。

以下链接可以帮助你更好地理解Python中列表的工作原理: http://www.python-course.eu/deep_copy.php

0

因为它们是同一个对象。你可以通过 id()is 来验证这一点。

#!/usr/bin/env python
#-*- coding:utf-8 -*-

def applyToEach(l,f):
    print 'inside id is: ', id(l)
    for i in range(len(l)):
        l[i]=f(l[i])

l=[1,-2,3.4]
print 'outside id is: ', id(l)

applyToEach(l,abs)
print(l)

在Python中,把一个值传给一个函数就相当于 'para = value'。不过,'=' 只是给这个值添加了一个引用,并没有复制这个值。

撰写回答