python文档似乎不清楚参数是通过引用还是通过值传递的,下面的代码生成的值"original"不变。
| 12
 3
 4
 5
 6
 7
 8
 
 | class PassByReference:def __init__(self):
 self.variable = 'Original'
 self.change(self.variable)
 print(self.variable)
 
 def change(self, var):
 var = 'Changed'
 | 
我可以通过实际引用传递变量吗?
		
		
- 有关简短的解释/说明,请参阅此StackOverflow问题的第一个答案。因为字符串是不可变的,所以不会更改它们,并且会创建一个新的变量,因此"外部"变量仍然具有相同的值。
- Blairconrad答案中的代码很好,但davidcournapeau和darenthomas提供的解释是正确的。
- 在阅读所选答案之前,请考虑阅读此短文本其他语言有"变量",python有"名称"。想想"名称"和"对象",而不是"变量"和"引用",你应该避免很多类似的问题。
- 为什么这不必要地使用一个类?这不是Java:P
- 另一个解决方法是创建这样的包装器"引用":ref=type("",(),'n':1)stackoverflow.com/a/1123054/409638
- 对于全局名称,可以通过将名称作为字符串传递并使用globals()来模拟按引用传递。def change(s): globals()▼显示 = 'changed'后接a = 'orig'; change('a'); print(a)打印'changed'。
- 某些不可变类型:int、float、long、complex、str、bytes、tuple、freezed set某些可变类型:byte array、list、set、dict
- python有变量。这个术语完全没有概念上的问题,而且它是常用的。
- JeffKnupp的博客和愚蠢的Python是对此的一些很好的解释。
 
	 
参数通过赋值传递。这背后的理由是双重的:
传入的参数实际上是对对象的引用(但引用是按值传递的)
有些数据类型是可变的,但另一些数据类型不是可变的
所以:
为了更清楚地说明这一点,我们来举几个例子。
列表-可变类型
让我们尝试修改传递给方法的列表:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | 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)
 | 
输出:
| 12
 3
 4
 
 | 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的引用,而不是它的副本,所以我们可以使用可变列表方法来更改它,并将更改反映在外部范围中。
现在让我们看看当我们试图更改作为参数传入的引用时会发生什么:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | 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)
 | 
输出:
| 12
 3
 4
 
 | 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指出的地方。
字符串-不可变类型
它是不可变的,所以我们无法更改字符串的内容。
现在,让我们尝试更改引用
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | 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)
 | 
输出:
| 12
 3
 4
 
 | before, outer_string = It was many and many a year agogot 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所指的位置。
我希望这能把事情弄清楚一点。
edit:注意到这并不能回答@david最初提出的问题,"我是否可以通过实际引用传递变量?"我们来研究一下。
我们怎么解决这个问题?
正如@andera的答案所示,您可以返回新值。这不会改变传递信息的方式,但会让您获得想要返回的信息:
| 12
 3
 4
 5
 6
 
 | 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)
 | 
如果您真的想避免使用返回值,可以创建一个类来保存您的值并将其传递到函数中,或者使用现有的类,如列表:
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | 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])
 | 
