当前位置:  开发笔记 > 编程语言 > 正文

如何通过引用传递变量?

如何解决《如何通过引用传递变量?》经验,为你挑选了18个好方法。

Python文档似乎不清楚参数是通过引用还是值传递,以下代码生成未更改的值'Original'

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

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

有什么我可以通过实际参考传递变量吗?



1> Blair Conrad..:

参数通过赋值传递.这背后的理由是双重的:

    传递的参数实际上是一个参考的一个对象(但参考通过值传递)

    一些数据类型是可变的,但其他数据类型则不可变

所以:

如果你将一个可变对象传递给一个方法,那么该方法会获得对同一个对象的引用,你可以将它改变为你心中的喜悦,但是如果你在方法中重新引用引用,那么外部范围将对它一无所知,之后你完成后,外部引用仍将指向原始对象.

如果将不可变对象传递给方法,则仍然无法重新绑定外部引用,甚至无法改变对象.

为了更清楚,让我们举一些例子.

列表 - 可变类型

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

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指向的位置.

字符串 - 不可变类型

它是不可变的,所以我们无法改变字符串的内容

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

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

虽然这看起来有点麻烦.


然后同样在C中,当你通过"引用"时,你实际上是通过_by value_引用...定义"通过引用":P
我不确定我理解你的条款.我已经离开了C游戏一段时间了,但当我进入它时,没有"通过参考传递" - 你可以传递东西,它总是按值传递,所以参数列表中的任何内容被复制了.但有时候东西是一个指针,它可以跟随内存(原始,数组,结构,等等),但你无法改变从外部作用域复制的指针 - 当你完成了这个功能,原始指针仍指向同一地址.C++引入了引用,其行为不同.
-1.显示的代码很好,关于如何完全错误的解释.请参阅DavidCournapeau或DarenThomas的答案,以获得有关原因的正确解释.
@Zac保龄球我真的不明白你所说的话在实际意义上与这个答案有关.如果Python新手想知道通过ref/val传递,那么这个答案的结果是:**1 - **你*可以*使用函数接收的引用作为其参数,来修改'outside'值变量,只要您不重新分配参数以引用新对象.**2 - **分配给一个不可变类型将*总是*创建一个新对象,它打破了你对外部变量的引用.
@CamJackson,你需要一个更好的例子 - 数字也是Python中的不可变对象.此外,如果没有在equals左侧下标的*any*赋值将重新分配给新对象,无论它是否是不可变的,这不是真的吗?`def Foo(alist):alist = [1,2,3]`****不会**从调用者的角度修改列表的内容.
哦,那很难过."它通过值传递,但值是参考".叹.嗯,*一切*是一个价值.当值是引用时,通过引用传递.Python中的差异很小,因为Pythons变量不像C的变量,但调用它传递值肯定是不正确的.
@Lennart Regebro,只是因为该值是对某事物的引用并不能使其通过引用传递.在C++中,您实际上可以通过引用传递,当您执行此操作时,如果重新分配引用,则实际将修改您传递的参数.在Python中,如果您尝试重新分配参数,您将只更改函数名称空间中名称的映射,这是根本不同的.尽管如此,通过引用传递的整个想法现在已经搞砸了,因为每个人都在使用它来描述不同的东西.
@BlairConrad,在C中传递"by reference"只是一个公认的约定,你将指针传递给某些东西而不是某些东西的值.在这方面,它的行为与Python完全相同:您可以更新指针指向的值,但更新指针本身在函数外部没有任何影响,因为指针是按值传递的.
提出可变性只会增加混乱.它实际上没有任何问题.你首先说明它是正确的,因为你可以改变你的范围内的东西的引用(比如指针指针).
@Zac Bowling Mutability是完全相关的.如果字符串是可变的,那么第一个字符串示例将具有不同的输出.重要的是要知道设置传入字符串的值将创建一个新的字符串对象,而不是修改传递的字符串对象.可变性是防止字符串参数表现相同的原因,在这种情况下是整数参数.
_"...传入的参数......"_是不正确的术语使用.[parameter](http://docs.python.org/3/glossary.html#term-parameter)是函数(或方法)定义中的命名实体,它指定一个参数(或在某些情况下,参数)功能可以接受.[argument](http://docs.python.org/3/glossary.html#term-argument)是在调用函数时传递给函数(或方法)的值.
@Cam Jackson实际上并不相关.通过引用传递意味着能够更改调用引用正在使用的指针.谈论更改数据时,指针的目标会增加混乱,只会增加实现细节.
@andrea,不是它根本不像C.Conrad是正确的,但术语引用/值在python中令人困惑.这就是为什么你应该真正使用另一个术语(请参阅我的链接到effbot获得一个很好的解释)
@Synderesis:*"在C++中"......* - 嗯,是的,但现在我们谈论的是Python.Python正在传递引用.由于它是通过引用,这是合理的"通过引用传递".*"在Python中,如果您尝试重新分配参数"* - 是的,但这是一个完全不同的问题.这是因为Python没有指向像C/C++这样的内存位置的变量.它有对象,变量是这些对象的名称.
@Lennart Regebro:我意识到这一点,但最后你还可以考虑使用值作为引用来传递Python.当然,这就是我最后添加最后一行的原因.将术语定义为不同语言的不同内容无论如何都是无用的,因为定义这些术语的重点在于明确你所说的内容.
因此,这个答案在事实上是不正确的,并且被不了解Python内部的新手称赞为"谢谢".这就是为什么它让我伤心.
@Lennart Regebro"它是一个作为值传递的引用,类型是引用/指针,你必须取消引用才能访问它." 我不明白这是怎么回事.Java使用引用但是按值传递,并且行为与Python相同,因为它具有对象和变量是引用这些对象的名称.您之前所做的陈述"当值是参考时,通过引用传递." 是不正确的,因为如果它通过值,您可以重新分配引用.但是,在通过引用传递时,如果您尝试重新分配您实际正在修改它的变量.
@Synderesis:再一次,然后:http://effbot.org/zone/call-by-object.htm参见Davids的回答.通过定义传值,那么一切都是值得通过的.对于它的措辞没有传递引用,因为传递引用的是当您传递的值是引用时,但您不必将该值视为函数内的引用.Java所谓的"按值传递,值是一个引用"是**正是传递引用的内容.**重点是Python**和Java**所做的***不是完全符合其他语言中使用的概念***.
@Lennart Regebro"对于它的写法没有传递引用,因为传递引用的是你传递的值是一个引用"在C++中你可以通过引用真正传递.Java是值得传递的:http://stackoverflow.com/questions/40480/is-java-pass-by-reference

2> Mark Ransom..:

问题来自对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'


简洁明了的解释.你的段落"当你调用一个函数......"是我听说过的最好的解释之一,即'Python函数参数是引用,通过值传递的相当神秘的短语'.我想如果你单独理解这一段,那么其他一切只是有意义,并从那里作为一个合乎逻辑的结论.然后,您只需要知道何时创建新对象以及何时修改现有对象.
@Glassjawed,我想你已经明白了."Changed"和"Original"是不同内存地址处的两个不同字符串对象,"var"从指向一个指向另一个指向另一个.
但是如何重新分配参考?我以为你不能改变'var'的地址,但你的字符串"Changed"现在将被存储在'var'内存地址中.您的描述使其看起来像"已更改"和"原始"属于内存中的不同位置而您只需将"var"切换到其他地址即可.那是对的吗?

3> Zenadix..:

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


"A被分配给B." 那不是暧昧吗?我认为在普通英语中,这可能意味着"A = B"或"B = A".
A是否可变是无关紧要的.如果您为B分配不同的内容,*A不会更改*.如果一个对象是可变的,你肯定可以改变它.但这与直接分配名称无关.
感谢您的更新,更好!让大多数人感到困惑的是分配订阅; 例如`B [0] = 2`,与直接分配相比,`B = 2`.

4> David Courna..:

它既不是按值传递,也不是按引用传递 - 它是逐个调用的.请见Fredrik Lundh:

http://effbot.org/zone/call-by-object.htm

这是一个重要的引用:

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

在您的示例中,Change调用方法时- 为其创建名称空间 ; 并var成为该命名空间中字符串对象的名称'Original'.然后,该对象在两个名称空间中具有名称.接下来,var = 'Changed'绑定var到一个新的字符串对象,因此该方法的命名空间忘记了'Original'.最后,忘记了该命名空间,并将字符串'Changed'与它一起使用.


不,它与*对象的Java语义完全相同.我不确定你的意思是"在第一种情况下,返回的值将是输入的副本,在第二种情况下是相同的." 但这种说法似乎显然是不正确的.
在Java中传递对象时,它与Java完全相同.但是,Java也有原语,它们通过复制原语的值来传递.因此它们在这种情况下不同.
它与Java完全相同.对象引用按值传递.任何以不同方式思考的人都应该附加一个可以交换两个引用的`swap`函数的Python代码,如:`a = [42]; b ='你好'; swap(a,b)#现在a是'Hello',b是[42]`
我觉得很难买.对我而言就像Java一样,参数是指向内存中对象的指针,这些指针通过堆栈或寄存器传递.
这不像java.它不相同的情况之一是不可变对象.想想lambda x:x的简单函数.将此应用于x = [1,2,3]和x =(1,2,3).在第一种情况下,返回的值将是输入的副本,并且在第二种情况下是相同的.
是否某个东西是一个对象是无关紧要的(特别是因为几乎所有东西都是python中的一个对象,例如int,float等等).可变性至关重要.您可以编写一个适用于列表(可变)的交换函数,但不能编写元组或字符串(不可变).另请参见:http://www.python-course.eu/passing_arguments.php,它在某些情况下清楚地显示了与java不同的行为.
我的评论确实不正确,但我认为python语义与java不同,正如在引用的文章中所解释的那样.在python中,函数内部的变化是否在其外部可见其变量取决于可变性,而不是特殊类型(比如java中的对象与原始类型),并且传递'protocol'的参数对于python中的所有内容都是相同的.

5> Daren Thomas..:

想想通过赋值而不是通过引用/通过值传递的东西.这样,只要您了解正常分配期间发生的情况,就可以清楚地知道发生了什么.

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

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](http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html#other -languages具备变量)

6> Raymond Hett..:

Effbot(又名Fredrik Lundh)将Python的变量传递样式描述为逐个调用对象:http: //effbot.org/zone/call-by-object.htm

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

当您进行诸如的分配时x = 1000,会创建一个字典条目,将当前名称空间中的字符串"x"映射到指向包含一千个整数对象的指针.

使用时更新"x"时x = 2000,将创建一个新的整数对象,并更新字典以指向新对象.旧的一千个对象没有变化(可能存在也可能不存在,具体取决于是否有其他任何东西引用该对象).

当您执行新的分配时y = x,会创建一个新的字典条目"y",该条目指向与"x"条目相同的对象.

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

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

希望能为您澄清问题.


@HonestAbe是的,在CPython中,*id()*返回地址.但是在PyPy和Jython等其他python中,*id()*只是一个唯一的对象标识符.
我非常感谢开发人员对此的了解.正如pepr的答案所暗示的那样,`id()`函数返回指针(对象引用)的值是真的吗?

7> pepr..:

从技术上讲,Python总是使用传递引用值.我将重复我的其他答复以支持我的发言.

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

您可以将参考值视为目标对象的地址.使用时会自动取消引用该地址.这样,使用参考值,您似乎直接使用目标对象.但总会有一个参考,更多的是跳到目标.

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

传递参数的插图示例

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

您可以使用id()内置函数来了解参考值(即目标对象的地址).

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

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


发明新术语(例如"按引用值传递"或"按对象调用"没有帮助)."呼叫(值|参考|名称)"是标准术语."参考"是一个标准术语.按值传递引用使用标准术语准确地描述了Python,Java和许多其他语言的行为.
@cayhorstmann:问题是*Python变量*与其他语言的术语含义不同.这样,*通过引用*调用不适合这里.另外,你如何*确切*定义术语*参考*?非正式地,Python方式可以很容易地描述为传递对象的地址.但它不适合Python的潜在分布式实现.
在该引用的末尾有一个脚注,其中显示:_"实际上,**通过对象引用调用**将是一个更好的描述,因为如果传递了一个可变对象,则调用者将看到被调用者所做的任何更改对它..."_我同意你的看法,认为混淆是因为试图使用与其他语言建立的术语.除了语义之外,需要理解的是:字典/命名空间,[名称绑定操作](http://docs.python.org/3.3/reference/executionmodel.html)和名称→指针→对象的关系(就像你已经知道的那样).

8> Lutz Prechel..:
Python中没有变量

理解参数传递的关键是停止思考"变量".Python中有名称和对象,它们一起看起来像变量,但总是区分三者是有用的.

    Python有名称和对象.

    赋值将名称绑定到对象.

    将参数传递给函数还会将名称(函数的参数名称)绑定到对象.

这就是它的全部.可变性与这个问题无关.

例:

a = 1

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

b = x

这会将名称绑定到b名称x当前绑定的同一对象.之后,名称与名称b无关x.

请参阅Python 3语言参考中的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中说'int`声明一个变量,但是`Integer`却没有.他们都声明了变量.`Integer`变量是一个对象,`int`变量是一个原语.例如,您通过显示"a = 1"来演示变量的工作原理; b = a; a ++#不会修改b`.在Python中也是如此(使用`+ = 1`,因为Python中没有'++`)!

9> AmanicA..:

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

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

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

(是的,我知道这可能很不方便,但有时这很简单.)


为少量文本+1,为Python没有传递引用的问题提供必要的解决方法.(作为一个适合此处以及本页任何地方的后续评论/问题:我不清楚为什么python不能像C#那样提供"ref"关键字,它只是将调用者的参数包含在列表中这个,并将函数中对参数的引用视为列表的第0个元素.)
尼斯.要通过ref传递,请换入[]的.

10> KobeJohn..:

(编辑 - Blair更新了他非常受欢迎的答案,现在它已经准确了)

我认为值得注意的是,目前投票率最高的帖子(布莱尔康拉德)虽然在结果方面是正确的,但却具有误导性,并且基于其定义是不正确的.虽然有许多语言(如C)允许用户通过引用传递或按值传递,但Python不是其中之一.

David Cournapeau的回答指出了真正的答案,并解释了为什么布莱尔康拉德的帖子中的行为似乎是正确的,而定义则不然.

在Python通过值传递的范围内,所有语言都是按值传递的,因为必须发送一些数据(无论是"值"还是"引用").但是,这并不意味着Python在C程序员会想到它的意义上是通过值传递的.

如果你想要这种行为,布莱尔康拉德的回答很好.但是如果你想知道为什么Python既没有通过价值传递也没有通过引用传递,那么请阅读David Cournapeau的回答.


根本不是所有语言都是按值调用的.在C++或Pascal(当然还有许多其他我不知道的)中,您可以通过引用进行调用.例如,在C++中,`void swap(int&x,int&y){int temp = x; x = y; y = temp; }`将交换传递给它的变量.在Pascal中,使用`var`而不是`&`.
我以为我很久以前就回复了这个问题,但是我没有看到它.为了完整 - cayhorstmann误解了我的答案.在大多数人第一次学习C/C++*的术语中,我并没有说一切都是按价值调用*.只是*某些*值被传递(值,名称,指针等)并且Blair原始答案中使用的术语不准确.

11> bobobobo..:

你在这里得到了一些非常好的答案.

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,5]

12> Mike Mazur..:

在这种情况下,var方法中标题的变量Change被赋予一个引用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']
>>> 

我相信其他人可以进一步澄清这一点.



13> ajknzhol..:

Python的pass-by-assignment方案与C++的引用参数选项并不完全相同,但事实证明它与C语言(和其他语言)的参数传递模型非常相似:

不可变参数有效地通过" " 传递.整数和字符串等对象通过对象引用而不是通过复制传递,但是因为无论如何都无法在原地更改不可变对象,效果就像制作副本一样.

可变参数有效地通过" 指针 " 传递.列表和字典之类的对象也通过对象引用传递,这类似于C传递数组的方式作为指针 - 可变对象可以在函数中就地更改,就像C数组一样.



14> Nuno Aniceto..:

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

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

>>> y
Traceback (most recent call last):
  File "", line 1, in 
NameError: name 'y' is not defined
>>> z
Traceback (most recent call last):
  File "", line 1, in 
NameError: name 'z' is not defined

>>> x(2)
>>> x

>>> y
Traceback (most recent call last):
  File "", line 1, in 
NameError: name 'y' is not defined
>>> z
2


我很想发布一个类似的回复 - 原来的提问者可能不知道他想要的实际上是使用全局变量,在函数之间共享.下面是我会分享的链接:http://stackoverflow.com/questions/423379/using-global-variables-in-a-function-other-than-the-one-that-c​​reated-them在回答@Tim ,堆栈溢出不仅是一个问答网站,它的参考知识,只有变得更强,更nuanced-很像更多的输入端的有效wiki-一个巨大的资源库.

15> Joop..:

这里的答案有很多见解,但我认为这里没有明确提到另外一点.引自python文档https://docs.python.org/2/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-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

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



16> matino..:

以下是pass by objectPython中使用的概念的简单(我希望)解释.
无论何时将对象传递给函数,对象本身都会被传递(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)

这显然会改变对象.这个答案解释得很好.


问题是作业没有执行您期望的其他事情。“ list = [1、2、3]”会导致将“ list”名称重用于其他用途,并忘记最初传递的对象。但是,您可以尝试`list [:] = [1,2,3]`(顺便说一句,`list`是变量的错误名称。考虑`[0,1] = [1,2,3]`完全是胡说八道,无论如何,您认为*对象本身已通过*是什么?您认为复制到函数的内容是什么?

17> Dolf Andring..:

除了关于这些东西如何在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



18> itmuckel..:

通过引用传递对象有一个小技巧,即使语言不可能.它也适用于Java,它是带有一个项目的列表.;-)

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

推荐阅读
Life一切安好
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有