如何通过引用传递变量?

发布于 2025-01-15 18:49:35 字数 820 浏览 5 评论 0原文

我编写这个类是为了测试:

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.change(self.variable)
        print(self.variable)

    def change(self, var):
        var = 'Changed'

当我尝试创建实例时,输出是Original。所以Python中的参数似乎是按值传递的。这是正确的吗?如何修改代码以获得引用传递的效果,从而使输出Changed


有时人们会感到惊讶,像 x = 1 这样的代码(其中 x 是参数名称)不会影响调用者的参数,但是像 这样的代码x[0] = 1 确实如此。发生这种情况是因为项目分配切片分配变异现有对象的方法,而不是重新分配变量,尽管=< /代码> 语法。有关详细信息,请参阅为什么函数可以修改调用者感知到的某些参数,而不是其他参数?

另请参阅按引用传递与按值传递之间有什么区别?,了解重要的、与语言无关的术语讨论。

I wrote this class for testing:

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.change(self.variable)
        print(self.variable)

    def change(self, var):
        var = 'Changed'

When I tried creating an instance, the output was Original. So it seems like parameters in Python are passed by value. Is that correct? How can I modify the code to get the effect of pass-by-reference, so that the output is Changed?


Sometimes people are surprised that code like x = 1, where x is a parameter name, doesn't impact on the caller's argument, but code like x[0] = 1 does. This happens because item assignment and slice assignment are ways to mutate an existing object, rather than reassign a variable, despite the = syntax. See Why can a function modify some arguments as perceived by the caller, but not others? for details.

See also What's the difference between passing by reference vs. passing by value? for important, language-agnostic terminology discussion.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(30

颜漓半夏 2025-01-22 18:49:36

由于您的示例恰好是面向对象的,因此您可以进行以下更改以获得类似的结果:

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.change('variable')
        print(self.variable)

    def change(self, var):
        setattr(self, var, 'Changed')

# o.variable will equal 'Changed'
o = PassByReference()
assert o.variable == 'Changed'

Since your example happens to be object-oriented, you could make the following change to achieve a similar result:

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.change('variable')
        print(self.variable)

    def change(self, var):
        setattr(self, var, 'Changed')

# o.variable will equal 'Changed'
o = PassByReference()
assert o.variable == 'Changed'
哆啦不做梦 2025-01-22 18:49:36

您只能使用空类作为实例来存储引用对象,因为内部对象属性存储在实例字典中。请参阅示例。

class RefsObj(object):
    "A class which helps to create references to variables."
    pass

...

# an example of usage
def change_ref_var(ref_obj):
    ref_obj.val = 24

ref_obj = RefsObj()
ref_obj.val = 1
print(ref_obj.val) # or print ref_obj.val for python2
change_ref_var(ref_obj)
print(ref_obj.val)

You can merely use an empty class as an instance to store reference objects because internally object attributes are stored in an instance dictionary. See the example.

class RefsObj(object):
    "A class which helps to create references to variables."
    pass

...

# an example of usage
def change_ref_var(ref_obj):
    ref_obj.val = 24

ref_obj = RefsObj()
ref_obj.val = 1
print(ref_obj.val) # or print ref_obj.val for python2
change_ref_var(ref_obj)
print(ref_obj.val)
天暗了我发光 2025-01-22 18:49:36

由于字典是通过引用传递的,因此您可以使用 dict 变量来存储其中的任何引用值。

# returns the result of adding numbers `a` and `b`
def AddNumbers(a, b, ref): # using a dict for reference
    result = a + b
    ref['multi'] = a * b # reference the multi. ref['multi'] is number
    ref['msg'] = "The result: " + str(result) + " was nice!"
    return result

number1 = 5
number2 = 10
ref = {} # init a dict like that so it can save all the referenced values. this is because all dictionaries are passed by reference, while strings and numbers do not.

sum = AddNumbers(number1, number2, ref)
print("sum: ", sum)             # the returned value
print("multi: ", ref['multi'])  # a referenced value
print("msg: ", ref['msg'])      # a referenced value

Since dictionaries are passed by reference, you can use a dict variable to store any referenced values inside it.

# returns the result of adding numbers `a` and `b`
def AddNumbers(a, b, ref): # using a dict for reference
    result = a + b
    ref['multi'] = a * b # reference the multi. ref['multi'] is number
    ref['msg'] = "The result: " + str(result) + " was nice!"
    return result

number1 = 5
number2 = 10
ref = {} # init a dict like that so it can save all the referenced values. this is because all dictionaries are passed by reference, while strings and numbers do not.

sum = AddNumbers(number1, number2, ref)
print("sum: ", sum)             # the returned value
print("multi: ", ref['multi'])  # a referenced value
print("msg: ", ref['msg'])      # a referenced value
我做我的改变 2025-01-22 18:49:36

虽然按引用传递不太适合 Python 并且应该很少使用,但有一些解决方法实际上可以用来获取当前分配给局部变量的对象,甚至可以从被调用函数内部重新分配局部变量。

基本思想是拥有一个可以执行该访问并且可以作为对象传递到其他函数或存储在类中的函数。

一种方法是在包装函数中使用global(对于全局变量)或nonlocal(对于函数中的局部变量)。

def change(wrapper):
    wrapper(7)

x = 5

def setter(val):
    global x
    x = val

print(x)

同样的想法也适用于读取和删除变量。

对于阅读而言,甚至还有一种更短的方法,即使用 lambda: x,它返回一个可调用函数,调用时返回 x 的当前值。这有点像遥远过去的语言中使用的“按名字称呼”。

传递 3 个包装器来访问变量有点笨拙,因此可以将它们包装到具有代理属性的类中:

class ByRef:
    def __init__(self, r, w, d):
        self._read = r
        self._write = w
        self._delete = d
    def set(self, val):
        self._write(val)
    def get(self):
        return self._read()
    def remove(self):
        self._delete()
    wrapped = property(get, set, remove)

# Left as an exercise for the reader: define set, get, remove as local functions using global / nonlocal
r = ByRef(get, set, remove)
r.wrapped = 15

Python 的“反射”支持使得获得能够在给定范围内重新分配名称/变量的对象成为可能无需在该范围内显式定义函数:

class ByRef:
    def __init__(self, locs, name):
        self._locs = locs
        self._name = name
    def set(self, val):
        self._locs[self._name] = val
    def get(self):
        return self._locs[self._name]
    def remove(self):
        del self._locs[self._name]
    wrapped = property(get, set, remove)

def change(x):
    x.wrapped = 7

def test_me():
    x = 6
    print(x)
    change(ByRef(locals(), "x"))
    print(x)

这里 ByRef 类包装了字典访问。因此,对 wrapped 的属性访问被转换为传递的字典中的项目访问。通过传递内置 locals 的结果和局部变量的名称,最终会访问局部变量。截至 3.5 的 Python 文档建议更改字典可能不起作用,但它似乎对我有用。

While pass by reference is nothing that fits well into Python and should be rarely used, there are some workarounds that actually can work to get the object currently assigned to a local variable or even reassign a local variable from inside of a called function.

The basic idea is to have a function that can do that access and can be passed as object into other functions or stored in a class.

One way is to use global (for global variables) or nonlocal (for local variables in a function) in a wrapper function.

