Python函数通过引用调用

Tim*_*man 62 python

在某些语言中,您可以使用特殊的保留字(如refval)通过引用或值传递参数.当您将参数传递给Python函数时,它永远不会在离开函数时改变参数的值.唯一的方法是使用全局保留字(或者我当前理解它).

例1:

k = 2

def foo (n):
     n = n * n     #clarity regarding comment below
     square = n
     return square

j = foo(k)
print j
print k
Run Code Online (Sandbox Code Playgroud)

会表现出来

>>4
>>2
Run Code Online (Sandbox Code Playgroud)

显示k是不变的

在此示例中,变量n永远不会更改

例2:

n = 0
def foo():
    global n
    n = n * n
    return n
Run Code Online (Sandbox Code Playgroud)

在此示例中,变量n已更改

在Python中是否有任何方法可以调用函数并告诉Python 参数还是引用参数而不是使用全局?

其次,在A级剑桥考试,他们现在说一个函数返回一个单一而过程返回值超过一个值.在80年代,我被教导了一个函数有一个返回语句,而程序没有.为什么现在不正确?

Sho*_*rma 134

基本上有三种'函数调用':

  • 通过价值
  • 通过引用传递
  • 通过对象引用传递

Python是一种PASS-BY-OBJECT-REFERENCE编程语言.

首先,重要的是要理解一个变量,变量(对象)的值是两个独立的东西.变量'指向'对象.变量不是对象.再次:

变量不是对象

示例:在以下代码行中:

>>> x = []
Run Code Online (Sandbox Code Playgroud)

[]是空列表,x是指向空列表的变量,但x本身不是空列表

将变量(x,在上述情况下)视为一个框,将变量([])的"值"视为框内的对象.

通过对象引用(python中的情况):

这里,"对象引用按值传递".

def append_one(li):
    li.append(1)
x = [0]
append_one(x)
print x
Run Code Online (Sandbox Code Playgroud)

这里,语句x = [0]产生一个指向对象[0]的变量x(box)

在被调用的函数上,创建了一个新的li.li的内容是SAME作为box x的内容.两个框都包含相同的对象.也就是说,两个变量都指向内存中的同一个对象.因此,li指向的对象的任何更改也将由x指向的对象反映.

总之,上述计划的产出将是:

[0,1]

注意:

如果在函数中重新分配变量li,则li将指向内存中的单独对象.但是,x将继续指向它之前指向的内存中的同一个对象.

例:

def append_one(li):
    li = [0, 1]
x = [0]
append_one(x)
print x
Run Code Online (Sandbox Code Playgroud)

该计划的输出将是:

[0]

通过参考:

调用函数的框传递给被调用函数.隐含地,框的内容(变量的值)被传递给被调用的函数.因此,对被调用函数中的框内容的任何更改都将反映在调用函数中.

通过价值:

在被调用函数中创建一个新框,并将来自调用函数的框内容的副本存储到新框中.

希望这可以帮助.

  • 你能举出“通过引用传递”和“通过值传递”的例子,就像你对“通过对象引用传递”所做的那样。 (4认同)
  • 我仍然不明白这与传递引用有何不同。这个答案迫切需要一个示例,说明相同的伪代码如何为所有 3 种范式产生不同的输出。链接到一个不同的答案,该答案仅解释了引用传递和值传递之间的区别并无济于事。 (4认同)

dav*_*off 62

你不能通过Python中的引用传递一个简单的原语,但是你可以做以下事情:

def foo(y):
  y[0] = y[0]**2

x = [5]
foo(x)
print x[0]  # prints 25
Run Code Online (Sandbox Code Playgroud)

然而,这是一种奇怪的方式.请注意,在Python中,您还可以返回多个值,使得一些用于传递引用的用例不那么重要:

def foo(x, y):
   return x**2, y**2

a = 1
b = 2
a, b = foo(a, b)  # a == 2; b == 4
Run Code Online (Sandbox Code Playgroud)

当您返回这样的值时,它们将作为元组返回,而元组又被解压缩.

