1

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.

Community
  • 1
  • 1
  • That looks a lot like C. – Linuxios Dec 08 '12 at 20:01
  • 1
    Since there's no straightforward way to work with mutable `int` references, can you explain why you wanted them? (Obviously it's not so you can swap two numbers!) Python probably has a good way to do it without mutable references. – alexis Dec 08 '12 at 20:03
  • @alexis - I need to return a value from a callback function associated with a Tkinter widget. The actual use case is closer to this - http://stackoverflow.com/questions/5374354/returning-a-value-after-calling-a-function-with-a-button-in-tkinter I would like to avoid globals. – sibinpthomas Dec 08 '12 at 20:33
  • @qarma - You do think that you are pretty smart don't you? Good for you. After you are done patting your back, you should read the text that comes after the code block (with that horrible temp var). – sibinpthomas Dec 12 '12 at 03:36
  • @qarma - Even though immaturely put, it is valid criticism nevertheless. I have edited my question to make sure that people with shorter attention spans do read till the end of the post. Hopefully this makes it better. – sibinpthomas Dec 12 '12 at 03:52
  • btw., pythonic for `tmp = a; a = b; b = tmp` is simply `a, b = b, a` – Dima Tisnek Dec 12 '12 at 12:21
  • @qarma - Dude, how are you not getting this? This question isn't about swapping, don't get distracted by it. The swapping example was used only as a prop to explain my question; which in hindsight seems like a really bad decision. Also, someone already suggested this in this thread. – sibinpthomas Dec 13 '12 at 03:55
  • that's why I posted a comment, not an answer – Dima Tisnek Dec 13 '12 at 11:28

3 Answers3

5

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.

Santiclause
  • 870
  • 1
  • 7
  • 12
  • 5
    class BoxedInteger()..., which will be very annoying everywhere *else*. The pythonic way to swap is just ``a, b = b, a`` – Max Dec 08 '12 at 20:00
  • Thanks @Max, I got that. However, my actual use case is closer to this - http://stackoverflow.com/questions/5374354/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. – sibinpthomas Dec 08 '12 at 20:40
  • @Santiclause - Why diss pointers from C/C++ while trying to make a point about Python? Deep down is Python any different? Doesn't Guido employ the same shenanigans to gift many a programmer a warped, but pleasanter, view of the world devoid of pointer shenanigans? – sibinpthomas Dec 08 '12 at 20:50
  • @Santiclause What does the word "shenanigan" mean , please ? I don't find it in (http://www.thefreedictionary.com/) – eyquem Dec 08 '12 at 21:57
  • You may want to consider using something like a ``Queue`` to pass things from your callbacks to your main thread of control, then. – Max Dec 08 '12 at 22:38
  • @sibinpthomas I'm not dissing pointers at all! I thoroughly love C and pointer shenanigans. Shenanigans isn't a bad thing. It's just not present in Python =P – Santiclause Dec 08 '12 at 23:51
  • @Santiclause - I will take your word for it :) – sibinpthomas Dec 09 '12 at 03:43
2

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.

Community
  • 1
  • 1
alexis
  • 48,685
  • 16
  • 101
  • 161
  • Is there any work around better than embedding integers in mutable types like lists or dicts for Python 2.7? – sibinpthomas Dec 09 '12 at 16:05
  • 1
    Yes, there is. It was fun to figure out but tbh I'm not sure it's worth the trouble... see the updated answer. – alexis Dec 09 '12 at 18:20
  • I think I will go with the mutable container option; the latest update actually scares me a little. My problem with using globals or this method of passing your entire locals dictionary to the callback is that it violates the principle of data-hiding. Ideally a method should have access to only that data that it reads or modifies and nothing more than that. And ideally data should be accessible only to those methods that read or modify it and none other. – sibinpthomas Dec 11 '12 at 04:34
  • True, passing all of `locals()` looks messy. Though if you control the `wrapper` and it does nothing else, I don't see the harm in it; consider it a language extension. Anyway, the container method is certainly more straightforward. – alexis Dec 11 '12 at 17:02
  • PS. You should "accept" the answer that best helped you resolve your question, by clicking on the check mark. – alexis Dec 11 '12 at 17:03
0

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.

Max
  • 10,701
  • 2
  • 24
  • 48