Python - Store function in variable

user3580316 picture user3580316 · Mar 11, 2016 · Viewed 25.6k times · Source

I have a concept where I store function in variables which makes it easier for me. But I am having the problem that the value in the variable isn't dynamically calling the function each time. So it returns the same value all the time. Just to make clear of the problem, I have made a code snip to illustrate it in a easy way:

def value():
        resp = requests.get('http://www.google.com').elapsed.total_seconds()
        return resp

test = value()

while True:
        print test
        time.sleep(10)

Output:

0.00649
0.00649

In this case, in the while true when I print test, it returns the same value, even though I am calling the function value(). How can I solve this issue? I know I can put the function in the while loop, but I want to have it as a variable.

Answer

André Laszlo picture André Laszlo · Mar 11, 2016

The previous answers are correct, but please let me elaborate.

In the world of Python, things have very precise meanings but it's not always clear what is what if you are just getting started.

Expressions are things that have a value, and they include things like 123, some_variable and 10 / 2. The name some_variable is called an identifier, simply because it identifies a value. Just like the name Scruffy might identify your dog.

Statements are things that affect the program flow or the state of your program, but lack a value. The following are examples of statements:

if x > 10:
    x -= 1

And

def foo(x):
    print("The value of x is", x)

Unlike in JavaScript, for example, where almost everything is an expression (and has a value), you can not do this:

my_function = def foo(x): print("The value of x is", x)

The previous def statement will create a function by the name foo, but the statement itself doesn't have a value. The name foo will refer to a value, however. It means that when you write foo, the value will be a thing, and this thing is a function!

The expression x() on the other hand will do two things. First it will look up the value of the identifier x. Hopefully this value is a function. After that, the parentheses means that the value will be called. What happens if x is not a function, then?

>>> x = 10
>>> x()
Traceback (most recent call last):
  File "<ipython-input-3-7354d77c61ac>", line 1, in <module>
    x()
TypeError: 'int' object is not callable

Big Fat Error: 10 is not a "callable". Callable means something that can be called like a function.

The parentheses are, as you probably know, a list of arguments. In this case the list is simply empty so when you write x() you are saying "call whatever the name 'x' is referring to but don't send any arguments".

A function call always has a value, since it's an expression. If you don't explicitly return anything, the value will simply be None.

To answer your question, finally, let's play the substitution game. The rules are simple, any expression can be replaced by its value:

def value():
    resp = requests.get('http://www.google.com').elapsed.total_seconds()
    return resp

This is a statement, so it doesn't have a value. A function is created with the name value, however.

The function consists of two statements (no value, again), a variable assignment and a return statement.

The thing to the right of the = is an expression however. Short story:

  • requests is referring to the requests module
  • get is referring to a module-global function in the above module
  • get('...') is calling this function, and something is returned.
  • The "something" has a property called elapsed, which has a property called total_seconds.
  • total_seconds is an identifier that refers to a callable. The callable is called without any arguments (total_seconds()) and something is returned. Probably a number, based on the name. Let's say its value is always 10, for simplicity.

The next statement is another assignment:

test = value()

This can be thought of as "let the name 'test' refer to the value that is returned by the callable identified by the name 'value' when it is called with an empty argument list". In our case, the function object called value will be called, resp will be assigned the value 10, then the return statement will let the caller know that this call is sending the value 10 back. The name test will refer to the value 10, from now on.

Let's go over the loop, quickly:

while True:
    print test
    time.sleep(10)

Do the following until the end of Time:

  • print (a statement in Python 2, an expression in Python 3!) has the side-effect of printing stuff to the screen. Otherwise it doesn't do much.
  • The stuff, in this case, is whatever the value of the expression test is. We already know that the identifier test is referring to the value 10. It will simply print "10" to the screen.
  • Sleep for ten seconds.
  • Repeat.

You probably want to invoke some function at each iteration of the loop ("invoke" is basically latin for "call", I like fancy words). Otherwise the program will just print "10", "10", "10", over and over again. To fix this this, you first have to change the expression evaluated as part of the print statement from just an identifier (test) to a function call:

print test()

But this will, as we saw before, raise a Big Fat Error since 10 is not a callable function. To fix it (that's what programmers do, right?) you also need to change the value of test from 10, since it's not a callable, to the function. A function can be referred to simply by its name, so just change this line:

test = value()  # equals ten

To this:

test = value  # equals the function called "value"

The function now has two names, the old name "value", and the new name "test". Each step in the loop will request the page again and return the new time it took for the request to complete. If the request times out you will have a different kind of crash, but that's another story.

Further information can be found in the Python Language Reference.