def change(wrapper):
    wrapper(7)

x = 5

def setter(val):
    global x
    x = val

print(x)

The same idea works for reading and deleting a variable.

For just reading, there is even a shorter way of just using lambda: x which returns a callable that when called returns the current value of x. This is somewhat like "call by name" used in languages in the distant past.

Passing 3 wrappers to access a variable is a bit unwieldy so those can be wrapped into a class that has a proxy attribute:

class ByRef:
    def __init__(self, r, w, d):
        self._read = r
        self._write = w
        self._delete = d
    def set(self, val):
        self._write(val)
    def get(self):
        return self._read()
    def remove(self):
        self._delete()
    wrapped = property(get, set, remove)

# Left as an exercise for the reader: define set, get, remove as local functions using global / nonlocal
r = ByRef(get, set, remove)
r.wrapped = 15

Pythons "reflection" support makes it possible to get a object that is capable of reassigning a name/variable in a given scope without defining functions explicitly in that scope:

class ByRef:
    def __init__(self, locs, name):
        self._locs = locs
        self._name = name
    def set(self, val):
        self._locs[self._name] = val
    def get(self):
        return self._locs[self._name]
    def remove(self):
        del self._locs[self._name]
    wrapped = property(get, set, remove)

def change(x):
    x.wrapped = 7

def test_me():
    x = 6
    print(x)
    change(ByRef(locals(), "x"))
    print(x)

Here the ByRef class wraps a dictionary access. So attribute access to wrapped is translated to a item access in the passed dictionary. By passing the result of the builtin locals and the name of a local variable, this ends up accessing a local variable. The Python documentation as of 3.5 advises that changing the dictionary might not work, but it seems to work for me.

想念有你 2025-01-22 18:49:36

Python 中的按引用传递与 C++/Java 中的按引用传递概念有很大不同。

  • Java 和 C#: 基本类型(包括字符串)按值传递(复制)。引用类型通过引用传递(地址复制),因此被调用函数中参数所做的所有更改对于调用者都是可见的。

  • C++:允许按引用传递或按值传递。如果参数是通过引用传递的,则可以修改它也可以不修改它,具体取决于该参数是否作为 const 传递。但是,无论是否为 const,参数都会维护对对象的引用,并且不能将引用分配为指向被调用函数内的不同对象。

  • Python:
    Python 是“按对象引用传递”,即人们常说的:“对象引用是按值传递的”。 (请阅读此处)。调用者和函数都引用同一个对象,但函数中的参数是一个新变量,它仅保存调用者中对象的副本。与 C++ 一样,参数可以在函数中修改,也可以不修改。这取决于传递的对象的类型。例如,不可变对象类型不能在被调用函数中修改,而可变对象可以更新或重新初始化。

    更新或重新分配/重新初始化可变变量之间的一个关键区别是更新的值会反映在被调用的函数中,而重新初始化的值则不会。新对象对可变变量的任何赋值的范围对于 python 中的函数来说都是本地的。 @blair-conrad 提供的示例非常有助于理解这一点。

Pass-by-reference in Python is quite different from the concept of pass by reference in C++/Java.

  • Java and C#: primitive types (including string) pass by value (copy). A reference type is passed by reference (address copy), so all changes made in the parameter in the called function are visible to the caller.

  • C++: Both pass-by-reference or pass-by-value are allowed. If a parameter is passed by reference, you can either modify it or not depending upon whether the parameter was passed as const or not. However, const or not, the parameter maintains the reference to the object and reference cannot be assigned to point to a different object within the called function.

  • Python:
    Python is “pass-by-object-reference”, of which it is often said: “Object references are passed by value.” (read here). Both the caller and the function refer to the same object, but the parameter in the function is a new variable which is just holding a copy of the object in the caller. Like C++, a parameter can be either modified or not in function. This depends upon the type of object passed. For example, an immutable object type cannot be modified in the called function whereas a mutable object can be either updated or re-initialized.

    A crucial difference between updating or reassigning/re-initializing the mutable variable is that updated value gets reflected back in the called function whereas the reinitialized value does not. The scope of any assignment of new object to a mutable variable is local to the function in the python. Examples provided by @blair-conrad are great to understand this.

掀纱窥君容 2025-01-22 18:49:36

我发现其他答案有点令人困惑,我不得不努力一段时间才能掌握这些概念。所以,我试图用我的语言给出答案。如果其他答案也让您感到困惑,它可能会对您有所帮助。所以,答案是这样的——

当你创建一个列表时——

my_list = []

你实际上是在创建一个列表类的对象:

my_list = list()

这里,my_list只是一个由构造函数创建的对象的内存地址(例如,140707924412080)的名称。 “列表”类。

的方法时,

def my_method1(local_list):
    local_list.append(1)

当您将此列表传递给定义为对同一内存地址 140707924412080 的另一个引用 将创建该方法。因此,当您使用追加方法对对象进行任何更改/变异时,它也会反映在 my_method1 之外。因为,外部列表 my_list 和 local_list 都引用相同的内存地址。

另一方面,当您将相同的列表传递给以下方法时,

def my_method2(local_list2):
    local_list2 = [1,2,3,4]

该过程的前半部分保持不变。即,创建一个新的引用/名称 local_list2,它指向相同的内存地址 140707924412080。但是当您创建一个新列表 [1,2,3,4] 时,会再次调用“list”类的构造函数并创建一个新的列表。对象被创建。这个新对象有一个完全不同的内存地址,例如140707924412112。当您将local_list2分配给[1,2,3,4]时,现在local_list2名称引用了一个新的内存地址,即140707924412112。因为在整个过程中您有没有对内存地址 140707924412080 处的对象进行任何更改,它仍然存在不受影响。

换句话说,这就是“其他语言有变量,Python 有名称”的精神。这意味着在其他语言中,变量引用内存中的固定地址。 重新分配变量

a = 1
a = 2

这意味着,在 C++ 中,如果通过存储值“1”的内存地址 ,现在将保存值“2”,因此,值“1”将完全丢失。而在Python中,由于一切都是对象,前面的'a'指的是存储类'int'的对象的内存地址,而类'int'又存储值'1'。但是,重新分配后,它引用了一个完全不同的内存地址,该地址存储新创建的类“int”对象,其值为“2”。

希望有帮助。

I found other answers a little bit confusing and I had to struggle a while to grasp the concepts. So, I am trying to put the answer in my language. It may help you if other answers are confusing to you too. So, the answer is like this-

When you create a list-

my_list = []

you are actually creating an object of the class list:

my_list = list()

Here, my_list is just a name given to the memory address (e.g., 140707924412080) of the object created by the constructor of the 'list' class.

When you pass this list to a method defined as

def my_method1(local_list):
    local_list.append(1)

another reference to the same memory address 140707924412080 is created. So, when you make any changes/mutate to the object by using append method, it is also reflected outside the my_method1. Because, both the outer list my_list and local_list are referencing the same memory address.

On the other hand, when you pass the same list to the following method,

def my_method2(local_list2):
    local_list2 = [1,2,3,4]

the first half of the process remains the same. i.e., a new reference/name local_list2 is created which points to the same memory address 140707924412080. But when you create a new list [1,2,3,4], the constructor of the 'list' class is called again and a new object is created. This new object has a completely different memory address, e.g., 140707924412112. When you assign local_list2 to [1,2,3,4], now the local_list2 name refers to a new memory address which is 140707924412112. Since in this entire process you have not made any changes to the object placed at memory address 140707924412080, it remains unaffected.

In other words, it is in the spirit that 'other languages have variables, Python have names'. That means in other languages, variables are referenced to a fixed address in memory. That means, in C++, if you reassign a variable by

a = 1
a = 2

the memory address where the value '1' was stored is now holding the value '2' And hence, the value '1' is completely lost. Whereas in Python, since everything is an object, earlier 'a' referred to the memory address that stores the object of class 'int' which in turn stores the value '1'. But, after reassignment, it refers to a completely different memory address that stores the newly created object of class 'int' holding the value '2'.

Hope it helps.

年华零落成诗 2025-01-22 18:49:36

我是 Python 新手,从昨天开始(尽管我已经编程 45 年了)。

我来到这里是因为我正在编写一个函数,我想要有两个所谓的输出参数。如果它只是一个外参数,我现在就不会因为检查引用/值在 Python 中的工作方式而烦恼。我只会使用函数的返回值来代替。但由于我需要两个这样的输出参数,我觉得我需要解决它。

在这篇文章中,我将展示我如何解决我的情况。也许来到这里的其他人会发现它很有价值,尽管它并不完全是主题问题的答案。经验丰富的Python程序员当然已经知道我使用的解决方案,但它对我来说是新的。

从这里的答案中我可以很快看出Python在这方面的工作方式有点像JavaScript,并且如果您想要参考功能,您需要使用解决方法。

但后来我在 Python 中发现了一些我以前在其他语言中没有见过的巧妙之处,即你可以从一个函数返回多个值,以一种简单的逗号分隔方式,如下所示:

def somefunction(p):
    a = p + 1
    b = p + 2
    c = -p
    return a, b, c

并且你可以在调用方同样处理,就像这样,

x, y, z = somefunction(w)

这对我来说已经足够好了,我很满意。没有必要使用某些解决方法。

在其他语言中,您当然也可以返回许多值,但通常是在对象的形式中,并且您需要相应地调整调用方。

Python 的实现方式既简单又好用。

如果您想进一步模仿通过引用,您可以执行以下操作:

def somefunction(a, b, c):
    a = a * 2
    b = b + a
    c = a * b * c
    return a, b, c

x = 3
y = 5
z = 10
print(F"Before : {x}, {y}, {z}")

x, y, z = somefunction(x, y, z)

print(F"After  : {x}, {y}, {z}")

这给出了这个结果

Before : 3, 5, 10
After  : 6, 11, 660

I am new to Python, started yesterday (though I have been programming for 45 years).

I came here because I was writing a function where I wanted to have two so-called out-parameters. If it would have been only one out-parameter, I wouldn't get hung up right now on checking how reference/value works in Python. I would just have used the return value of the function instead. But since I needed two such out-parameters I felt I needed to sort it out.

In this post I am going to show how I solved my situation. Perhaps others coming here can find it valuable, even though it is not exactly an answer to the topic question. Experienced Python programmers of course already know about the solution I used, but it was new to me.

From the answers here I could quickly see that Python works a bit like JavaScript in this regard, and that you need to use workarounds if you want the reference functionality.

But then I found something neat in Python that I don't think I have seen in other languages before, namely that you can return more than one value from a function, in a simple comma-separated way, like this:

def somefunction(p):
    a = p + 1
    b = p + 2
    c = -p
    return a, b, c

and that you can handle that on the calling side similarly, like this

x, y, z = somefunction(w)

That was good enough for me and I was satisfied. There isn't any need to use some workaround.

In other languages you can of course also return many values, but then usually in the from of an object, and you need to adjust the calling side accordingly.

The Python way of doing it was nice and simple.

If you want to mimic by reference even more, you could do as follows:

def somefunction(a, b, c):
    a = a * 2
    b = b + a
    c = a * b * c
    return a, b, c

x = 3
y = 5
z = 10
print(F"Before : {x}, {y}, {z}")

x, y, z = somefunction(x, y, z)

print(F"After  : {x}, {y}, {z}")

which gives this result

Before : 3, 5, 10
After  : 6, 11, 660
暗喜 2025-01-22 18:49:36

或者,您可以使用 ctypes ,它看起来像这样:

import ctypes

def f(a):
    a.value = 2398 ## Resign the value in a function

a = ctypes.c_int(0)
print("pre f", a)
f(a)
print("post f", a)

因为 a 是 ac int 而不是一个 Python 整数,显然是通过引用传递的。不过,您必须小心,因为可能会发生奇怪的事情,因此不建议这样做。

Alternatively, you could use ctypes which would look something like this:

import ctypes

def f(a):
    a.value = 2398 ## Resign the value in a function

a = ctypes.c_int(0)
print("pre f", a)
f(a)
print("post f", a)

As a is a c int and not a Python integer and apparently passed by reference. However, you have to be careful as strange things could happen, and it is therefore not advised.

叶落知秋 2025-01-22 18:49:36

使用数据类。此外,它还允许您应用类型限制(也称为“类型提示”)。

from dataclasses import dataclass

@dataclass
class Holder:
    obj: your_type # Need any type? Use "obj: object" then.

def foo(ref: Holder):
    ref.obj = do_something()

我同意人们的观点,在大多数情况下你最好考虑不要使用它。

然而,当我们谈论上下文时,这是值得的那样知道。

不过,您可以设计一个显式上下文类。在进行原型设计时,我更喜欢数据类,因为很容易来回序列化它们。

Use dataclasses. Also, it allows you to apply type restrictions (aka "type hints").

from dataclasses import dataclass

@dataclass
class Holder:
    obj: your_type # Need any type? Use "obj: object" then.

def foo(ref: Holder):
    ref.obj = do_something()

I agree with folks that in most cases you'd better consider not to use it.

And yet, when we're talking about contexts, it's worth to know that way.

You can design an explicit context class though. When prototyping, I prefer dataclasses, just because it's easy to serialize them back and forth.

寄与心 2025-01-22 18:49:36

关于这个问题已经有很多很好的答案(或者说意见),我已经读过它们,但我想提一下缺少的一个。来自 常见问题解答部分中的 Python 文档。我不知道发布此页面的日期,但这应该是我们真正的参考:

请记住,在 Python 中参数是通过赋值传递的。自从
赋值只是创建对对象的引用,没有别名
调用者和被调用者的参数名称之间,所以
引用调用
本身。

如果你有:

a = SOMETHING

def fn(arg):
    pass

并且你像 fn(a) 那样调用它,那么你所做的正是你在赋值中所做的。因此会发生这种情况:

arg = a

创建了对 SOMETHING 的附加引用。变量只是符号/名称/引用。他们不“持有”任何东西。

