简体   繁体   中英

How do I go about simulating the equivalent of pass-by-reference in Python?

What I essentially need is a mutable integer which can accomplish the equivalent of the below without having to resort to using single element lists.

Below is a contrived code sample which is representative of the essence of my actual use case [1]

>>> a = [5]  
>>> b = [77]  
>>> def swap(num1, num2):  
...     temp = num1[0]  
...     num1[0] = num2[0]  
...     num2[0] = temp  
>>> swap(a, b)  
>>> a  
[77]  
>>> b  
[5]  

[1] My actual use case is closer to this -> Returning a value after calling a function with a button in Tkinter
I need to return a value from a callback function associated with a Tkinter widget, and I would like to avoid globals.

You'll have to use a class, I'm afraid. Python's a high level language, and doesn't support the pointer shenanigans that are present in C (and C++), so you'll want to use an object that holds the value of an integer which you may then modify.

Based on the comments, your goal is to get back the return status of a function that you don't call directly, and you want to avoid globals. In C you'd have no choice but to use a reference, but in python you have much more flexibility than just locals and (C-style) globals: You can use "closures" to let your callback assign to a local variable directly.

In Python 3

If you use python 3, you can do this straightforwardly with the nonlocal keyword. The simplest case is when you define your callback on the spot:

myfunction():
    x = None
    def callback():
        nonlocal x    # Python 3 only 
        x = 5

    gtk.somefunction(callback)  # Executes callback()
    print "My callback returned", x

But your callback is probably defined elsewhere, or called from lots of different places? No problem, just define on the spot a wrapper that captures the return value of the real callback:

def real_callback():
    return 11

def myfunction():
    x = 0 

    def wrapper():
        nonlocal x     # Python 3 only
        x = real_callback()

    gtk.somefunction(wrapper)
    print "my callback returned", x

This can be obscured by turning wrapper into decorator, but that's a different matter.

Python 2 solution

In python 2 there's no nonlocal statement, and implicit closures are read-only : If you try the above without the nonlocal statement, you get an error. You can assign to a variable if you declare it global , and that's all. So, some trickery is necessary:

First, the function locals() returns a dictionary with all the variables of the local context. locals()['x'] is the local variable x . But locals() is normally read-only. Fortunately there's a nice (or terrible) work-around: For its own reasons, exec disables the optimization that renders locals() read-only... and, it turns out, it stays disabled for the lifetime of the calling function! (Tested on Python 2.6.6, YMMV. If it doesn't work, try exec "a = 0" instead). So, you can do it like this:

def wrapper(callback, context, varname):
    def _callback():
        context[varname] = callback()
    return _callback

def mycode():
    exec ""
    some_library_function(wrapper(real_callback, locals(), 'z'))
    print "The callback returned", z

Is this preferable to just using a mutable container for your return value? That's a matter of taste, I guess. But you can use the same wrapper any number of times, so in a sense it's cleaner than the python 3 solution... if you ignore the magic exec .

In the context of callbacks (see comments on Santiclaus's answer), you may want to pass something like a Queue to the callback function, and have the callback post its results via the queue. Your main thread can then wait on this queue for it's results.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM