如何通过引用传递变量?

发布于 2025-01-25 16:08:21 字数 835 浏览 3 评论 0 原文

我写了此类进行测试:

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

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

当我尝试创建一个实例时,输出为原始。因此,python中的参数似乎是按值传递的。那是对的吗?我如何修改代码以获取通过参考的效果,以使输出更改?


有时人们会惊讶地发现 x = 1 ,其中 x 是参数名称,不会影响呼叫者的参数,而是代码之类的代码。 x [0] = 1 做。发生这种情况是因为 item sissmentment 切片分配突变现有对象而不是重新分配变量的方法,尽管 = << /代码>语法。 呼叫者所感知的某些参数,而不是其他参数?

请参阅为什么函数可以修改 >另请参见“通过参考传递与通过价值传递的传递之间有什么区别?对于重要的,语言 - aggnostic术语讨论。

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-02-01 16:08:21

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

  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']

由于传递的参数是对 offecter_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_list outer_list 参考的副本,我们有 the_list 指向新列表,但是没有办法更改<<代码> outer_list 指向。

字符串 - 不变的类型

它是不变的,因此现在我们无能为力更改字符串的内容

,让我们尝试更改参考

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

再次,因为<代码> the_string 参数是按值传递的,为其分配了一个新字符串,没有效果。 the_string outer_string 参考的副本,我们有 the_string 指向一个新字符串,但是没有办法更改<代码> 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-02-01 16:08:21

问题来自对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 是字符串对象'原始'的引用。调用更改时,将第二个参考 var 向对象创建。在函数内部,您可以将参考 var 重新分配给其他字符串对象'更改',但是参考 self.variable.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-02-01 16:08:21

我发现其他答案相当长且复杂,因此我创建了这个简单的图来解释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-02-01 16:08:21

它既不是逐个价值,也不是通过引用 - 它是逐个呼叫。请参阅Fredrik Lundh:

通过对象调用

这是一个重要的报价:

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

在您的示例中,当更改方法被称为 - 名称空间 var 在该名称空间内成为一个名称,对于字符串对象'原始'。然后,该对象在两个名称空间中具有一个名称。接下来, var ='更改''绑定 var 与新的字符串对象,因此该方法的命名空间忘记了'Original'。最后,该名称空间被遗忘了,字符串'更改'以及其中。

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-02-01 16:08:21

想想通过分配传递的东西而不是通过参考/按值。这样,总是很明显,只要您了解正常分配过程中会发生什么。

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

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

由于无法修改不变的类型,它们似乎是按值传递的表示将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-02-01 16:08:21

Python中没有变量

了解参数传递的关键是停止思考“变量”。 Python中有名称和对象,一起
看起来像变量,但总是区分三个很有用。

  1. Python有名称和对象。
  2. 分配将名称绑定到对象。
  3. 将参数传递到函数中还将名称(函数的参数名称)绑定到对象。

这就是其中的全部。可变性与这个问题无关。

示例:

a = 1

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

b = x

这将名称 b 绑定到名称 x <的同一对象。 /代码>当前绑定到。
之后,名称 b 与名称 x 无关。

请参阅节 3.1 https://docs.python.org/3/reference/executermodel.html#naming-and-binding“ rel =“ noreferrer”> 4.2 python 3语言参考。

如何在问题中显示的代码中读取示例

,语句 self.change(self.variable)绑定名称 var (在函数范围内更改)到保存值'原始'的对象和分配 var ='更改''(在函数主体中更改)再次分配相同的名称:到其他对象(碰巧也容纳一个字符串,但可能完全是其他对象)。

如何通过引用传递,

因此,如果您要更改的内容是一个可变的对象,则没有问题,因为一切都是通过参考有效传递的。

