1

I would like to have a function as an optional argument of another function in python but it is not clear for me how I can do that.

For example I define the following function:

import os, time, datetime

def f(t=datetime.datetime.now()):
    return t.timetuple()

I have placed t=datetime.datetime.now() in order for the argument to be optional so to be able to call f() with no arguments.

Now whenever in time I execute f() I get the same datetime A (which is wrong according to what I expect), but whenever in time I execute f(datetime.datetime.now()) I get different datetimes (which is correct as expected).

For example

>>> f()
time.struct_time(tm_year=2015, tm_mon=6, tm_mday=20, tm_hour=15, tm_min=36, tm_sec=2, tm_wday=5, tm_yday=171, tm_isdst=-1)
>>> f()
time.struct_time(tm_year=2015, tm_mon=6, tm_mday=20, tm_hour=15, tm_min=36, tm_sec=2, tm_wday=5, tm_yday=171, tm_isdst=-1)
>>> f(datetime.datetime.now())
time.struct_time(tm_year=2015, tm_mon=6, tm_mday=20, tm_hour=15, tm_min=37, tm_sec=1, tm_wday=5, tm_yday=171, tm_isdst=-1)
>>> f()
time.struct_time(tm_year=2015, tm_mon=6, tm_mday=20, tm_hour=15, tm_min=36, tm_sec=2, tm_wday=5, tm_yday=171, tm_isdst=-1)

So why the fourth call returns me back to min 36 and sec 2 while the call was made before that? Why the first two calls give the same exact time even if I let plenty of time between them?

6
  • Get rid of the extra parentheses in the function definition... you're calling the method at definition time. Commented Jun 20, 2015 at 21:03
  • which one is redundant ? Commented Jun 20, 2015 at 21:11
  • Really? There's only one pair you can remove without a syntax error... Commented Jun 20, 2015 at 21:36
  • removing the first and calling f() gives me an Attribute error, removing the second and calling f() returns just the object. In any case by removing the parentheses I do not get the desired result which is an output when calling f(), not to mention getting the correct output. I do not thing the problem is in the parentheses. Commented Jun 20, 2015 at 21:47
  • You need to move the calling parentheses from the definition line into the function body. Commented Jun 20, 2015 at 21:50

4 Answers 4

2

As mentioned by flask, the default value is evaluated when the function is parsed, so it will be set to one time.

The typical solution to this, is to not have the default a mutable value. You can do the followings:

def f(t=None):
    if not t:
        t = datetime.datetime.now()
    return t.timetuple()

BTW, for the readers' benefit, you should try to use meaningful method and variable names.

Sign up to request clarification or add additional context in comments.

4 Comments

... it is dlask no flask. I can't edit that ( 6 char min for edit)
@pebox11 You've accepted this as an answer, but this doesn't answer your question - this is not passing a function as a parameter.
I think an optional argument is a parameter. I am sorry if I confused you.
You should generally test None by identity; if t is None:
1

You can prevent the function from being evaluated by assigning the function when loading it into your second function.

import datetime

def f(t = datetime.datetime.now()):
    return t.timetuple()

def main(ff=f):
    print ff
    print ff()

>>> main()
<function f at 0x10a4e6938>
time.struct_time(tm_year=2015, tm_mon=6, tm_mday=20, tm_hour=15, tm_min=39, tm_sec=28, tm_wday=5, tm_yday=171, tm_isdst=-1)

edit: function is always evaluated in parameter. Solution: decouple it's assignment from the parameter

def f(t="now"):
    if t=="now":
        return datetime.datetime.now().timetuple()
    else:
        return t.timetuple()

def main(ff=f):
    print ff
    print ff()

import time
main()
time.sleep(3)
main()

2 Comments

It does not work. Sequential execution of main() within, say 3 seconds, gives me the same time, where it shouldn't
You are correct, I made an edit which has a solution although it's kind of hacky.
1

Here, the optional parameter is the function for datetime now - no parentheses, as @jonsharpe was recommending. Calling f calls the default function, and calling it twice returns two different times:

>>> import datetime
>>> def f(t=datetime.datetime.now):
...     return t()
... 
>>> f()
datetime.datetime(2015, 6, 21, 0, 6, 10, 698000)

>>> f()
datetime.datetime(2015, 6, 21, 0, 6, 12, 269000)

Or you can pass in another function, here a test one, and override t. Calling f() calls the function passed in:

>>> def test():
...     return "hi"
... 
>>> f(t=test)
'hi'

3 Comments

Say you have stored in variable t the value datetime.datetime.now(). Then f(t) is a TypeError cause datetime.datetime is not callable. So what you provided is not completely correct.
I think my problem is that your title says "function as optional argument" and your question says "I would like to have a function as an optional argument" but you don't actually want that, you want "optional argument evaluated at runtime instead of compile time". Passing a function around as a parameter to another function is a useful thing in itself.
I apologize if I was not precise enough
-1

The default parameter value is evaluated only once when the function is defined.

1 Comment

maybe, but how can I avoid that?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.