编辑: 另一种思考方式是,虽然您无法通过Python中的引用显式传递变量,但您可以修改传入的对象的属性.在我的示例(和其他)中,您可以修改列表的成员但是,你不能完全重新分配传入的变量.例如,看到以下两段代码看起来可能会做类似的事情,但结果会有不同的结果:

def clear_a(x):
  x = []

def clear_b(x):
  while x: x.pop()

z = [1,2,3]
clear_a(z) # z will not be changed
clear_b(z) # z will be emptied
Run Code Online (Sandbox Code Playgroud)

  • @alexey`clear_a`被赋予对`z`的引用并将该引用存储在`x`中.然后立即更改`x`以引用不同的数组.原始的`z`数组在`clear_a`的范围内被遗忘,但实际上并没有改变.它在返回全局范围时继续保持不变.与`clear_b`对比,它引用了'z`然后直接对它进行操作,而没有创建新的数组或以其他方式指向`x`和其他东西. (6认同)
  • 查看程序的结果会很有帮助。例如,'print x[0]' 是否返回 25?(是的,确实如此,但应该显示出来。) (2认同)

Kir*_*ser 24

好的,我会捅这个.Python通过对象引用传递,这与您通常认为的"通过引用"或"按值"不同.举个例子:

def foo(x):
    print x

bar = 'some value'
foo(bar)
Run Code Online (Sandbox Code Playgroud)

因此,您要创建一个值为'some value'的字符串对象,并将其"绑定"到名为的变量bar.在C中,这类似于bar指向"某个值"的指针.

当你打电话时foo(bar),你不是在bar自我传递.你传递bar的是价值:指向"某种价值"的指针.此时,对同一个字符串对象有两个"指针".

现在比较一下:

def foo(x):
    x = 'another value'
    print x

bar = 'some value'
foo(bar)
Run Code Online (Sandbox Code Playgroud)

这就是差异所在.在线:

x = 'another value'
Run Code Online (Sandbox Code Playgroud)

你实际上并没有改变它的内容x.事实上,这甚至都不可能.相反,您正在创建一个值为"另一个值"的新字符串对象.那个赋值算子?它并没有说" x用新的价值覆盖东西".它说"更新x指向新对象".在该行之后,有两个字符串对象:"某个值"(bar指向它)和"另一个值"(x指向它).

这并不笨拙.当您了解它的工作原理时,它就是一个优雅,高效的系统.

  • 但是,如果我*想要*函数来改变我变量的内容呢?我认为没有办法做到这一点? (2认同)
  • 实际上我要问的是,是否有一种简单的方法可以让python不创建指向同一对象的新指针,而是使用原始指针.我认为没有办法做到这一点.我从其他答案中得出结论,正常的解决方法是实现通过引用传递的方法只是返回更多参数.如果是这种情况,我认为a,b,c,d,e = foo(a,b,c,d,e)比foo(a,b,c,更加笨重)的论证有一定的有效性. d,e)所示. (2认同)
  • @aquirdturtle 这与创建新指针无关。您必须返回一个新对象,因为“字符串”、“元组”和其他对象是**不可变的**。如果您传递一个可变对象,例如“列表”,您可以在函数内更改它,而无需返回它,因为所有函数参数都是指向您传递的相同对象的指针。 (2认同)

att*_*182 21

希望以下描述总结得很好:

这里有两件事要考虑 - 变量和对象.

  1. 如果要传递变量,则它会传递值,这意味着函数中对变量所做的更改对于该函数是本地的,因此不会全局反映.这更像是一种'C'行为.

例:

def changeval( myvar ):
   myvar = 20; 
   print "values inside the function: ", myvar
   return

myvar = 10;
changeval( myvar );
print "values outside the function: ", myvar
Run Code Online (Sandbox Code Playgroud)

O/P:

values inside the function:  20 
values outside the function:  10
Run Code Online (Sandbox Code Playgroud)
  1. 如果要传递包含在可变对象(如列表)中的变量,则只要未重新分配对象,对对象所做的更改将全局反映.