如果是 noreferrer“> norReferrer”>不可能 bool,number,string),要走的方法是将其包裹在可变的对象中。
对此的快速解决方案是一个单元素列表(而不是 self.variable ,Pass [self.variable] ,在函数Modify 中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-02-01 16:08:21

Effbot(又名Fredrik Lundh)将Python的可变传递样式描述为呼叫: https://web.archive.org/web/2020201111195827/http:/http://effbot.org/zone/call-by-by-byby-object.htm

对象已分配在堆和指针上可以通过任何地方传递。

  • 当您进行诸如 x = 1000 之类的作业时,创建了一个字典条目,将当前名称空间中的字符串“ x”映射到指向包含一千个整数对象的指针。

  • 使用 x = 2000 更新“ x”时,将创建一个新的整数对象,并且词典已更新以指向新对象。旧的一千个对象没有变化(并且可能还活着,具体取决于其他任何内容)。

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

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

  • 列表之类的对象是可变。这意味着可以通过指向对象的任何内容来更改对象的内容。例如, x = []; y = x; X.Append(10);打印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-02-01 16:08:21

从技术上讲, 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-02-01 16:08:21

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

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-02-01 16:08:21

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

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-02-01 16:08:21

在这种情况下> var 。它不再指向 self.variable 。 修改 var self.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-02-01 16:08:21

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-02-01 16:08:21

这里的答案有很多见解,但我认为这里没有明确提及另一点。引用python文档 python中本地和全局变量的规则是什么?

在python中,仅在函数中引用的变量隐含全局。如果将变量分配给该函数正文内的任何地方,则假定它是本地的。如果变量在函数内部曾分配了一个新值,则该变量是隐式的本地局部,您需要明确将其声明为“全局”。
虽然一开始有些令人惊讶,但片刻的考虑解释了这一点。一方面,要求全局用于分配的变量为意外的副作用提供了一个标准。另一方面,如果所有全局参考都需要全局,则您将一直使用全局。您必须将其声明为全球对内置功能或导入模块的组件的每个引用。这个混乱将击败全球声明在识别副作用的有用性。

即使将可变的对象传递到一个函数时,仍然适用。对我来说,它清楚地说明了分配给对象和在函数中对象上操作的行为差异的原因。

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-02-01 16:08:21

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

http://docs.python.org/3/faq/programming.html#what-are-the-the-the-rules-for-local-and-global-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
半边脸i 2025-02-01 16:08:21

这是对概念的简单(我希望)解释 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-02-01 16:08:21

除了对这些东西在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-02-01 16:08:21

我使用以下方法将一些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-02-01 16:08:21

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

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-02-01 16:08:21

由于似乎无处提到了一种模拟参考的方法,例如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-02-01 16:08:21

鉴于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.

王权女流氓 2025-02-01 16:08:21

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

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-02-01 16:08:21

您只能将一个空的类用作存储参考对象的实例,因为内部对象属性存储在实例字典中。请参阅示例。

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-02-01 16:08:21

由于字典是通过引用传递的,因此您可以使用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-02-01 16:08:21

虽然通过参考可以很好地适合Python,因此很少使用,但实际上有些解决方法可以使对象当前分配给本地变量,甚至可以从称为函数的内部重新分配局部变量。

基本思想是具有可以执行该访问权限的函数,并且可以作为对象传递到其他功能中或存储在类中。

一种方法是在包装器函数中使用 Global (对于全局变量)或非局部(对于函数中的本地变量)。

def change(wrapper):
    wrapper(7)

x = 5

def setter(val):
    global x
    x = val

print(x)

相同的想法适用于阅读和 del et and and a a acte a a varible。

仅需阅读即可使用 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

Pythons“反射”支持使得获得能够在给定范围中重新分配名称/变量的对象不明确定义功能在该范围中:

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 类包含字典访问。因此,属性访问包装被转换为传递字典中的项目访问。通过传递内置当地人的结果和本地变量的名称,最终可以访问本地变量。截至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-02-01 16:08:21

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-02-01 16:08:21

我发现其他答案有些混乱,我不得不努力掌握这些概念。因此,我试图将答案放在我的语言中。如果其他答案也使您感到困惑,这可能会为您提供帮助。因此,答案就像是这样的 -

当您创建列表时 -

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,指向同一内存地址14070707924412080。但是,当您创建一个新列表[1,2,3,4]时,“列表”类的构造函数再次称为创建对象。该新对象具有完全不同的内存地址,例如140707924412112。当您将local_list2分配给[1,2,3,4]时,现在local_list2名称是指一个新的内存地址,该地址为14070707924412112。由于在整个过程中,没有对放置在内存地址的对象进行任何更改,它仍然不受影响。

换句话说,本着“其他语言有变量,python有名字”的精神。这意味着在其他语言中,变量被引用在内存中的固定地址。这意味着,在C ++中,如果您通过

a = 1
a = 2

存储“ 1”的值“ 1”的内存地址重新分配一个变量,则现在持有值'2',因此,值'1'完全丢失了。而在Python中,由于所有内容都是一个对象,因此“ a”较早地引用了存储类“ 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-02-01 16:08:21

我是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-02-01 16:08:21

另外,您可以使用 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)

: 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-02-01 16:08:21

使用 dataclasses 。另外,它允许您应用类型限制(又称“类型提示”)。

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-02-01 16:08:21

已经有很多很棒的答案(或者说意见),我已经阅读了,但是我想提到一个丢失的答案。一个来自 python的文档在FAQ部分中。我不知道发布此页面的日期,但这应该是我们的真实参考:

请记住,python中的论点是通过分配通过的。自从
作业只是创建对象的引用,没有别名
在呼叫者和Callee中的参数名称之间,因此
逐个引用
本身

如果您有:

a = SOMETHING

def fn(arg):
    pass

然后将其称为 fn(a),您正在做分​​配中的工作。因此,这发生了:

arg = a

创建了某物的附加引用。变量只是符号/名称/参考。他们没有“持有”任何东西。

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.

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