在Python中通过引用传递整数


96

如何在Python中通过引用传递整数?

我想修改传递给函数的变量的值。我读过Python中的所有内容都是按值传递的,但是必须有一个简单的技巧。例如,在Java中,你可以通过引用类型的IntegerLong等等。

  1. 如何通过引用将整数传递给函数?
  2. 最佳做法是什么?

如果将其包裹在一个匿名类(可变的)中,将其包装为一个int类,这是一个很好的方法,但略有复杂性,其行为类似于“引用”:stackoverflow.com/a/1123054/409638 ie ref = type('',() ,{'n':1})
罗伯特2014年

Answers:


106

在Python中,这种方式不太有效。Python将引用传递给对象。在函数内部,您有一个对象-您可以随意更改该对象(如果可能)。但是,整数是不可变的。一种解决方法是在可以更改的容器中传递整数:

def change(x):
    x[0] = 3

x = [1]
change(x)
print x

这充其量是丑陋的/笨拙的,但是您不会在Python中做得更好。原因是因为在Python中,赋值(=)接受右侧对象结果的任何对象,并将其绑定到左侧对象*(或将其传递给适当的函数)。

了解了这一点,我们可以看到为什么无法更改函数内部不可变对象的值的原因-您不能更改其任何属性,因为它是不可变的,并且您不能仅给新的“变量”赋值值,因为您实际上是在创建一个新对象(与旧对象不同),并为其赋予旧对象在本地名称空间中的名称。

通常,解决方法是简单地返回所需的对象:

def multiply_by_2(x):
    return 2*x

x = 1
x = multiply_by_2(x)

*在上述第一个示例中,3实际上传递给x.__setitem__


11
“ Python传递对象。” 否。Python传递引用(指向对象的指针)。
user102008'2013-4-8

6
@ user102008-公平点 我觉得这时的语义变得非常混乱。最终,它们也不是“指针”。至少,肯定与您在的意义上不同C。(例如,不必取消引用它们)。在我的思维模型中,将事物视为“名称”和“对象”会更容易。分配将左侧的“名称”绑定到右侧的“对象”。调用函数时,将传递“对象”(有效地将其绑定到函数中的新本地名称)。
mgilson

当然,这是对python引用的描述,但要找到一种对不熟悉该术语的人进行简洁描述的方法并不容易。
mgilson

2
难怪我们对术语感到困惑。在这里,我们将其描述为按对象调用,在这里将其描述为传递值。在其他地方,它被称为“引用传递”,在其实际含义上带有星号。基本上,问题在于社区尚未弄清该如何称呼
mgilson

1
Python引用与Java引用完全相同。Java引用是根据对象的JLS指针进行的。而且,在Java / Python引用和指向对象的C ++指针之间基本上存在语义上的完全对立,而其他任何东西也都没有描述这种语义。“不必取消引用它们”好吧,.操作员只需取消引用左侧即可。运算符在不同的语言中可以不同。
user102008 2013年

31

您需要通过引用传递的大多数情况是,您需要将多个值返回给调用方。“最佳实践”是使用多个返回值,这在Python中比在Java等语言中要容易得多。

这是一个简单的例子:

def RectToPolar(x, y):
    r = (x ** 2 + y ** 2) ** 0.5
    theta = math.atan2(y, x)
    return r, theta # return 2 things at once

r, theta = RectToPolar(3, 4) # assign 2 things at once

9

不完全直接传递值,而是像传递值一样使用它。

x = 7
def my_method():
    nonlocal x
    x += 1
my_method()
print(x) # 8

注意事项:

  • nonlocal 在python 3中引入
  • 如果封闭范围是全局范围,请使用global代替nonlocal

7

确实,最佳做法是退后一步,然后询问您是否真的需要这样做。为什么要修改传递给函数的变量的值?

如果您需要快速破解,最快的方法是传递一个list整数,并粘贴[0]如mgilson的答案所示,在每次使用时使用。

如果您需要做一些更重要的事情,请写一个 class具有int作为属性的,以便您可以对其进行设置。当然,这会迫使您为类和属性命名,如果您什么都没想到,请返回并再次阅读该句子几次,然后使用list

更一般而言,如果您尝试将某些Java习惯用法直接移植到Python,那么您做错了。即使有直接对应的内容(如static/ @staticmethod),您仍然不想在大多数Python程序中使用它,只是因为您要在Java中使用它。


JAVA不会通过引用传递整数(整数或任何对象。装箱的对象也会在赋值时被替换)。
Luis Masuelli 2014年

@LuisMasuelli好吧,盒装的灵长类动物就像对象一样被对待,唯一阻止其按OP要求使用的事实是盒子是不可变的(而且由于基元本身也是不可变的,所以整个东西是不可变的,只能在可变水平)
Kroltan's

一个很好的用例是计算递归函数中的调用次数(总数,而不是深度)。您需要一个可以在所有分支呼叫中增加的数字。标准int不会削减
z33k '19

4

在Python中,每个值都是引用(指向对象的指针),就像Java中的非基本体一样。另外,像Java一样,Python仅按值传递。因此,从语义上讲,它们几乎是相同的。

既然您在问题中提到Java,那么我想看看您如何实现Java的目标。如果您可以用Java展示它,那么我可以向您展示如何用Python完全等效地展示它。


4

numpy的单元素数组是可变的,但对于大多数用途,它可以被就好像它是一个数值蟒变量进行评价。因此,它比单元素列表更方便使用按引用编号的容器。

    import numpy as np
    def triple_var_by_ref(x):
        x[0]=x[0]*3
    a=np.array([2])
    triple_var_by_ref(a)
    print(a+1)

输出:

3

3
class PassByReference:
    def Change(self, var):
        self.a = var
        print(self.a)
s=PassByReference()
s.Change(5)     



0

在Python中,所有内容均按值传递,但如果要修改某些状态,则可以更改传递给方法的列表或对象内的整数值。


3
我认为,因为everything is passed by value这不是真的。引用文档:arguments are passed using call by value (where the value is always an object reference, not the value of the object)
Astery

1
列表,对象和词典通过引用传递
AN88,2009年

2
如果是这样,则将在调用站点上反映对函数中参数的分配。由于Astery引用的传递是按值传递的,而这些值是对象引用。
递归

0

正确的答案是使用一个类,然后将值放入该类中,这使您可以完全按需传递引用。

class Thing:
  def __init__(self,a):
    self.a = a
def dosomething(ref)
  ref.a += 1

t = Thing(3)
dosomething(t)
print("T is now",t.a)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.