There are already many great answers (or let's say opinions) about this and I've read them, but I want to mention a missing one. The one from Python's documentation in the FAQ section. I don't know the date of publishing this page, but this should be our true reference:

Remember that arguments are passed by assignment in Python. Since
assignment just creates references to objects, there’s no alias
between an argument name in the caller and callee, and so no
call-by-reference
per se.

If you have:

a = SOMETHING

def fn(arg):
    pass

and you call it like fn(a), you're doing exactly what you do in assignment. So this happens:

arg = a

An additional reference to SOMETHING is created. Variables are just symbols/names/references. They don't "hold" anything.

旧人哭 2025-01-22 18:49:35

参数是 通过分配传递。这背后的基本原理是双重的:

  1. 传入的参数实际上是对对象的引用(但引用是按值传递的)
  2. 某些数据类型是可变的,但其他数据类型则不然

  • 如果您将一个可变对象传递给一个方法,该方法将获得对该同一对象的引用,并且您可以根据自己的喜好对其进行变异,但是如果您在该方法中重新绑定该引用,则外部作用域将对此一无所知,之后完成后,外部引用仍将指向原始对象。

  • 如果您将一个不可变对象传递给一个方法,您仍然无法重新绑定外部引用,甚至无法改变该对象。

为了更清楚地说明这一点,让我们举一些例子。

列表 - 可变类型

让我们尝试修改传递给方法的列表:

def try_to_change_list_contents(the_list):
    print('got', the_list)
    the_list.append('four')
    print('changed to', the_list)

outer_list = ['one', 'two', 'three']

print('before, outer_list =', outer_list)
try_to_change_list_contents(outer_list)
print('after, outer_list =', outer_list)

输出:

before, outer_list = ['one', 'two', 'three']
got ['one', 'two', 'three']
changed to ['one', 'two', 'three', 'four']
after, outer_list = ['one', 'two', 'three', 'four']

由于传入的参数是对 outer_list 的引用,而不是它的副本,我们可以使用变异列表方法来更改它,并将更改反映在外部范围中。

现在让我们看看当我们尝试更改作为参数传入的引用时会发生什么:

def try_to_change_list_reference(the_list):
    print('got', the_list)
    the_list = ['and', 'we', 'can', 'not', 'lie']
    print('set to', the_list)

outer_list = ['we', 'like', 'proper', 'English']

print('before, outer_list =', outer_list)
try_to_change_list_reference(outer_list)
print('after, outer_list =', outer_list)

输出:

before, outer_list = ['we', 'like', 'proper', 'English']
got ['we', 'like', 'proper', 'English']
set to ['and', 'we', 'can', 'not', 'lie']
after, outer_list = ['we', 'like', 'proper', 'English']

由于 the_list 参数是按值传递的,因此将一个新列表分配给它对方法外部的代码没有任何影响。 the_listouter_list 引用的副本,我们让 the_list 指向一个新列表,但无法更改 outer_list 引用的位置code>outer_list 指出。

String - 不可变类型

它是不可变的,因此我们无法更改字符串的内容

现在,让我们尝试更改引用

def try_to_change_string_reference(the_string):
    print('got', the_string)
    the_string = 'In a kingdom by the sea'
    print('set to', the_string)

outer_string = 'It was many and many a year ago'

print('before, outer_string =', outer_string)
try_to_change_string_reference(outer_string)
print('after, outer_string =', outer_string)

输出:

before, outer_string = It was many and many a year ago
got It was many and many a year ago
set to In a kingdom by the sea
after, outer_string = It was many and many a year ago

同样,由于 < code>the_string 参数是按值传递的,为其分配新字符串不会对方法外部的代码可见产生任何影响。 the_stringouter_string 引用的副本,我们让 the_string 指向一个新字符串,但是没有办法改变 outer_string 引用的位置。 code>outer_string 指出。

我希望这能让事情澄清一点。

编辑:有人指出,这并没有回答@David最初提出的问题:“我可以做些什么来通过实际引用传递变量吗?”。让我们努力吧。

我们如何解决这个问题?

正如@Andrea 的回答所示,您可以返回新值。这不会改变传入内容的方式,但可以让您获取想要返回的信息:

def return_a_whole_new_string(the_string):
    new_string = something_to_do_with_the_old_string(the_string)
    return new_string

# then you could call it like
my_string = return_a_whole_new_string(my_string)

如果您确实想避免使用返回值,您可以创建一个类来保存您的值并将其传递给函数或者使用现有的类,例如列表:

def use_a_wrapper_to_simulate_pass_by_reference(stuff_to_change):
    new_string = something_to_do_with_the_old_string(stuff_to_change[0])
    stuff_to_change[0] = new_string

# then you could call it like
wrapper = [my_string]
use_a_wrapper_to_simulate_pass_by_reference(wrapper)

do_something_with(wrapper[0])

虽然这看起来有点麻烦。

Arguments are passed by assignment. The rationale behind this is twofold:

  1. the parameter passed in is actually a reference to an object (but the reference is passed by value)
  2. some data types are mutable, but others aren't

So:

  • If you pass a mutable object into a method, the method gets a reference to that same object and you can mutate it to your heart's delight, but if you rebind the reference in the method, the outer scope will know nothing about it, and after you're done, the outer reference will still point at the original object.

  • If you pass an immutable object to a method, you still can't rebind the outer reference, and you can't even mutate the object.

To make it even more clear, let's have some examples.

List - a mutable type

Let's try to modify the list that was passed to a method:

def try_to_change_list_contents(the_list):
    print('got', the_list)
    the_list.append('four')
    print('changed to', the_list)

outer_list = ['one', 'two', 'three']

print('before, outer_list =', outer_list)
try_to_change_list_contents(outer_list)
print('after, outer_list =', outer_list)

Output:

before, outer_list = ['one', 'two', 'three']
got ['one', 'two', 'three']
changed to ['one', 'two', 'three', 'four']
after, outer_list = ['one', 'two', 'three', 'four']

Since the parameter passed in is a reference to outer_list, not a copy of it, we can use the mutating list methods to change it and have the changes reflected in the outer scope.

Now let's see what happens when we try to change the reference that was passed in as a parameter:

def try_to_change_list_reference(the_list):
    print('got', the_list)
    the_list = ['and', 'we', 'can', 'not', 'lie']
    print('set to', the_list)

outer_list = ['we', 'like', 'proper', 'English']

print('before, outer_list =', outer_list)
try_to_change_list_reference(outer_list)
print('after, outer_list =', outer_list)

Output:

before, outer_list = ['we', 'like', 'proper', 'English']
got ['we', 'like', 'proper', 'English']
set to ['and', 'we', 'can', 'not', 'lie']
after, outer_list = ['we', 'like', 'proper', 'English']

Since the the_list parameter was passed by value, assigning a new list to it had no effect that the code outside the method could see. The the_list was a copy of the outer_list reference, and we had the_list point to a new list, but there was no way to change where outer_list pointed.

String - an immutable type

It's immutable, so there's nothing we can do to change the contents of the string

Now, let's try to change the reference

def try_to_change_string_reference(the_string):
    print('got', the_string)
    the_string = 'In a kingdom by the sea'
    print('set to', the_string)

outer_string = 'It was many and many a year ago'

print('before, outer_string =', outer_string)
try_to_change_string_reference(outer_string)
print('after, outer_string =', outer_string)

Output:

before, outer_string = It was many and many a year ago
got It was many and many a year ago
set to In a kingdom by the sea
after, outer_string = It was many and many a year ago

Again, since the the_string parameter was passed by value, assigning a new string to it had no effect that the code outside the method could see. The the_string was a copy of the outer_string reference, and we had the_string point to a new string, but there was no way to change where outer_string pointed.

I hope this clears things up a little.

EDIT: It's been noted that this doesn't answer the question that @David originally asked, "Is there something I can do to pass the variable by actual reference?". Let's work on that.

How do we get around this?

As @Andrea's answer shows, you could return the new value. This doesn't change the way things are passed in, but does let you get the information you want back out:

def return_a_whole_new_string(the_string):
    new_string = something_to_do_with_the_old_string(the_string)
    return new_string

# then you could call it like
my_string = return_a_whole_new_string(my_string)

If you really wanted to avoid using a return value, you could create a class to hold your value and pass it into the function or use an existing class, like a list:

def use_a_wrapper_to_simulate_pass_by_reference(stuff_to_change):
    new_string = something_to_do_with_the_old_string(stuff_to_change[0])
    stuff_to_change[0] = new_string

# then you could call it like
wrapper = [my_string]
use_a_wrapper_to_simulate_pass_by_reference(wrapper)

do_something_with(wrapper[0])

Although this seems a little cumbersome.

执手闯天涯 2025-01-22 18:49:35

问题源于对 Python 中变量的误解。如果您习惯了大多数传统语言,那么您会对按以下顺序发生的情况有一个心理模型:

a = 1
a = 2

您认为 a 是存储值 1 的内存位置,然后更新以存储值2。 Python 中的工作方式并非如此。相反,a 首先作为对值为 1 的对象的引用,然后重新分配为对值为 2 的对象的引用。即使 a 不再引用第一个对象,这两个对象可能会继续共存;事实上,它们可以被程序内任意数量的其他引用共享。

当您使用参数调用函数时,会创建一个新的引用来引用传入的对象。这与函数调用中使用的引用是分开的,因此无法更新该引用并使其引用新对象。在您的示例中:

def __init__(self):
    self.variable = 'Original'
    self.Change(self.variable)

def Change(self, var):
    var = 'Changed'

self.variable 是对字符串对象'Original' 的引用。当您调用 Change 时,您会创建对该对象的第二个引用 var。在函数内部,您将引用 var 重新分配给不同的字符串对象 'Changed',但引用 self.variable 是单独的并且不会更改。

解决这个问题的唯一方法是传递一个可变对象。由于两个引用都引用同一个对象,因此对该对象的任何更改都会反映在两个位置。

def __init__(self):         
    self.variable = ['Original']
    self.Change(self.variable)

def Change(self, var):
    var[0] = 'Changed'

The problem comes from a misunderstanding of what variables are in Python. If you're used to most traditional languages, you have a mental model of what happens in the following sequence:

a = 1
a = 2

You believe that a is a memory location that stores the value 1, then is updated to store the value 2. That's not how things work in Python. Rather, a starts as a reference to an object with the value 1, then gets reassigned as a reference to an object with the value 2. Those two objects may continue to coexist even though a doesn't refer to the first one anymore; in fact they may be shared by any number of other references within the program.

When you call a function with a parameter, a new reference is created that refers to the object passed in. This is separate from the reference that was used in the function call, so there's no way to update that reference and make it refer to a new object. In your example:

def __init__(self):
    self.variable = 'Original'
    self.Change(self.variable)

def Change(self, var):
    var = 'Changed'

self.variable is a reference to the string object 'Original'. When you call Change you create a second reference var to the object. Inside the function you reassign the reference var to a different string object 'Changed', but the reference self.variable is separate and does not change.

The only way around this is to pass a mutable object. Because both references refer to the same object, any changes to the object are reflected in both places.

def __init__(self):         
    self.variable = ['Original']
    self.Change(self.variable)

def Change(self, var):
    var[0] = 'Changed'
恏ㄋ傷疤忘ㄋ疼 2025-01-22 18:49:35

我发现其他答案相当长且复杂,因此我创建了这个简单的图表来解释 Python 处理变量和参数的方式。
在此处输入图像描述

I found the other answers rather long and complicated, so I created this simple diagram to explain the way Python treats variables and parameters.
enter image description here

无敌元气妹 2025-01-22 18:49:35

它既不是按值传递,也不是按引用传递,而是按对象调用。请参阅 Fredrik Lundh 的文章:

按对象调用

这是一个重要的引用:

“...变量[名称]不是对象;它们不能由其他变量表示或由对象引用。”

在您的示例中,当调用 Change 方法时 -a 命名空间 是为其创建的;并且 var 成为该命名空间内字符串对象 'Original' 的名称。该对象在两个命名空间中都有一个名称。接下来,var = 'Changed'var 绑定到一个新的字符串对象,因此该方法的命名空间会忘记'Original'。最后,该命名空间被遗忘,字符串'Changed'也随之被遗忘。

It is neither pass-by-value or pass-by-reference - it is call-by-object. See this, by Fredrik Lundh:

Call By Object

Here is a significant quote:

"...variables [names] are not objects; they cannot be denoted by other variables or referred to by objects."

In your example, when the Change method is called--a namespace is created for it; and var becomes a name, within that namespace, for the string object 'Original'. That object then has a name in two namespaces. Next, var = 'Changed' binds var to a new string object, and thus the method's namespace forgets about 'Original'. Finally, that namespace is forgotten, and the string 'Changed' along with it.

愚人国度 2025-01-22 18:49:35

想想通过赋值传递的东西,而不是通过引用/按值传递。这样,只要您了解正常任务期间发生的情况,就总是清楚发生了什么。

因此,当将列表传递给函数/方法时,列表将被分配给参数名称。追加到列表将导致列表被修改。在函数内部重新分配列表不会更改原始列表,因为:

a = [1, 2, 3]
b = a
b.append(4)
b = ['a', 'b']
print a, b      # prints [1, 2, 3, 4] ['a', 'b']

由于不可变类型无法修改,因此它们看起来就像按值传递 - 将 int 传递到函数中意味着将 int 分配给函数的参数。您只能重新分配它,但不会更改原始变量值。

Think of stuff being passed by assignment instead of by reference/by value. That way, it is always clear, what is happening as long as you understand what happens during the normal assignment.

So, when passing a list to a function/method, the list is assigned to the parameter name. Appending to the list will result in the list being modified. Reassigning the list inside the function will not change the original list, since:

a = [1, 2, 3]
b = a
b.append(4)
b = ['a', 'b']
print a, b      # prints [1, 2, 3, 4] ['a', 'b']

Since immutable types cannot be modified, they seem like being passed by value - passing an int into a function means assigning the int to the function's parameter. You can only ever reassign that, but it won't change the original variables value.

沩ん囻菔务 2025-01-22 18:49:35

Python 中没有变量

理解参数传递的关键是停止思考“变量”。 Python 中有名称和对象,它们在一起
看起来像变量,但始终区分三者很有用。

  1. Python 有名称和对象。
  2. 赋值将名称绑定到对象。
  3. 将参数传递给函数还会将名称(函数的参数名称)绑定到对象。

这就是全部内容了。可变性与这个问题无关。

示例:

a = 1

这将名称 a 绑定到包含值 1 的整数类型的对象。

b = x

这将名称 b 绑定到与名称 x< 相同的对象/code> 目前已绑定到。
之后,名称b 就不再与名称x 相关了。

请参阅 3.14.2

如何阅读问题中的示例

在问题中显示的代码中,语句 self.Change(self.variable) 绑定名称 var (在函数范围内Change) 到保存值 'Original' 和赋值 var = 'Changed' 的对象(在函数 的主体中) Change) 再次分配相同的名称:给其他对象(它碰巧也有一根绳子,但也可能完全是别的东西)。