例:

def changelist( mylist ):
   mylist2=['a'];
   mylist.append(mylist2);
   print "values inside the function: ", mylist
   return

mylist = [1,2,3];
changelist( mylist );
print "values outside the function: ", mylist
Run Code Online (Sandbox Code Playgroud)

O/P:

values inside the function:  [1, 2, 3, ['a']]
values outside the function:  [1, 2, 3, ['a']]
Run Code Online (Sandbox Code Playgroud)
  1. 现在考虑重新分配对象的情况.在这种情况下,对象指的是新的存储位置,该存储位置是发生这种情况的函数的本地存储位置,因此不会全局反映.

例:

def changelist( mylist ):
   mylist=['a'];
   print "values inside the function: ", mylist
   return

mylist = [1,2,3];
changelist( mylist );
print "values outside the function: ", mylist
Run Code Online (Sandbox Code Playgroud)

O/P:

values inside the function:  ['a']
values outside the function:  [1, 2, 3]
Run Code Online (Sandbox Code Playgroud)

  • 这个答案非常混乱。Python 不会以不同的方式传递不同类型的值:Python 中的所有函数参数都通过赋值接收它们的值,并且其行为与语言中其他任何地方的赋值行为完全相同。(“尽管这种方式起初可能并不明显,除非你是荷兰人。”) (4认同)
  • 这是最好的总结!^^^最佳摘要在这里^^^大家都读了这个答案!1.传递简单变量2.传递被修改的对象引用3.传递在函数中重新分配的对象引用 (2认同)

Yas*_*jab 9

从技术上讲,python 不按值传递参数:全部按引用传递。但是......由于python有两种类型的对象:不可变和可变,下面是发生的事情:

  • 不可变参数通过值有效传递:字符串、整数、元组都是不可变对象类型。虽然它们在技术上是“通过引用传递”(就像所有参数一样),但由于您无法在函数内部就地更改它们,它看起来/行为就像通过值传递一样。

  • 可变参数通过引用有效传递:列表或字典通过其指针传递。函数内部的任何就地更改(如(append 或 del))都会影响原始对象。

这就是 Python 的设计方式:没有副本,全部通过引用传递。您可以显式传递副本。

def sort(array):
    # do sort
    return array

data = [1, 2, 3]
sort(data[:]) # here you passed a copy
Run Code Online (Sandbox Code Playgroud)

最后一点我想提一下哪个函数有它自己的作用域。

def do_any_stuff_to_these_objects(a, b): 
    a = a * 2 
    del b['last_name']

number = 1 # immutable
hashmap = {'first_name' : 'john', 'last_name': 'legend'} # mutable
do_any_stuff_to_these_objects(number, hashmap) 
print(number) # 1 , oh  it should be 2 ! no a is changed inisde the function scope
print(hashmap) # {'first_name': 'john'}
Run Code Online (Sandbox Code Playgroud)


Phi*_*son 8

Python既不是按值传递也不是传递引用.它更像是"对象引用按值传递",如下所述:http://robertheaton.com/2014/02/09/pythons-pass-by-object-reference-as-explained-by-philip-k-dick /.

  1. 这就是为什么它不是价值传递的原因. 因为

    def append(list):
    list.append(1)

    list = [0]
    reassign(list)
    append(list)

返回[0,1]表明某种引用已明确传递,因为pass-by-value不允许函数根本改变父作用域.

胡先生,看起来像传递参考?不.

  1. 这就是为什么它不是传递参考.因为

    def reassign(list):
    list = [0,1]

    list = [0]
    重新分配(列表)
    打印列表

返回[0],表示在重新分配列表时销毁了原始引用. pass-by-reference将返回[0,1].

有关更多信息,请访问:http://effbot.org/zone/call-by-object.htm

如果希望函数不操作外部作用域,则需要复制创建新对象的输入参数.

def append(list):
    list.append(1)

list = [0]
reassign(list)
append(list)
Run Code Online (Sandbox Code Playgroud)