26

I have this example code, trying to demonstrate using a callback function:

def callback(a, b):
    print('Sum = {0}'.format(a+b))

def main(callback=None):
    print('Add any two digits.')
    if callback != None:
        callback

main(callback(1, 2))

I get this result:

Sum = 3
Add any two digits.

It seems that the callback function executes before the logic in main. Why? How can I make it so that the callback is not called until it is used within main?

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
srgbnd
  • 5,404
  • 9
  • 44
  • 80
  • 1
    [Comparisons to singletons like None should always be done with is or is not, never the equality operators.](https://www.python.org/dev/peps/pep-0008/#id51) So, in your case `if callback is not None` would be the right choice. – pfabri Feb 15 '21 at 16:19

7 Answers7

62

In this code

if callback != None:
    callback

callback on its own doesn't do anything; it accepts parameters - def callback(a, b):

The fact that you did callback(1, 2) first will call that function, thereby printing Sum = 3, and then main() gets called with the result of the callback function, which is printing the second line

Since callback returns no explicit value, it is returned as None.

Thus, your code is equivalent to

callback(1, 2)
main()

Solution

You could try not calling the function at first and just passing its handle.

def callback(n):
    print("Sum = {}".format(n))

def main(a, b, _callback = None):
    print("adding {} + {}".format(a, b))
    if _callback:
        _callback(a+b)

main(1, 2, callback)
OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
  • 1
    Good idea to pass the two args before the call back to make the interpreter do some work for you! – erip Nov 28 '16 at 12:25
  • 1
    @erip How do you mean? Like your answer? – OneCricketeer Nov 28 '16 at 16:19
  • 1
    I meant yours is a good idea. :) Forcing the user to pass the arguments before the callback ensures that they exist lest there's a error calling `main`. – erip Nov 28 '16 at 16:29
  • @cricket_007, is it right that callback method is simply passing callable to a function or a class with a purpose to call it later? – garej Oct 14 '18 at 11:12
  • what is meaning of if _callback: ? _callback would return true when we pass function like main(1, 2, callback) but I dont understand how the if statement evaluates to be true? – nad87563 Jul 23 '21 at 16:27
  • 1
    @nad87563 If the function handle has been defined (`is not None`) – OneCricketeer Jul 23 '21 at 16:29
10

Here's what you wanted to do :

def callback(a, b):
    print('Sum = {0}'.format(a+b))

def main(a,b,f=None):
    print('Add any two digits.')
    if f is not None:
        f(a,b)

main(1, 2, callback)
Community
  • 1
  • 1
Eric Duminil
  • 52,989
  • 9
  • 71
  • 124
5

The problem is that you're evaluating the callback before you pass it as a callable. One flexible way to solve the problem would be this:

def callback1(a, b):
    print('Sum = {0}'.format(a+b))

def callback2(a):
    print('Square = {0}'.format(a**2))

def callback3():
    print('Hello, world!')

def main(callback=None, cargs=()):
    print('Calling callback.')
    if callback is not None:
        callback(*cargs)

main(callback1, cargs=(1, 2))
main(callback2, cargs=(2,))
main(callback3)

Optionally you may want to include a way to support keyword arguments.

OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
farsil
  • 955
  • 6
  • 19
4

As mentioned in the comments, your callback is called whenever it's suffixed with open and close parens; thus it's called when you pass it.

You might want to use a lambda and pass in the values.

#!/usr/bin/env python3

def main(callback=None, x=None, y=None):
    print('Add any two digits.')
    if callback != None and x != None and y != None:
        print("Result of callback is {0}".format(callback(x,y)))
    else:
        print("Missing values...")

if __name__ == "__main__":
    main(lambda x, y: x+y, 1, 2)
erip
  • 16,374
  • 11
  • 66
  • 121
2

Your code is executed as follows:

main(callback(1, 2))

callback function is called with (1, 2) and it returns None (Without return statement, your function prints Sum = 3 and returns None)

main function is called with None as argument (So callback != None will always be False)

ettanany
  • 19,038
  • 9
  • 47
  • 63
1

This is an old post, but perhaps the following may be additional clarification on writing and using a callback function, especially if you wonder where it gets its arguments from and whether you can access its return values (if there is no way to get it from the function that takes the callback function).

The following code defines a class CallBack that has two callback methods (functions) my_callback_sum and my_callback_multiply. The callback methods are fed into the method foo.

# understanding callback

class CallBack:

    @classmethod
    def my_callback_sum(cls, c_value1, c_value2):
        value = c_value1 + c_value2
        print(f'in my_callback_sum --> {c_value1} + {c_value2} = {value}')
        cls.operator = '+'
        return cls.operator, value

    @classmethod
    def my_callback_multiply(cls, c_value1, c_value2):
        value = c_value1 * c_value2
        print(f'in my_callback_multiply --> {c_value1} * {c_value2} = {value}')
        cls.operator = '*'
        return cls.operator, value

    @staticmethod
    def foo(foo_value, callback):
        _, value = callback(10, foo_value)
        # note foo only returns the value not the operator from callback!
        return value


if __name__ == '__main__':
    cb = CallBack()

    value = cb.foo(20, cb.my_callback_sum)
    print(f'in main --> {value} and the operator is {cb.operator}')

    value = cb.foo(20, cb.my_callback_multiply)
    print(f'in main --> {value} and the operator is {cb.operator}')

result:

in my_callback_sum --> 10 + 20 = 30
in main --> 30 and the operator is +
in my_callback_multiply --> 10 * 20 = 200 
in main --> 200 and the operator is *

As you can see one value for the callback function c_value2 it gets from argument foo_value in foo and given in main the value 20, while c_value1 it gets internally from foo in this case the value 10 (and may be not clearly visible if foo is some method of a third party imported module, like pyaudio).

The return value of the callback function functions can be retrieved by adding it to the namespace of the class CallBack, in this case cls.operator

Bruno Vermeulen
  • 2,970
  • 2
  • 15
  • 29
  • This does not appear to be a callback function as it does not "callback" a function defined outside the class. To me, this is a function by reference... Great technique for abstraction, but not technically a callback, IMO. – Scott Lundberg Nov 01 '20 at 16:34
0

You can use anonymous functions

def callback(a, b):
    print('Sum = {0}'.format(a+b))

def main(callback=None):
    print('Add any two digits.')
    if callback is not None:
        callback()

tmp_func = lambda: main(lambda: callback(2,3))
tmp_func()

#OR

tmp_func = lambda x,y: main(lambda: callback(x,y))
tmp_func(2,4)
OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
Mican
  • 73
  • 5