如何通过引用传递

因此,如果您要更改的内容是可变对象,那么没有问题,因为所有内容都是通过引用有效传递的。

如果它是一个 不可变 对象(例如bool、number、string),解决方法是将其包装在可变对象中。
快速而肮脏的解决方案是一个单元素列表(而不是 self.variable,传递 [self.variable] 并在函数中修改 var[0]).
更多的 pythonic 方法是引入一个简单的单属性类。该函数接收类的实例并操作属性。

There are no variables in Python

The key to understanding parameter passing is to stop thinking about "variables". There are names and objects in Python and together they
appear like variables, but it is useful to always distinguish the three.

  1. Python has names and objects.
  2. Assignment binds a name to an object.
  3. Passing an argument into a function also binds a name (the parameter name of the function) to an object.

That is all there is to it. Mutability is irrelevant to this question.

Example:

a = 1

This binds the name a to an object of type integer that holds the value 1.

b = x

This binds the name b to the same object that the name x is currently bound to.
Afterward, the name b has nothing to do with the name x anymore.

See sections 3.1 and 4.2 in the Python 3 language reference.

How to read the example in the question

In the code shown in the question, the statement self.Change(self.variable) binds the name var (in the scope of function Change) to the object that holds the value 'Original' and the assignment var = 'Changed' (in the body of function Change) assigns that same name again: to some other object (that happens to hold a string as well but could have been something else entirely).

