【问题标题】:How does Python referencing work?Python 引用是如何工作的?
【发布时间】:2012-04-01 06:14:58
【问题描述】:

我对 Python 引用感到困惑。考虑以下示例:

我的任务:编辑列表中的每个元素

d = { 'm': [1,2,3] }
m = d['m']
m = m[1:]   # m changes its reference to the new sliced list, edits m but not d (I wanted to change d)

同样:

d = { 'm': [1,2,3] }
m = d['m']
m = m[0]    # As per python referencing, m should be pointing to d['m'] and should have edited d

在python中,一切都是通过引用进行的,那么什么时候创建一个新对象? 我们是否总是需要来自copy 模块的copydeepcopy 来制作对象副本?

请澄清。

【问题讨论】:

  • 如果你是视觉学习者,here's 一个很好的教程
  • 非常有帮助的评论 @jordoex 先生 - 非常棒!

标签: python reference pass-by-reference pass-by-value


【解决方案1】:

在python中,一切都是通过引用来实现的

在 Python 中,一切都是引用,引用通过值传递。

如果您想使用这些术语。但这些术语使事情更难理解。

简单得多:在 Python 中,变量是对象的名称。 = 用于更改名称所指的对象。左侧可以引用现有对象的一部分,在这种情况下,通过替换该部分来更改整个对象。这是因为反过来,对象并没有真正包含它的部分,而是包含更多的名称,这可能导致开始引用不同的东西。

那么什么时候创建一个新对象?

对象是在创建时创建的(通过使用类构造函数,或者在具有文字表示的内置类型的情况下,通过键入文字)。我不明白这与您的其他问题有何关系。

m = m[1:]   # m changes its reference to the new sliced list

是的,当然。现在m 指的是评估m[1:] 的结果。

edits m but not d (I wanted to change d)

是的,当然。为什么改变d?这不是某种魔法,它只是评估d['m'] 的结果。两条线都发生完全相同的事情。

让我们看一个更简单的例子。

m = 1
m = 2

这是否会导致1 变为2?不,当然不。整数是不可变的。但同样的事情正在发生:m 被导致命名一件事,然后命名另一件事。

或者,另一种方式:如果“引用”按您期望的方式工作,那么m = m[1:] 行将是递归的。您期望它的意思是“在您看到m 的任何地方,将其视为表示m[1:]”。那么,在这种情况下,m[1:] 实际上意味着 m[1:][1:],然后意味着 m[1:][1:][1:],等等。

【讨论】:

    【解决方案2】:

    Ethan Furman 很好地解释了 Python 内部的工作原理,我不再赘述。

    由于m确实代表了字典中的列表,你可以修改它。您只是不能将它重新分配给新的东西,当您使用 = 将其等同于新切片时会发生这种情况。

    例如切掉列表的第一个元素:

    >>> m[0:1] = []
    >>> d
    {'m': [2, 3]}
    

    【讨论】:

    • 我讨厌投反对票,如果我做错了什么,我想找出原因。此答案已在 Python 2.7 中使用推荐的惯用语进行了测试:docs.python.org/library/stdtypes.html#mutable-sequence-types
    • 要从列表中删除项目或切片,我通常更喜欢更明确的del m[0]。当然,这是一种风格偏好,我不知道为什么有人不赞成。我的猜测是,许多看似随机的否决票都是由于智能手机的显示屏太小造成的。
    【解决方案3】:

    在 Python 中,变量不是一个存放东西的盒子,它是一个指向对象的名称。在您的代码中:

    • d = { 'm': [1,2,3] } --> 将名称 d 绑定到字典
    • m = d['m'] --> 将名称 m 绑定到一个列表
    • m = m[1:] --> 将名称 m 绑定到另一个列表

    您的第三行并没有改变m 本身,而是m 指向的内容。

    要编辑列表中的元素,您可以:

    m = d['m']
    for i, item in enumerate(m):
        result = do_something_with(item)
        m[i] = result
    

    【讨论】:

    • 除了enumerate 之外,还有其他方法可以编辑此列表的每个元素吗?
    • “指向对象的名称”可能会令人困惑,因为“指针”不是 Python 术语。我只想说“它是一个对象的名称”。
    • “你的第三行并没有改变 m 本身,但是 m 指向什么”对于读者来说可能有点不清楚。更明确地说:“第三行将 m 从指向一件事 (d['m']) 更改为指向另一件事 ([2,3])。”
    • @Ethan Furman “你的第三行并没有改变 m 本身,而是改变 m 指向的内容。”据我了解,python 字符串被认为是“不可变的”,这是否意味着,与其更改内存地址处的字符串,不如将名称绑定到具有不同名称的不同内存地址?
    • @ScottyBlades:可变/不可变无关紧要,内存地址通常不是 Python 程序员关心的问题。在a = b 中,a 已绑定到(或者是另一个名称)b 所指的同一对象。 a 不是单独的副本;如果b 是可变的和变异的,那么a 也将显示更改,因为ab 只是同一事物的两个名称;同样,如果你做了del b,你并没有删除b所指的对象,只是删除了名称b——你仍然可以通过它的a名称访问该对象。
    猜你喜欢
    • 1970-01-01
    • 2017-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-18
    • 1970-01-01
    相关资源
    最近更新 更多