为啥python不允许通过引用传递变量

Posted

技术标签:

【中文标题】为啥python不允许通过引用传递变量【英文标题】:Why doesn't python allow passing variables by reference为什么python不允许通过引用传递变量 【发布时间】:2013-08-22 02:34:46 【问题描述】:

这可能更适合讨论组,但我不精通 语言的内部(甚至语言本身)。无论如何,困扰我的是:

如果 python 允许使用 nonlocal 关键字对外部作用域进行干扰(副作用),那么为什么它不允许通过以下方式对函数参数进行类似的干扰 允许通过引用传递参数:

现在可以:

>>> def outer():
       x = 1
       def inner():
           nonlocal x
           x = 2
           print("inner:", x)
       inner()
       print("outer:", x)

>>> outer()
inner: 2
outer: 2

为什么不 - 或者如果我们有什么可能会出错:

>>> def outer():
       x = 1
       def inner(byref x):
           x = 2
           print("inner:", x)
       inner(x)
       print("outer:", x)

>>> outer()
inner: 2
outer: 2

(使用一些关键字,如 'byref' 或 'nonlocal,仅用于说明)。

【问题讨论】:

【参考方案1】:

在 Python 中,int 对象是不可变的。当您认为您正在更改 x 的值时,您实际上是在不同的内存位置创建了一个新的 int 并让 x 引用它。

【讨论】:

好的,这是它的内部工作,这里很少有问题讨论这个问题。但是是什么阻止我们使用相同的技术来模拟通过引用传递 - 即创建一个新对象并让内部函数更改外部名称的引用?类似于 nonlocal 正在做的事情。 @BaselShishani,什么都没有。例如,你可以传递x = [1],然后说x[0] = 2,但你不能隐藏x 不再是真正的int 的事实 @BaselShishani 这不仅仅是一个实现细节。这是 Python 如何处理对其对象的引用的基本概念。这与 C++ 或 Java 等语言中的概念非常不同。 在java中你也总是按值传递引用。在 C 中也是如此。我的感觉是允许通过可修改引用的语言数量实际上是少数 @downvoter。您可以随意投反对票,但这不会改变 Python 引用的工作方式。对于错误的答案,我会保留我的反对票。【参考方案2】:

Python 总是使用 参考值 传递参数。请参阅此处的this 链接,尤其是 rad 用户提供的详细回复 pepr

this 链接也非常详细地讨论了参数是如何在 python 中传递的,并且 pass-by-reference 可以被模拟 - 请参阅这个问题的已接受答案的编辑。

如果您想深入研究 - 请参阅这篇 Effbot 文章,其中还包含围绕同一主题的一些辩论/讨论。

【讨论】:

为什么 Python 设计者认为提供标准机制以允许函数更改外部名称的引用目标不是一个好主意。 这是一个很好的问题,我的猜测是来自 Java(Script) 的抱怨者在没有访问命名空间的情况下无法处理编程,这说服了他们走这条路。请注意,在真正的 Python (2.7) 中,绝对不允许这样做。我应该提一下,函数 args 中缺少关键字可能与任何事物一样具有美学意义。到目前为止,它一直被避免,Guido 不会把它放在像 nonlocal 这样愚蠢的东西对面的像对称这样微不足道的东西上。 @ap Python 比 Java 和 javascript 都要老:它的第一个版本是在 1991 年(Java 和 JS 是在 1995 年发布的)。而且它的语义更古老,可以追溯到 1980 年代的 ABC。 @Daniel 这是真的!我的怀疑是,如果不是因为这些语言和其他语言自最初成立之日起相对普及,3.x(绝对是在它们之后)可能不会包含我心目中的丑陋和- 大部分没用nonlocaledit 我正在为此失去互联网积分!帮助!【参考方案3】:

这是不可能的,因为 Python 根本没有“变量”的概念。 Python 程序中的所有名称都是对对象的引用。因此,您只能在调用函数或方法时传递对对象的引用。这称为按引用值传递。

但是,无法获得对现有引用的引用。在这方面,引用是 Python 世界中唯一的“非一等”公民。

程序中有两种由名称引用的对象:不可变的(例如字符串、整数、元组)和可变的(例如列表、集合、大多数用户类)。

您可以在可变对象上调用实际上修改对象状态的方法,这看起来类似于在 C++ 等语言中传递对对象的引用。除此之外,通过引用传递在 Python 中没有多大意义。

有关某些链接,请参阅 Prahalad Deshpande 的回答。

【讨论】:

"Python 根本没有“变量”的概念。"当然可以。与 Java、C、Smalltalk、Ruby 以及几乎所有其他语言相同。 @newacct 不,它没有。它只引用堆上的对象。这是 Python 的动态类型系统的基础。如果您编写 x=2 和之后的 x='abc',则引用 x 将引用两个不同类型的不同对象,它们可能独立于引用而存在并被其他引用引用。 所以?这如何使它没有变量? x 是一个变量。它的值是一个引用,因为 Python 中的所有值都是引用。在 Java 中,您还可以编写 Object x = 2;,然后 x = "abc";x 将指向两个不同类型的不同对象。这会使 Java 中的 x 不是变量吗?【参考方案4】:

假设您确实允许引用参数。那么,当您执行以下操作时会发生什么?

def f(byref x):
    print x
    print x
    x = 3

class Foo(object):
    def __init__(self):
        self.count = 0
    @property
    def x(self):
        count += 1
        return count

f(Foo().x)

什么时候调用getter?它被调用了多少次?这是打印两次1,还是打印1,然后是2,或者2,然后是3?由于没有设置器,当您尝试分配给x 时会发生什么?

在另一种语言中,变量是放置东西的地方,而不是值的名称,这不是问题。 Foo().x 将拥有一个内存位置,f 将使用该内存位置作为其x 参数。在 Python 中,它不是那样工作的。你不能传入1的内存位置并改变它的内容,因为那样其他代码会发现1突然神奇地变成了2。与 C++ 等语言相比,Python 的名称语义和属性/项 getter/setter 使引用参数更加混乱,在这些语言中它们已经令人困惑且容易出错。

【讨论】:

Byrefs 可以(在概念上)限制为不可变类型,因为那是它们有用的地方。这将减少复杂的副作用和不必要的语义(例如引用列表和字典)。不能说根据 Python 内部结构进行这样的检查是否可行/合理。 @BaselShishani:问题在于变量没有类型。值有类型。如果您通过引用传递3,则与Python 已经做的相比没有任何好处,因为您不能将3 变成4。如果您通过引用传递一个评估为3 的左值,那么您要么必须将所有引用参数限制为简单变量(并失去引用参数的大部分功能),要么您必须处理讨厌的 getter/setter 语义。 “在另一种语言中,变量是放置事物的地方,而不是值的名称,”还有其他语言同时具有属性和传递引用。例如,C#。 C# 不允许您通过引用传递属性。 C# 和 Python 的区别在于,在 C# 中,属性和属性是声明的,所以当你看到点语法时,你就知道它是否是属性;而 Python 更加动态,所有属性访问都是通过 .__getattr__().__setattr__() 等方法进行的,因此检查起来要困难得多。我想这就是你想说的。

以上是关于为啥python不允许通过引用传递变量的主要内容,如果未能解决你的问题,请参考以下文章

python中变量的引用、可变和不可变类型、局部变量和全局变量

为啥我会收到“仅应通过引用传递变量”错误?

python 引用

为啥在 PHP 中通过引用传递?

python中函数参数的引用方式

为啥这个 Ruby 方法通过引用传递它的参数