How to pass by reference

So if the thing you want to change is a mutable object, there is no problem, as everything is effectively passed by reference.

If it is an immutable object (e.g. a bool, number, string), the way to go is to wrap it in a mutable object.
The quick-and-dirty solution for this is a one-element list (instead of self.variable, pass [self.variable] and in the function modify var[0]).
The more pythonic approach would be to introduce a trivial, one-attribute class. The function receives an instance of the class and manipulates the attribute.

江南烟雨〆相思醉 2025-01-22 18:49:35

Effbot(又名 Fredrik Lundh)将 Python 的变量传递风格描述为“按对象调用”: https://web.archive.org/web/20201111195827/http://effbot.org/zone/call-by-object.htm

对象在堆上分配,并指向它们可以在任何地方传递。

  • 当您进行诸如x = 1000这样的赋值时,会创建一个字典条目,将当前命名空间中的字符串“x”映射到指向包含 1000 的整数对象的指针。

  • 当您使用 x = 2000 更新“x”时,会创建一个新的整数对象,并且字典会更新为指向新对象。旧的一千个对象没有改变(并且可能还活着,也可能不活着,具体取决于是否有其他东西引用该对象)。

  • 当您进行新的赋值(例如y = x)时,会创建一个新的字典条目“y”,该条目指向与“x”条目相同的对象。

  • 像字符串和整数这样的对象是不可变的。这仅意味着在创建对象后没有任何方法可以更改该对象。例如,整数对象一千一旦创建,就永远不会改变。数学是通过创建新的整数对象来完成的。

  • 像列表这样的对象是可变的。这意味着对象的内容可以通过任何指向该对象的内容来更改。例如,x = []; y = x; x.append(10); print y 将打印 [10]。创建了空列表。 “x”和“y”都指向同一个列表。 append 方法改变(更新)列表对象(就像向数据库添加一条记录),结果对“x”和“y”都可见(就像数据库更新对到该数据库的每个连接)。

希望能为您澄清问题。

Effbot (aka Fredrik Lundh) has described Python's variable passing style as call-by-object: https://web.archive.org/web/20201111195827/http://effbot.org/zone/call-by-object.htm

Objects are allocated on the heap and pointers to them can be passed around anywhere.

  • When you make an assignment such as x = 1000, a dictionary entry is created that maps the string "x" in the current namespace to a pointer to the integer object containing one thousand.

  • When you update "x" with x = 2000, a new integer object is created and the dictionary is updated to point at the new object. The old one thousand object is unchanged (and may or may not be alive depending on whether anything else refers to the object).

  • When you do a new assignment such as y = x, a new dictionary entry "y" is created that points to the same object as the entry for "x".

  • Objects like strings and integers are immutable. This simply means that there are no methods that can change the object after it has been created. For example, once the integer object one-thousand is created, it will never change. Math is done by creating new integer objects.

  • Objects like lists are mutable. This means that the contents of the object can be changed by anything pointing to the object. For example, x = []; y = x; x.append(10); print y will print [10]. The empty list was created. Both "x" and "y" point to the same list. The append method mutates (updates) the list object (like adding a record to a database) and the result is visible to both "x" and "y" (just as a database update would be visible to every connection to that database).

Hope that clarifies the issue for you.

谁与争疯 2025-01-22 18:49:35

从技术上讲,Python 始终使用传递引用值。我将重复我的其他答案来支持我的声明。

Python 始终使用按引用传递值。没有任何例外。任何变量赋值都意味着复制参考值。也不例外。任何变量都是与引用值绑定的名称。总是。

您可以将引用值视为目标对象的地址。该地址在使用时会自动解除引用。这样,使用参考值时,看起来就像直接使用目标对象一样。但两者之间总有一个参考,再多一步就能跳转到目标。

下面是证明 Python 使用引用传递的示例:

传递参数的说明示例

如果参数是按值传递的,外部 lst 无法修改。绿色是目标对象(黑色是里面存储的值,红色是对象类型),黄色是里面有参考值的内存——如箭头所示。蓝色实线箭头是传递给函数的参考值(通过蓝色虚线箭头路径)。丑陋的深黄色是内部字典。 (它实际上也可以绘制为绿色椭圆。颜色和形状仅表明它是内部的。)

您可以使用 id()内置函数,用于了解引用值是什么(即目标对象的地址)。

在编译语言中,变量是能够捕获类型值的内存空间。在 Python 中,变量是绑定到引用变量的名称(在内部捕获为字符串),该引用变量保存目标对象的引用值。变量的名称是内部字典中的键,该字典项的值部分存储对目标的引用值。

参考值隐藏在 Python 中。没有任何明确的用户类型用于存储参考值。但是,您可以使用列表元素(或任何其他合适的容器类型中的元素)作为引用变量,因为所有容器也将元素存储为对目标对象的引用。换句话说,元素实际上并不包含在容器内——只有对元素的引用包含在容器内。

Technically, Python always uses pass by reference values. I am going to repeat my other answer to support my statement.

Python always uses pass-by-reference values. There isn't any exception. Any variable assignment means copying the reference value. No exception. Any variable is the name bound to the reference value. Always.

You can think about a reference value as the address of the target object. The address is automatically dereferenced when used. This way, working with the reference value, it seems you work directly with the target object. But there always is a reference in between, one step more to jump to the target.

Here is the example that proves that Python uses passing by reference:

Illustrated example of passing the argument

If the argument was passed by value, the outer lst could not be modified. The green are the target objects (the black is the value stored inside, the red is the object type), the yellow is the memory with the reference value inside -- drawn as the arrow. The blue solid arrow is the reference value that was passed to the function (via the dashed blue arrow path). The ugly dark yellow is the internal dictionary. (It actually could be drawn also as a green ellipse. The colour and the shape only says it is internal.)