虽然这看起来有点麻烦。
		
		
- 在C中也是如此,当你通过"引用"时,你实际上是通过值传递引用…定义"参照":p
- 我不确定我理解你的条件。我已经退出C游戏有一段时间了,但回到我在里面的时候,没有"传递引用"——你可以传递东西,它总是传递值,所以参数列表中的任何内容都会被复制。但有时,它是一个指针,可以跟随到内存块(原语、数组、结构等),但不能更改从外部作用域复制的指针-完成函数后,原始指针仍然指向相同的地址。C++引入了引用,其行为不同。
- @安德里亚,一点也不像C。conrad是正确的,但是术语reference/values在python中是混淆的。这就是为什么你真的应该使用另一个术语(请参阅我到effbot的链接以获得很好的解释)
- 提出易变性只是增加了混乱。它实际上与这个问题没有任何关系。您首先声明它是正确的,因为您可以更改不在您的范围内的某个对象(如指针指针)的引用。
- @ZAC保龄球的易变性是完全相关的。如果字符串是可变的,那么第一个字符串示例将具有不同的输出。重要的是要知道,设置传入字符串的值将创建一个新的字符串对象,而不是修改传递的对象。在本例中,可变性是防止字符串参数的行为与整数参数的行为相同的原因。
- @卡姆·杰克逊实际上与此无关。通过引用传递意味着能够更改调用引用所使用的指针。谈到更改数据,指针的目的地增加了混乱,只是一个实现细节。
- @扎克保龄球,我真的不知道你说的话在实际意义上与这个答案有什么关系。如果一个python新手想知道通过ref/val传递,那么这个答案的要点是:1-你可以使用函数接收到的引用作为它的参数,修改变量的"outside"值,只要你不重新分配参数来引用一个新的对象。2-分配给不可变类型将始终创建一个新对象,这会中断对外部变量的引用。
- @Camjackson,您需要一个更好的例子-数字也是Python中不变的对象。此外,如果不在等号的左侧订阅任何赋值,是否会将名称重新分配给一个新对象,不管它是否不可变?def Foo(alist): alist = [1,2,3]不会从调用者的角度修改列表的内容。
- @blairconrad,在c中传递"by-reference"只是一种常见的约定,通过这种约定,可以将指针传递给某个对象,而不是某个对象的值。在这方面,它的行为与python完全相同:您可以更新指针指向的值,但是更新指针本身在函数之外没有任何效果,因为指针是通过值传递的。
- 提示sys._getframe(1).f_locals['new_string'] = 'foo'黑客。或者只是使用ctypes。:)
- - 1。显示的代码是好的,关于如何完全错误的解释。有关原因的正确解释,请参阅DavidCournapeau或Darenthomas的答案。
- "…,但如果在方法中重新绑定引用,外部作用域将一无所知,完成后,外部引用仍将指向原始对象。"这是错误的或至少不相关-您永远不能重新绑定引用。
- 哦,那太遗憾了。"它是按值传递的,但该值是一个引用"。叹息。好吧,一切都是有价值的。当值是引用时,这是通过引用传递的。python中的差异是最小的,因为python s变量与c的变量不同,但是调用pass-by值绝对是不正确的。
- 我投了反对票,因为为字符串和列表提供的代码(在函数体中进行赋值)基本上是相同的,并且具有相同的行为。列表是可变的,字符串不是。你的回答没有使我明白一件事。这让我意识到我完全不懂python。
- @LennartRegebro,仅仅因为值是对某个事物的引用,并不能通过引用传递它。在C++中,您实际上可以通过引用,并且当您这样做时,如果重新分配引用,则所传递的参数实际上将被修改。在python中,如果您试图重新分配参数,那么您只需更改函数名称空间中名称的映射,这是根本不同的。尽管如此,旁路参考的整个想法现在还是搞砸了,因为每个人都用它来描述不同的事情。
- @同义词:"C++"-嗯,是的,但现在我们谈论的是Python。python正在传递引用。因为它正在传递引用,所以这是合理的"传递引用"。在Python中,如果您尝试重新分配参数"—是的,但这是一个完全不同的问题。这是因为Python没有指向指向C/C++的内存位置的变量。它有对象,变量是这些对象的名称。
- (因此,我赞成大卫的文章,称之为"按对象调用"。)
- @LennartRegebro:我意识到了这一点,但最终您也可以考虑将python作为传递值,其值为引用。当然,这就是为什么我添加了最后一行。为不同的语言定义不同的术语意味着不同的事物,这完全是无用的,因为定义这些术语的关键是要让它明白你在说什么。
- 因此,这个答案实际上是不正确的,并且被不了解Python内部结构的新手作为"感谢"进行了升级。这就是为什么我会难过。
- @lennart regebro"它是一个作为值传递的引用,类型是一个引用/指针,您必须取消引用才能访问它。"我不知道这是怎么回事。Java使用引用,但它是按值传递的,并且行为与Python相同,因为它有对象和变量是引用这些对象的名称。您之前所做的"当值是引用时,这是传递引用。"语句不正确,因为如果它是传递值,则可以重新分配引用。但是,在传递引用中,如果试图重新分配变量,则实际上是在修改它。
- @synderesis:再次,然后:effbot.org/zone/call-by-object.htm请参阅davids答案。对于传递值的定义,则所有内容都是传递值。不存在使用该语句的传递引用,因为传递引用是指传递的值是引用,但不必将该值视为函数内的引用。Java所谓的"传递值和值是引用",正是通过引用的意思。整个问题是Python和Java所做的并不完全适用于其他语言中的任何概念。
- "莱纳特ReGeReo"不存在通过它的措辞的引用,因为通过引用是当您传递的值是引用时,"C++中,您可以有真正的按引用"。Java是按值传递的:StasOfFuff.com /Strus/40480/IS-Java-旁路引用
- @synderisis:我希望"移动这个聊天"选项总是可用的,对于我来说,同事们真的不是重复同样的事情的地方。
- "…参数传入…"是术语的错误用法。参数是函数(或方法)定义中的命名实体,用于指定函数可以接受的参数(在某些情况下,是参数)。参数是在调用函数时传递给函数(或方法)的值。
- 除非你看EDCOX1,0,等等,这和Java完全一样。在Java中,引用的地址空间是实现细节,但在Python中是某个范围内的字符串(EDCOX1(1))。在C中,它是一个全局范围内的数字。此外,Python中只有一种类型的引用(对象引用)和Java中的几个原语,其中的一个引用对象。python中的pass-by引用意味着"传递变量的作用域和名称",但目前没有这样的内容。它可以显式地完成——也就是说,当使用一个类(一个作用域)模拟这个机制时。
- 我经常看到术语"按对象引用调用"而不是"按分配调用"
- 作为传递名称引用的方法,为什么不将名称本身作为字符串传递?然后使用eval(name)获取名称引用的当前对象。
- jeffknupp.com/blog/2012/11/13/…studidpythonideas.blogspot.in/2013/11/…^这些也是一些很好的解释。
- "但是如果您在方法中重新绑定引用,外部作用域将一无所知",那么,如果传递的引用是可变的,并且您执行了一些操作(比如在列表中附加一些值),那么外部作用域将可以看到这些操作。只有执行重新绑定后,外部作用域才会停止看到更改。前面的所有操作都会影响原始对象。
- @伊桑弗曼,你错了。即使您阅读了davidcournapeau的答案中的链接(即按对象调用),您也会发现所有表达式"按对象调用"、"按共享调用"或"按对象引用调用"的含义与使用地址访问对象的含义相同。当然,必须从名称空间确定地址。我不能接受"按对象引用调用"与"按引用调用"不同。
- @blairconrad"传入的参数实际上是对对象的引用(但引用是通过值传递的)"。这听起来像是参照传递。传递引用和分配有什么不同?你能举个例子解释一下"旁路作业"吗?
- @ml_pro,"pass-by-assignment"似乎是由python文档管理器用来描述"pass-by-value"的术语。对于用户来说,我认为传递一个引用(或句柄)的值在函数中没有任何功能上的区别,如Java或C语言和Python所做的,而且我永远不会使用术语"PASS BASE赋值";它被编辑成答案,我假设与文档对齐。
 
	 
问题来自对Python中的变量的误解。如果你习惯了大多数的传统语言,你就有了一个心理模型,可以按照以下顺序进行:
您认为a是存储值1的内存位置,然后更新以存储值2。这不是Python中的工作方式。相反,a开始作为对值为1的对象的引用,然后重新分配为对值为2的对象的引用。即使a不再引用第一个对象,这两个对象也可能继续共存;事实上,它们可以由程序中的任何其他引用共享。
当使用参数调用函数时,将创建一个引用传入对象的新引用。这与函数调用中使用的引用不同,因此无法更新该引用并使其引用新对象。在您的示例中:
| 12
 3
 4
 5
 6
 
 | 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是独立的,不会更改。
