Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/361.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何通过引用传递变量?_Python_Reference_Parameter Passing_Pass By Reference - Fatal编程技术网

Python 如何通过引用传递变量?

Python 如何通过引用传递变量?,python,reference,parameter-passing,pass-by-reference,Python,Reference,Parameter Passing,Pass By Reference,Python文档似乎不清楚参数是通过引用还是通过值传递的,下面的代码生成未更改的值“Original” class PassByReference: def __init__(self): self.variable = 'Original' self.change(self.variable) print(self.variable) def change(self, var): var = 'Changed'

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

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

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

我可以通过实际引用传递变量吗?

在这种情况下,方法
Change
中标题为
var
的变量被指定为
self.variable
的引用,您可以立即为
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']
>>> 
我相信其他人可以进一步澄清这一点。

论点是正确的。这背后的理由有两个:

  • 传入的参数实际上是对对象的引用(但引用是按值传递的)
  • 某些数据类型是可变的,但其他数据类型不是可变的
  • 因此:

    • 如果将一个可变对象传递给一个方法,该方法将获得对该对象的引用,您可以随心所欲地对其进行变异,但如果您在该方法中重新绑定该引用,外部范围将对此一无所知,并且在您完成后,外部引用仍将指向原始对象

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

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

    列表-可变类型 让我们尝试修改传递给方法的列表:

    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)
    
    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=['1','2','3']
    得到['1','2','3']
    更改为['1','2','3','4']
    之后,外部列表=['1'、'2'、'3'、'4']
    
    由于传入的参数是对
    outer\u list
    的引用,而不是它的副本,因此我们可以使用mutating list方法对其进行更改,并将更改反映在外部范围中

    现在让我们看看当我们试图更改作为参数传入的引用时会发生什么:

    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)
    
    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','property','English']
    得到['we'、'like'、'property'、'English']
    设置为[‘和’、‘我们’、‘能’、‘不能’、‘撒谎’]
    在后面,outer_list=['we','like','property','English']
    
    由于
    the_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)
    
    输出:

    以前,outer_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])
    

    虽然这看起来有点麻烦。

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

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

    设想通过赋值传递,而不是通过引用/值传递。这样,只要你了解正常作业期间发生的事情,就会清楚发生了什么

    因此,当将列表传递给函数/方法时,该列表被指定给参数名。添加到列表将导致列表被修改。在函数内重新分配列表不会更改原始列表,因为:

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

    由于不可变类型无法修改,它们看起来像是通过值传递的——将int传递给函数意味着将int分配给函数的参数。您只能重新分配它,但它不会更改原始变量值。

    它既不是按值传递也不是按引用传递-它是按对象调用的。见Fredrik Lundh所著:

    这里有一句重要的话:

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

    在您的示例中,当调用
    Change
    方法时,会为其创建一个;而
    var
    成为该名称空间中字符串对象
    'Original'
    的名称。然后,该对象在两个名称空间中有一个名称。接下来,
    var='Changed'
    var
    绑定到一个新的字符串对象,因此该方法的名称空间忘记了
    'Original'
    。最后,这个名称空间被遗忘了,字符串
    也随之“改变”

    (编辑-布莱尔更新了他广受欢迎的答案,因此现在它是准确的)

    我认为重要的是要注意到,目前投票最多的帖子(由布莱尔·康拉德撰写)虽然在结果上是正确的,但它具有误导性,几乎是不正确的
    def __init__(self):
        self.variable = 'Original'
        self.Change(self.variable)
    
    def Change(self, var):
        var = 'Changed'
    
    def __init__(self):         
        self.variable = ['Original']
        self.Change(self.variable)
    
    def Change(self, var):
        var[0] = 'Changed'
    
    def change_me(list):
       list = [1, 2, 3]
    
    my_list = [0, 1]
    change_me(my_list)
    
    [0, 1] = [1, 2, 3]
    
    def change_me(list):
       list.append(2)
    
    [0, 1].append(2)
    
    >>> 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
    
    a = 1
    
    b = x
    
    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
    
    class PassByReference:
        def __init__(self):
            self.variable = 'Original'
            self.Change()
            print self.variable
    
        def Change(self):
            self.variable = 'Changed'
    
    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
    
    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
    
    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
    
    def change(wrapper):
        wrapper(7)
    
    x = 5
    def setter(val):
        global x
        x = val
    print(x)
    
    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
    
    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)
    
    class PassByReferenceIsh:
        def __init__(self):
            self.variable = 'Original'
            self.change('variable')
            print self.variable
    
        def change(self, var):
            self.__dict__[var] = 'Changed'
    
    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'
    
    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)
    
    # 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
    
    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
    
    def somefunction(p):
        a=p+1
        b=p+2
        c=-p
        return a, b, c
    
    x, y, z = somefunction(w)
    
    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
    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)
    
    import builtins
    
    class sstr(str):
        def __str__(self):
            if hasattr(self, 'changed'):
                return self.changed
    
            return self
    
        def change(self, value):
            self.changed = value
    
    builtins.str = sstr
    
    def change_the_value(val):
        val.change('After')
    
    val = str('Before')
    print (val)
    change_the_value(val)
    print (val)
    
    class PassByReference:
        def __init__(self, variable, pass_by_reference=True):
            self._variable_original = 'Original'
            self._variable = variable
            self._pass_by_reference = pass_by_reference # False => pass_by_value
            self.change(self.variable)
            print(self)
    
        def __str__(self):
            print(self.get_variable())
    
        def get_variable(self):
            if pass_by_reference == True:
                return self._variable
            else:
                return self._variable_original
    
        def set_variable(self, something):
            self._variable = something
    
        def change(self, var):
            self.set_variable(var)
    
    def caller_method():
    
        pbr = PassByReference(variable='Changed') # this will print 'Changed'
        variable = pbr.get_variable() # this will assign value 'Changed'
    
        pbr2 = PassByReference(variable='Changed', pass_by_reference=False) # this will print 'Original'
        variable2 = pbr2.get_variable() # this will assign value 'Original'