You can use the id() built-in function to learn what the reference value is (that is, the address of the target object).

In compiled languages, a variable is a memory space that is able to capture the value of the type. In Python, a variable is a name (captured internally as a string) bound to the reference variable that holds the reference value to the target object. The name of the variable is the key in the internal dictionary, the value part of that dictionary item stores the reference value to the target.

Reference values are hidden in Python. There isn't any explicit user type for storing the reference value. However, you can use a list element (or element in any other suitable container type) as the reference variable, because all containers do store the elements also as references to the target objects. In other words, elements are actually not contained inside the container -- only the references to elements are.

辞慾 2025-01-22 18:49:35

我通常使用的一个简单技巧是将其包装在一个列表中:(

def Change(self, var):
    var[0] = 'Changed'

variable = ['Original']
self.Change(variable)      
print variable[0]

是的,我知道这可能很不方便,但有时这样做很简单。)

A simple trick I normally use is to just wrap it in a list:

def Change(self, var):
    var[0] = 'Changed'

variable = ['Original']
self.Change(variable)      
print variable[0]

(Yeah I know this can be inconvenient, but sometimes it is simple enough to do this.)

爱要勇敢去追 2025-01-22 18:49:35

您在这里得到了一些非常好的答案。

x = [ 2, 4, 4, 5, 5 ]
print x  # 2, 4, 4, 5, 5

def go( li ) :
  li = [ 5, 6, 7, 8 ]  # re-assigning what li POINTS TO, does not
  # change the value of the ORIGINAL variable x

go( x ) 
print x  # 2, 4, 4, 5, 5  [ STILL! ]


raw_input( 'press any key to continue' )

You got some really good answers here.

x = [ 2, 4, 4, 5, 5 ]
print x  # 2, 4, 4, 5, 5

def go( li ) :
  li = [ 5, 6, 7, 8 ]  # re-assigning what li POINTS TO, does not
  # change the value of the ORIGINAL variable x

go( x ) 
print x  # 2, 4, 4, 5, 5  [ STILL! ]


raw_input( 'press any key to continue' )
吃→可爱长大的 2025-01-22 18:49:35

在这种情况下,方法 Change 中标题为 var 的变量被分配了对 self.variable 的引用,并且您立即将一个字符串分配给 var。它不再指向 self.variable。以下代码片段显示了如果修改 varself.variable 指向的数据结构(在本例中是一个列表)会发生什么:

>>> class PassByReference:
...     def __init__(self):
...         self.variable = ['Original']
...         self.change(self.variable)
...         print self.variable
...         
...     def change(self, var):
...         var.append('Changed')
... 
>>> q = PassByReference()
['Original', 'Changed']
>>> 

我确信其他人可以进一步澄清这一点。

In this case the variable titled var in the method Change is assigned a reference to self.variable, and you immediately assign a string to var. It's no longer pointing to self.variable. The following code snippet shows what would happen if you modify the data structure pointed to by var and self.variable, in this case a list:

>>> class PassByReference:
...     def __init__(self):
...         self.variable = ['Original']
...         self.change(self.variable)
...         print self.variable
...         
...     def change(self, var):
...         var.append('Changed')
... 
>>> q = PassByReference()
['Original', 'Changed']
>>> 

I'm sure someone else could clarify this further.

触ぅ动初心 2025-01-22 18:49:35

Python 的传递赋值方案与 C++ 的引用参数选项不太一样,但在实践中它与 C 语言(和其他语言)的参数传递模型非常相似:

  • 有效地传递不可变参数“ 按价值。”整数和字符串等对象是通过对象引用传递的,而不是通过复制传递的,但由于无论如何都无法就地更改不可变对象,因此效果很像制作副本。
  • 可变参数实际上是“通过指针”传递的。列表等对象
    字典也是通过对象引用传递的,这和C的方式类似
    将数组作为指针传递——可变对象可以在函数中就地更改,
    很像 C 数组。

Python’s pass-by-assignment scheme isn’t quite the same as C++’s reference parameters option, but it turns out to be very similar to the argument-passing model of the C language (and others) in practice:

  • Immutable arguments are effectively passed “by value.” Objects such as integers and strings are passed by object reference instead of by copying, but because you can’t change immutable objects in place anyhow, the effect is much like making a copy.
  • Mutable arguments are effectively passed “by pointer.” Objects such as lists
    and dictionaries are also passed by object reference, which is similar to the way C
    passes arrays as pointers—mutable objects can be changed in place in the function,
    much like C arrays.
眼眸里的那抹悲凉 2025-01-22 18:49:35

这里的答案有很多见解,但我认为这里没有明确提到额外的一点。引用Python文档Python 中局部变量和全局变量的规则是什么?

在Python中,仅在函数内部引用的变量是隐式全局的。如果在函数体内的任何位置为变量分配了新值,则假定该变量是局部变量。如果在函数内为变量分配了新值,则该变量是隐式局部变量,您需要将其显式声明为“全局”。
虽然一开始有点令人惊讶,但稍加思考就解释了这一点。一方面,要求全局分配变量可以防止意外的副作用。另一方面,如果所有全局引用都需要 global,那么您将始终使用 global。您必须将对内置函数或导入模块的组件的每个引用声明为全局。这种混乱会破坏全局声明在识别副作用方面的用处。

即使将可变对象传递给函数,这仍然适用。对我来说,它清楚地解释了函数中分配给对象和操作对象之间的行为差​​异的原因。

def test(l):
    print "Received", l, id(l)
    l = [0, 0, 0]
    print "Changed to", l, id(l)  # New local object created, breaking link to global l

l = [1, 2, 3]
print "Original", l, id(l)
test(l)
print "After", l, id(l)

给出:

Original [1, 2, 3] 4454645632
Received [1, 2, 3] 4454645632
Changed to [0, 0, 0] 4474591928
After [1, 2, 3] 4454645632

对未声明为全局的全局变量的赋值因此会创建一个新的局部对象并断开与原始对象的链接。

There are a lot of insights in answers here, but I think an additional point is not clearly mentioned here explicitly. Quoting from Python documentation What are the rules for local and global variables in Python?

In Python, variables that are only referenced inside a function are implicitly global. If a variable is assigned a new value anywhere within the function’s body, it’s assumed to be a local. If a variable is ever assigned a new value inside the function, the variable is implicitly local, and you need to explicitly declare it as ‘global’.
Though a bit surprising at first, a moment’s consideration explains this. On one hand, requiring global for assigned variables provides a bar against unintended side-effects. On the other hand, if global was required for all global references, you’d be using global all the time. You’d have to declare as global every reference to a built-in function or to a component of an imported module. This clutter would defeat the usefulness of the global declaration for identifying side-effects.

Even when passing a mutable object to a function this still applies. And to me it clearly explains the reason for the difference in behavior between assigning to the object and operating on the object in the function.

def test(l):
    print "Received", l, id(l)
    l = [0, 0, 0]
    print "Changed to", l, id(l)  # New local object created, breaking link to global l

l = [1, 2, 3]
print "Original", l, id(l)
test(l)
print "After", l, id(l)

gives:

Original [1, 2, 3] 4454645632
Received [1, 2, 3] 4454645632
Changed to [0, 0, 0] 4474591928
After [1, 2, 3] 4454645632

The assignment to an global variable that is not declared global therefore creates a new local object and breaks the link to the original object.

行雁书 2025-01-22 18:49:35

正如您所说,您需要有一个可变对象,但我建议您检查全局变量,因为它们可以帮助您甚至解决此类问题!

http://docs.python.org/3/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python

示例:

>>> def x(y):
...     global z
...     z = y
...

>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'z' is not defined

>>> x(2)
>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
2

As you can state you need to have a mutable object, but let me suggest you to check over the global variables as they can help you or even solve this kind of issue!

http://docs.python.org/3/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python

example:

>>> def x(y):
...     global z
...     z = y
...

>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'z' is not defined

>>> x(2)
>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
2
无可置疑 2025-01-22 18:49:35

这是对 Python 中使用的通过对象传递概念的简单(我希望)解释。
每当您将对象传递给函数时,都会传递对象本身(Python 中的对象实际上是您在其他编程语言中所说的值),而不是对此对象的引用。换句话说,当您调用时:

def change_me(list):
   list = [1, 2, 3]

my_list = [0, 1]
change_me(my_list)

正在传递实际对象 - [0, 1](在其他编程语言中称为值)。所以实际上函数 change_me 会尝试做类似的事情:

[0, 1] = [1, 2, 3]

这显然不会改变传递给函数的对象。如果函数看起来像这样:

def change_me(list):
   list.append(2)

那么调用将导致:

[0, 1].append(2)

这显然会改变对象。 这个答案很好地解释了这一点。

Here is the simple (I hope) explanation of the concept pass by object used in Python.
Whenever you pass an object to the function, the object itself is passed (object in Python is actually what you'd call a value in other programming languages) not the reference to this object. In other words, when you call:

def change_me(list):
   list = [1, 2, 3]

my_list = [0, 1]
change_me(my_list)

The actual object - [0, 1] (which would be called a value in other programming languages) is being passed. So in fact the function change_me will try to do something like:

[0, 1] = [1, 2, 3]

which obviously will not change the object passed to the function. If the function looked like this:

def change_me(list):
   list.append(2)

Then the call would result in:

[0, 1].append(2)

which obviously will change the object. This answer explains it well.

早茶月光 2025-01-22 18:49:35

除了关于这些东西如何在 Python 中工作的所有精彩解释之外,我没有看到针对该问题的简单建议。正如您似乎确实创建了对象和实例一样,处理实例变量并更改它们的 Pythonic 方法如下:

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.Change()
        print self.variable

    def Change(self):
        self.variable = 'Changed'

在实例方法中,您通常引用 self 来访问实例属性。在__init__中设置实例属性并在实例方法中读取或更改它们是正常的。这也是您将 self 作为第一个参数传递给 def Change 的原因。

另一个解决方案是创建一个静态方法,如下所示:

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.variable = PassByReference.Change(self.variable)
        print self.variable

    @staticmethod
    def Change(var):
        var = 'Changed'
        return var

Aside from all the great explanations on how this stuff works in Python, I don't see a simple suggestion for the problem. As you seem to do create objects and instances, the Pythonic way of handling instance variables and changing them is the following:

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.Change()
        print self.variable

    def Change(self):
        self.variable = 'Changed'

In instance methods, you normally refer to self to access instance attributes. It is normal to set instance attributes in __init__ and read or change them in instance methods. That is also why you pass self as the first argument to def Change.

Another solution would be to create a static method like this:

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.variable = PassByReference.Change(self.variable)
        print self.variable

    @staticmethod
    def Change(var):
        var = 'Changed'
        return var
秉烛思 2025-01-22 18:49:35

我使用以下方法将一些 Fortran 代码快速转换为 Python。确实,它不是像最初提出的问题那样通过引用传递,但在某些情况下这是一个简单的解决方法。

a = 0
b = 0
c = 0

def myfunc(a, b, c):
    a = 1
    b = 2
    c = 3
    return a, b, c

a, b, c = myfunc(a, b, c)
print a, b, c

I used the following method to quickly convert some Fortran code to Python. True, it's not pass by reference as the original question was posed, but it is a simple workaround in some cases.

a = 0
b = 0
c = 0

def myfunc(a, b, c):
    a = 1
    b = 2
    c = 3
    return a, b, c

a, b, c = myfunc(a, b, c)
print a, b, c
断肠人 2025-01-22 18:49:35

要模拟通过引用传递对象,请将其包装在单项列表中:

class PassByReference:
    def __init__(self, name):
        self.name = name

def changeRef(ref):
    ref[0] = PassByReference('Michael')

obj = PassByReference('Peter')
print(obj.name)

p = [obj]
changeRef(p)

print(p[0].name)

分配给列表的元素会改变列表,而不是重新分配名称。由于列表本身具有引用语义,因此更改会反映在调用者中。

To simulate passing an object by reference, wrap it in a one-item list:

class PassByReference:
    def __init__(self, name):
        self.name = name

def changeRef(ref):
    ref[0] = PassByReference('Michael')

obj = PassByReference('Peter')
print(obj.name)

p = [obj]
changeRef(p)

print(p[0].name)

Assigning to an element of the list mutates the list rather than reassigning a name. Since the list itself has reference semantics, the change is reflected in the caller.

赴月观长安 2025-01-22 18:49:35

因为似乎没有地方提到模拟引用的方法,例如 C++ 中已知的方法是使用“更新”函数并传递它而不是实际变量(或者更确切地说,“名称”):

def need_to_modify(update):
    update(42) # set new value 42
    # other code

def call_it():
    value = 21
    def update_value(new_value):
        nonlocal value
        value = new_value
    need_to_modify(update_value)
    print(value) # prints 42

这对于“out-仅引用”或在具有多个线程/进程的情况下(通过使更新函数线程/多处理安全)。

显然,上面的内容不允许读取该值,而只能更新它。

Since it seems to be nowhere mentioned an approach to simulate references as known from e.g. C++ is to use an "update" function and pass that instead of the actual variable (or rather, "name"):

def need_to_modify(update):
    update(42) # set new value 42
    # other code

def call_it():
    value = 21
    def update_value(new_value):
        nonlocal value
        value = new_value
    need_to_modify(update_value)
    print(value) # prints 42

This is mostly useful for "out-only references" or in a situation with multiple threads / processes (by making the update function thread / multiprocessing safe).

Obviously the above does not allow reading the value, only updating it.

节枝 2025-01-22 18:49:35

考虑到 Python 处理值和对它们的引用的方式,引用任意实例属性的唯一方法是通过名称:

class PassByReferenceIsh:
    def __init__(self):
        self.variable = 'Original'
        self.change('variable')
        print self.variable

    def change(self, var):
        self.__dict__[var] = 'Changed'

当然,在实际代码中,您会在 dict 查找上添加错误检查。

Given the way Python handles values and references to them, the only way you can reference an arbitrary instance attribute is by name:

class PassByReferenceIsh:
    def __init__(self):
        self.variable = 'Original'
        self.change('variable')
        print self.variable

    def change(self, var):
        self.__dict__[var] = 'Changed'

In real code you would, of course, add error checking on the dict lookup.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文