唯一的方法是传递一个可变的对象。因为这两个引用引用同一个对象,所以对该对象的任何更改都会反映在这两个地方。
| 12
 3
 4
 5
 6
 
 | def __init__(self):         self.variable = ['Original']
 self.Change(self.variable)
 
 def Change(self, var):
 var[0] = 'Changed'
 | 
		
		
- 很好的简洁的解释。你的段落"当你调用一个函数时…"是我听过的一个相当晦涩的短语,"python函数参数是引用,通过值传递。"我认为如果你单独理解这个段落,所有其他的东西都是有意义的,并从那里作为逻辑结论流动。然后你只需要知道你什么时候创建一个新的对象,什么时候修改一个已有的对象。
- 但是如何重新分配引用呢?我以为你不能更改var的地址,但是你的字符串"changed"现在将被存储在var内存地址中。您的描述使它看起来像是"已更改"和"原始"属于内存中的不同位置,而您只是将"var"切换到不同的地址。对吗?
- @Glassjawed,我想你明白了。""changed"和"original"是位于不同内存地址的两个不同字符串对象,"var"从指向一个对象更改为指向另一个对象。
- 这是最好的解释,应该是公认的答案。简明扼要。另一些人提出了新的、令人困惑的术语,如"引用是通过值传递的"、"调用是对象"等。这个答案可以清除一切。谢谢)
- 使用id()函数有助于澄清问题,因为它可以在Python创建新对象时(我认为,无论如何)清楚地表明这一点。
- 哇…所以列表是可变的是有意义的,因为可以想象说"好的,python,为我做这些盒子"…然后可以修改框的内容。但是字符串和数字之类的东西可以不复杂地指向内存中的一系列1和0,因此用名称"标记"它们更为合理。
- 这个答案和effbot在python对象上发表的这篇博文一起使事情尽可能清晰。
- 为什么要使用这个命名约定?
- @它是直接从问题中复制出来的。
- 他们有change而不是change。
- @索洛莫尼克看了编辑历史,有人在2017年1月改变了它。
- 好点。那么,这个代码也应该更改。
- 函数调用不会创建新的引用-使用函数内部和外部的id函数来确认这一点。不同之处在于,当您试图在函数内部更改对象时,对象会发生什么。
- @tonysuffolk66 id给出了被引用对象的标识,而不是引用本身。
- Mark-我把你的引用用于表示一个对象;当一个函数被调用时,一个新的绑定被创建为一个新名称的现有对象(随着引用计数的增加)。我看到的大多数文档都将此作为新的绑定进行讨论,而不是新的引用。为误会道歉。
- @ TyySuffkk66我来自C++背景,所以我可能对术语有点松懈。C++引用与Python变量最接近。
- MarkRansom——严格地说,Python没有"变量"——而不是以C/C++方式拥有一个名为该名称的内存位置。python拥有的是一个或多个绑定到对象引用的名称,或者有时名称绑定到对象引用的容器。哦,术语的乐趣是——)
- 那么,我们可以说可变对象是通过引用传递的,而不可变对象不是吗?
- @不,它们都是通过引用传递的。只是对于一个不变的对象,引用对您没有帮助。
- @Markransom谢谢。从python文档中描述它的"按分配调用"方式对我来说更有意义,现在我明白了。
- @markransom什么是reference?你的定义不仅仅是指指向/指向一个对象的"某物"(这是一个有助于描述引用does是什么,但不一定是什么的有用定义)。
- @用最简单的术语来说,引用是指"引用"对象的东西。它的物理表示形式很可能是一个指针,但这只是一个实现细节。它实际上是一个抽象的概念。
 
	 
我发现其他的答案相当长和复杂,所以我创建了这个简单的图来解释Python处理变量和参数的方式。
		
		
- 你的图表很好地阐明了基础概念,尽管我不会把其他答案称为长而复杂的答案。
- 非常优雅的解释。我认为其他的答案也很长很复杂。干得好!
- 图片中有错误。B=A表示A分配给B。
- 可爱,让人很容易发现中间有一个任务的细微差别,对一个偶然的旁观者来说并不明显。+ 1
- A是否可变并不重要。如果你把不同的东西分配给B,A不会改变。如果一个对象是可变的,当然可以改变它。但这与直接将任务分配给某个名称无关。
- @玛蒂,你说得对。我去掉了答案中提到易变性的部分。我觉得现在不能再简单了。
- 谢谢你的更新,好多了!使大多数人困惑的是订阅的分配;例如,B[0] = 2,而直接分配则是B = 2。
- "A被分配给B。"这不是含糊不清的吗?我认为在普通英语中,这可以表示A=B或B=A。
- 我确实很喜欢视觉表现,但仍然忽略了mutable和immutable的观点,这使得右腿不活动,因为没有可用的append。(不过,对视觉代表的支持率仍有上升趋势):)
- 这个答案很好。我会把其他答案称为长而复杂的@brucewayne,因为它们确实是-如果一些东西可以在这个简单的图表中解释,或者在一个非常长而详细的答案中解释,而这两个答案都教授相同的东西,那么长的答案就复杂了,因为没有附加值。所有冗长的回答都是我个人困惑的原因,这帮助我理清了思路。
 
	 
它既不是传递值也不是传递引用-它是由对象调用的。见Fredrik Lundh:
http://effbot.org/zone/call-by-object.htm(按对象调用)
这是一个重要的引述:
"...variables [names] are not objects; they cannot be denoted by other variables or referred to by objects."
在您的示例中,当调用Change方法时--为其创建一个命名空间;并且var在该命名空间中成为字符串对象'Original'的名称。然后,该对象在两个名称空间中有一个名称。接下来,var = 'Changed'将var绑定到一个新的字符串对象,因此该方法的命名空间忘记了'Original'。最后,这个名称空间被遗忘了,字符串'Changed'也被遗忘了。
		
		
- 我觉得很难买到。对我来说,就像Java一样,这些参数是指向内存中对象的指针,这些指针是通过堆栈或寄存器传递的。
- 这不像Java。其中一种情况是不可变对象。考虑简单的函数lambda x:x。将其应用于x=[1,2,3]和x=(1,2,3)。在第一种情况下,返回值将是输入的副本,在第二种情况下相同。
- +1个链接。很好地消除了误解,Python是一种奇怪的形式的引用值(或者通过赋值传递,或者通过引用或任何副本的拷贝),这对于那些使用C和C++的人来说是没有意义的。
- 不,它完全类似于Java的对象语义。我不确定您所说的"在第一种情况下,返回的值将是输入的一个副本,在第二种情况下是相同的"是什么意思,但该语句似乎明显不正确。
- 我的评论确实是错误的,但是我认为Python语义学与Java不同,这是在参考文献中所解释的。在Python中,函数中的更改是否在其外部可见,因为它的参数取决于可更改性,而不是特定类型(例如,对象与Java中的原始类型),并且Pothon中的所有参数传递协议都是相同的。
- 它和Java完全一样。对象引用按值传递。任何认为不同的人都应该为可以交换两个引用的swap函数附加python代码,如:a = [42] ; b = 'Hello'; swap(a, b) # Now a is 'Hello', b is [42]。
- 无论某个对象是否为对象都是无关的(尤其是因为几乎所有东西都是Python中的对象,例如int、float等等)。易变性很重要。您可以编写一个交换函数,用于列表(可变),但不用于元组或字符串(不可变)。还可以看到:Python Curr.Eu/PuxIngAgulMuts.PHP,它在某些情况下清楚地显示了不同于Java的行为。
- effbot.org/zone/call-by-object.htm页面不再可用:"502–坏网关/您请求的页面当前不可用。请稍后再试。"有其他具有相同内容的链接吗?
- 在Java中传递对象时,Java与Java完全相同。但是,Java也有原语,它们通过复制原语的值而被传递。因此,在这种情况下,它们是不同的。
- @ Claudiu:它不仅在Java原语的情况下,Java中的任何不可变对象也会与原语(如Java对象的字符串)相同。
- @ Claudiu是100%正确的,除了Java与其他原语的行为没有区别——它只是有附加的原语,其中Python没有。当你看EDCOX1、2和其他元材料时,其他的差异是可见的。作用域以不同的方式被实现,这个实现细节在Python。
 
	 
想想那些通过赋值而不是通过引用/值传递的东西。这样,只要你理解在正常作业中发生的事情,一切都很清楚。
因此,当将列表传递给函数/方法时,该列表被分配给参数名。附加到列表将导致列表被修改。在函数内重新分配列表不会更改原始列表,因为:
| 12
 3
 4
 5
 
 | a = [1, 2, 3]b = a
 b.append(4)
 b = ['a', 'b']
 print a, b      # prints [1, 2, 3, 4] ['a', 'b']
 | 
由于不能修改不可变类型,它们看起来像是按值传递的——将int传递给函数意味着将int分配给函数参数。您只能重新分配它,但它不会改变原始变量的值。
		
		
- 乍一看,这个答案似乎回避了原来的问题。在读了第二遍之后,我开始意识到这件事很清楚。这里可以找到对这个"名称分配"概念的一个很好的跟进:类似于pythonista的代码:惯用的python
 
	 
effbot(又名fredrik lundh)将python的变量传递样式描述为按对象调用:http://effbot.org/zone/call-by-object.htm
对象在堆上分配,指向它们的指针可以在任何地方传递。
- 当进行诸如x = 1000这样的赋值时,将创建一个字典条目,该条目将当前命名空间中的字符串"x"映射到指向包含1000的整数对象的指针。 
- 使用x = 2000更新"x"时,将创建一个新的整数对象,并更新字典以指向新对象。旧的一千个对象是不变的(可能还活着,也可能不活着,这取决于是否有其他的对象)。 
- 当您执行新的赋值(如y = x时,将创建一个新的字典条目"y",该条目指向与"x"条目相同的对象。 
- 像字符串和整数这样的对象是不可变的。这仅仅意味着在创建对象之后,没有方法可以更改它。例如,一旦创建了整数对象1000,它将永远不会改变。数学是通过创建新的整数对象来完成的。 
- 像列表这样的对象是可变的。这意味着可以通过指向对象的任何内容来更改对象的内容。例如,x = []; y = x; x.append(10); print y将打印[10]。已创建空列表。"x"和"y"都指向同一列表。append方法改变(更新)列表对象(如向数据库添加记录),结果对"x"和"y"都可见(就像数据库更新对该数据库的每个连接都可见一样)。 
希望你能澄清这个问题。
		
		
- 我非常感谢从开发人员那里了解到这一点。像Pepr的答案所建议的那样,id()函数返回指针的(对象引用的)值是真的吗?
- @是的,在cpython中,id()返回地址。但是在其他的python中,比如pypy和jython,id()只是一个唯一的对象标识符。
- 这个答案,再加上使用id()的提示,当一个分配将一个现有的名称绑定到一个新的对象时,这个提示在整个线程中是最好的。
 
	 
从技术上讲,python总是使用pass-by引用值。我要重复我的另一个答案来支持我的陈述。
python总是使用传递引用值。没有例外。任何变量赋值都意味着复制引用值。也不例外。任何变量都是绑定到引用值的名称。总是。
可以将引用值视为目标对象的地址。地址在使用时自动取消引用。这样,使用引用值时,您似乎直接使用目标对象。但是在两者之间总是有一个参照物,要跳到目标上还要多走一步。
下面的示例证明了Python使用了按引用传递:

如果参数是按值传递的,则不能修改外部lst。绿色是目标对象(黑色是存储在其中的值,红色是对象类型),黄色是内存,其中的引用值——绘制为箭头。蓝色实心箭头是传递给函数的参考值(通过蓝色虚线箭头路径)。难看的深黄色是内部词典。(实际上也可以画成绿色椭圆。颜色和形状只表示它是内部的。)
您可以使用id()内置函数来了解参考值是什么(即目标对象的地址)。
在编译语言中,变量是能够捕获类型值的内存空间。在Python中,变量是绑定到保存目标对象引用值的引用变量的名称(在内部捕获为字符串)。变量的名称是内部字典中的键,该字典项的值部分存储对目标的引用值。
引用值隐藏在python中。没有任何用于存储引用值的显式用户类型。但是,可以使用一个列表元素(或任何其他合适容器类型中的元素)作为引用变量,因为所有容器都将元素存储为对目标对象的引用。换句话说,元素实际上不包含在容器中——只有对元素的引用才是。
		
		
- 实际上,这是通过参考值确认的。+1对于这个答案,尽管示例不好。
- 发明新术语(例如"传递引用值"或"按对象调用"没有帮助)。call by(value reference name)"是标准术语。"参考"是一个标准术语。通过值传递引用准确地描述了使用标准术语的Python、Java和其他语言的宿主的行为。
- @Cayhorstmann:问题是python变量的术语含义与其他语言中的术语含义不同。这样,参照调用在这里不合适。另外,您如何准确地定义术语引用?非正式地,可以很容易地将python方法描述为传递对象的地址。但它不适合可能分布式的Python实现。
- 我喜欢这个答案,但是你可以考虑这个例子是否真的有助于或者伤害了流程。此外,如果您将"引用值"替换为"对象引用",您将使用我们可以认为是"正式"的术语,如这里所示:定义函数
- 但是官员说……"参数是通过按值调用传递的(其中值始终是一个对象引用,而不是对象的值)。"通过这种方式,您可能会尝试将它以文本形式替换为…参数是通过引用调用传递的,这有点令人困惑,因为它不是真的。这种混淆是由一种更为复杂的情况引起的,在这种情况下,没有一个经典术语是完全适合的。我没有找到任何能说明这种行为的简单例子。
- 在引用的末尾有一个脚注,上面写着:"实际上,通过对象引用调用将是更好的描述,因为如果传递了一个可变对象,调用方将看到被调用方对它所做的任何更改…"我同意您的看法,混淆是由尝试适应其他语言建立的术语引起的。撇开语义不谈,需要理解的是:字典/名称空间、名称绑定操作以及名称→指针→对象的关系(正如您已经知道的那样)。
 
	 
我通常使用的一个简单技巧是将它包装在一个列表中:
| 12
 3
 4
 5
 6
 
 | def Change(self, var):var[0] = 'Changed'
 
 variable = ['Original']
 self.Change(variable)
 print variable[0]
 | 
(是的,我知道这很不方便,但有时做起来很简单。)
		
		
- +1对于少量的文本,提供必要的解决方案来解决Python没有通过引用的问题。(下面的注释/问题不仅适用于此页面,也适用于此页面上的任何地方:我不清楚为什么python不能像c那样提供"ref"关键字,它只是将调用方的参数包装在这样的列表中,并将函数中对参数的引用视为列表的第0个元素。)
- 很好。若要通过引用,请将其包装在[]中。
 
	 
(编辑-布莱尔更新了他广受欢迎的答案,使其准确无误)
我认为重要的是要注意到,目前获得最多选票的职位(布莱尔·康拉德),虽然其结果是正确的,但具有误导性,并且根据其定义是不正确的。虽然有许多语言(如C)允许用户通过引用或传递值,但python不是其中之一。
大卫·库尔纳波的回答指向了真实的答案,并解释了为什么布莱尔·康拉德的文章中的行为似乎是正确的,而定义却不正确。
如果python是传递值,那么所有语言都是传递值,因为必须发送一些数据(无论是"值"还是"引用")。然而,这并不意味着在C程序员会想到的意义上,python是传递值。
如果你想这样做,布莱尔·康拉德的回答很好。但是如果你想知道为什么python既不是传递值,也不是传递引用,那么请阅读大卫·库纳波的答案。
		
		
- 并非所有语言都是按值调用的。在C++或Pascal(当然还有许多其他我不知道的)中,你可以通过引用来调用。例如,在C++中,EDCOX1(3)将交换传递给它的变量。在Pascal中,使用var而不是&。
- 我以为我早就回复了,但我看不出来。为了完整-凯霍斯特曼误解了我的答案。我并不是说在大多数人第一次学习C/C++的时候,所有东西都是有价值的。只是传递了一些值(值、名称、指针等),布莱尔最初的回答中使用的术语不准确。
 
	 
python中没有变量
理解参数传递的关键是停止思考"变量"。在python中有名称和对象,它们在一起看起来像变量,但总是区分这三个变量是有用的。
python有名称和对象。
赋值将名称绑定到对象。
将参数传递到函数中也会将名称(函数的参数名称)绑定到对象。
这就是一切。易变性与这个问题无关。
例子:
这会将名称a绑定到一个包含值1的integer类型的对象。
这会将名称b绑定到名称x当前绑定到的同一对象。此后,名称b与名称x不再有任何关系。
请参见Python3语言参考中的第3.1和4.2节。
因此,在问题中所示的代码中,语句self.Change(self.variable)将名称var(在函数Change的范围内)绑定到持有值'Original'的对象,而赋值var = 'Changed'(在函数Change的主体中)再次将相同的名称分配给另一个对象好吧,但可能是完全不同的东西)。
		
		
- "Python没有变数"是一个愚蠢而混乱的口号,我真的希望人们不要再这么说了…:(剩下的答案很好!
- 这可能令人震惊,但并不愚蠢。我也不认为这会让人困惑:它希望能让接受者对即将到来的解释敞开心扉,让她有一种有用的"我想知道他们有什么而不是变数"的态度。(是的,您的里程可能会有所不同。)
- 你还会说javascript没有变量吗?它们与Python的工作原理相同,Java,Ruby,PHP,…我认为更好的教学技巧是,"python的变量与c的工作方式不同。"
- 是的,Java有变量。Python和JavaScript、Ruby、PHP等也是如此。在Java中,你不会用EDCOX1 0声明一个变量,但是EDCOX1×1不。它们都声明变量。Integer变量是一个对象,int变量是一个原语。作为一个例子,您通过显示a = 1; b = a; a++ # doesn't modify b来演示变量的工作方式。在python中也是如此(使用+= 1,因为python中没有++!
- "变量"的概念很复杂,常常很模糊:变量是由名称标识的值的容器。在python中,值是对象,容器是对象(参见问题?)名字其实是分开的。我相信用这种方式准确理解变量要困难得多。名称和对象的解释看起来更困难,但实际上更简单。
- 我真的不明白这个答案对这个问题有什么帮助。没有给出解决方案,只叙述了Python的现状。你怎么能通过一个论点来改变它呢?这里的大多数答案都没有给出解决方案。但这一条,否认了明显存在的第一行,所以它脱颖而出。抱歉投了反对票,但这让我有点生气。
- var&183;i&183;a&183;表格?弗尔?B(?)l/submit形容词1。不一致的或有固定模式的;容易改变的。我认为python确实有"变量"
- "这就是一切。易变性与这个问题无关。如果self.variable是可变的,那么可以在函数self.change()内对其进行更改。因此,将不可变类型作为参数函数传递将永远不会影响传递的"变量"。
- 我喜欢这个答案。为了进一步理解"变量"和"名称"(强调引号)的概念,可以在这里找到:类似于pythonista的代码:惯用的python
 
	 
你得到了一些很好的答案。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | 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' )
 | 
		
		
- 是的,但是如果你做x=[2,4,4,5,5],y=x,x[0]=1,打印x[1,4,4,5,5]打印y[1,4,4,5]
 
	 
在这种情况下,方法Change中名为var的变量将被分配一个对self.variable的引用,然后立即将一个字符串分配给var。它不再指向self.variable。下面的代码片段显示了如果修改var和self.variable所指向的数据结构(在本例中是一个列表),会发生什么情况:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | >>> 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']
 >>>
 | 
我相信其他人可以进一步澄清这一点。
Python的PASS分配方案与C++的参考参数选项不完全相同,但实际上与C语言(和其他语言)的参数传递模型非常类似:
- 不可变参数实际上是"按值"传递的。像整数和字符串这样的对象是由对象引用传递的,而不是通过复制传递的,但是由于无论如何都不能在适当的位置更改不可变对象,因此效果很像复制。
- 可变参数通过"指针"有效传递。对象如列表字典也通过对象引用传递,这类似于C将数组作为指针传递。可以在函数中就地更改可变对象,很像C数组。
正如您所说,您需要有一个可变的对象,但让我建议您检查全局变量,因为它们可以帮助您甚至解决此类问题!
http://docs.python.org/3/faq/programming.html python中局部变量和全局变量的规则是什么?
例子:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 
 | >>> 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
 | 
		
		
- 我想发表一个类似的回答——最初的提问者可能不知道他想要的实际上是使用一个在函数之间共享的全局变量。下面是我将分享的链接:stack overflow.com/question s/423379/&hellip;作为对@tim的回答,stack overflow不仅仅是一个问答网站,它是一个庞大的参考知识库,只会变得更强大和更细微-就像一个活跃的wiki-有更多的输入。
 
	 
这里有很多关于答案的见解,但我认为这里没有明确提到另外一点。从python文档中引用https://docs.python.org/2/faq/programming.html python中局部和全局变量的规则是什么
"在python中,只在函数内部引用的变量是隐式全局的。如果在函数体中的任何位置为变量分配了一个新值,则假定该变量为局部变量。如果在函数中为变量分配了一个新值,则该变量是隐式局部变量,您需要将其显式声明为"global"。虽然一开始有点惊讶,但片刻的考虑可以解释这一点。一方面,为指定的变量要求全局性可以提供一个防止意外副作用的条。另一方面,如果所有全局引用都需要全局引用,那么您将一直使用全局引用。您必须将对内置函数或导入模块的组件的每个引用声明为全局引用。这种混乱将破坏全球声明识别副作用的有效性。"
即使将可变对象传递给函数,这仍然适用。对我来说,清楚地解释了分配给对象和在函数中操作对象之间行为差异的原因。
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | 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)
 | 
给予:
| 12
 3
 4
 
 | Original [1, 2, 3] 4454645632Received [1, 2, 3] 4454645632
 Changed to [0, 0, 0] 4474591928
 After [1, 2, 3] 4454645632
 | 
因此,对未声明为全局的全局变量的赋值将创建一个新的本地对象,并断开与原始对象的链接。
下面是对Python中使用的pass by object概念的简单(我希望)解释。每当您将对象传递给函数时,对象本身就被传递(Python中的对象实际上是您在其他编程语言中调用的值),而不是对该对象的引用。换句话说,当你打电话时:
| 12
 3
 4
 5
 
 | def change_me(list):list = [1, 2, 3]
 
 my_list = [0, 1]
 change_me(my_list)
 | 
正在传递实际对象-[0,1](在其他编程语言中称为值)。因此,实际上,函数change_me将尝试执行如下操作:
这显然不会改变传递给函数的对象。如果函数如下所示:
| 12
 
 | def change_me(list):list.append(2)
 | 
然后调用将导致:
这显然会改变物体。这个答案解释得很好。
		
		
- 问题是,这项任务所做的事情超出了你的预期。list = [1, 2, 3]导致将list名称重新用于其他内容,并忘记了最初传递的对象。但是,您可以尝试使用list[:] = [1, 2, 3](顺便说一下,list是变量的错误名称。想到[0, 1] = [1, 2, 3]完全是胡说八道。不管怎样,您认为对象本身被传递意味着什么?你认为该职能部门复制了什么?
- @Pepr对象不是文本。它们是物体。谈论他们的唯一方法就是给他们起一些名字。这就是为什么一旦你掌握了它就这么简单,但解释起来却非常复杂的原因。-)
- @维基:我知道。无论如何,列表文本被转换为列表对象。实际上,python中的任何对象都可以不带名称存在,即使没有任何名称,也可以使用它。你可以把它们看作是匿名对象。考虑对象是列表的元素。他们不需要名字。您可以通过索引或遍历列表来访问它们。不管怎样,我坚持认为[0, 1] = [1, 2, 3]只是一个坏例子。在Python中没有类似的东西。
- @Pepr:我不一定是指Python定义名称,只是普通名称。当然,alist[2]算作alist第三个元素的名称。但我想我误解了你的问题。-)
- 哎呀。我的英语明显比我的Python差得多。:-)我再试试。我只是说,你必须给对象起一些名字来谈论它们。我所说的"名称"并不是指"由python定义的名称"。我知道python机制,不用担心。
 
	 
除了所有关于这些东西如何在Python中工作的很好的解释之外,我没有看到一个简单的关于这个问题的建议。就像创建对象和实例一样,处理实例变量和更改实例变量的方法如下:
| 12
 3
 4
 5
 6
 7
 8
 
 | class PassByReference:def __init__(self):
 self.variable = 'Original'
 self.Change()
 print self.variable
 
 def Change(self):
 self.variable = 'Changed'
 | 
在实例方法中,通常使用self访问实例属性。在__init__中设置实例属性并在实例方法中读取或更改这些属性是正常的。这也是为什么你把self作为第一个论点传给def Change的原因。
另一种解决方案是创建这样的静态方法:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | 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
 | 
有一个小技巧可以通过引用传递一个对象,即使语言不能使之成为可能。它也在Java中工作,它是带有一个项目的列表。;-)
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 
 | class PassByReference:def __init__(self, name):
 self.name = name
 
 def changeRef(ref):
 ref[0] = PassByReference('Michael')
 
 obj = PassByReference('Peter')
 print obj.name
 
 p = [obj] # A pointer to obj! ;-)
 changeRef(p)
 
 print p[0].name # p->name
 | 
这是一个丑陋的黑客,但它起作用。-P
		
		
- p是对可变列表对象的引用,可变列表对象反过来存储对象obj。引用"p"被传递到changeRef中。在changeRef中,会创建一个新的引用(新引用称为ref,指向p指向的同一列表对象。但是,由于列表是可变的,所以两个引用都可以看到对列表的更改。在本例中,使用ref引用更改索引0处的对象,以便随后存储PassByReference('Michael')对象。对list对象的更改是使用ref完成的,但是这个更改对p是可见的。
- 现在,引用p和ref指向存储单个对象的列表对象PassByReference('Michael')。因此,p[0].name返回Michael。当然,ref已经超出了范围,可能会被垃圾收集,但都是一样的。
- 但是,您没有更改与引用obj关联的原始PassByReference对象的私有实例变量name。实际上,obj.name将返回Peter。上述评论假设Mark Ransom给出的定义。
- 也就是说,我不同意这是一种黑客行为(我认为这是指一些有用的东西,但出于未知、未经测试或实施者无意的原因)。您只需将列表中的一个PassByReference对象替换为另一个PassByReference对象,并引用这两个对象中的后者。
 
	 
我使用以下方法快速地将两个Fortran代码转换为python。是的,当最初的问题被提出时,它不是通过引用传递的,但在某些情况下是一个简单的工作。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | a=0b=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
 | 
虽然pass-by引用不适合python,而且很少使用,但有一些解决方法可以实际工作,以使对象当前分配给局部变量,甚至从被调用函数内部重新分配局部变量。
其基本思想是拥有一个可以进行访问的函数,并且可以作为对象传递到其他函数或存储在类中。
一种方法是在包装函数中使用global(用于全局变量)或nonlocal(用于函数中的局部变量)。
| 12
 3
 4
 5
 6
 7
 8
 
 | def change(wrapper):wrapper(7)
 
 x = 5
 def setter(val):
 global x
 x = val
 print(x)
 | 
同样的思想也适用于读取和使用一个变量。
对于只是阅读,甚至还有一种更短的方法来使用lambda: x,它返回一个可调用的,当被调用时返回当前的x值。这有点像过去语言中使用的"按名称调用"。
传递3个包装器来访问变量有点难,因此可以将这些包装器包装到具有代理属性的类中:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 
 | 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的"反射"支持使获得一个对象成为可能,该对象能够在给定范围内重新分配名称/变量,而无需在该范围内显式定义函数:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 
 | 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文档建议更改字典可能不起作用,但它似乎对我有用。
考虑到python处理值和引用这些值的方式,引用任意实例属性的唯一方法是按名称:
| 12
 3
 4
 5
 6
 7
 8
 
 | class PassByReferenceIsh:def __init__(self):
 self.variable = 'Original'
 self.change('variable')
 print self.variable
 
 def change(self, var):
 self.__dict__[var] = 'Changed'
 | 
当然,在真正的代码中,您可以在dict查找中添加错误检查。
Python中的PASS引用与C++/Java中的按引用传递概念有很大的不同。
- Java:通过引用传递所有内容,因此调用函数中的参数所做的所有更改都是调用方可见的。
- C++:允许通过引用或通过值传递。如果参数是通过引用传递的,则可以根据参数是否作为常量传递来修改它。但是,无论常量与否,参数维护对对象的引用,并且不能将引用分配给所调用函数中的其他对象。
- Python:python是"pass-by-object-reference",其中常有这样的说法:"object-references是按值传递的。"[此处阅读]1。调用者和函数都引用同一个对象,但函数中的参数是一个新变量,它只在调用者中保存对象的副本。像C++一样,一个参数可以在函数中被修改或不被修改,这取决于传递的对象的类型。在被调用函数中不能修改不可变的对象类型,而可变的对象可以更新或重新初始化。更新或重新分配/重新初始化可变变量之间的一个重要区别是,更新后的值会反映回被调用的函数中,而重新初始化的值则不会。将新对象分配给可变变量的范围是Python中函数的局部范围。@blair conrad提供的例子很好地理解了这一点。
由于您的示例恰好是面向对象的,因此您可以进行以下更改以获得类似的结果:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | 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'
 | 
		
		
- 尽管这是可行的。它不是通过引用传递的。它是"通过对象引用"。
 
	 
您只能使用空类作为实例来存储引用对象,因为内部对象属性存储在实例字典中。请参见示例。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 | 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)
 |