Reputation: 2883
I have something akin to the following code:
class thingy(object):
def __init__(creds=get_credentials()):
# does stuff
pass
def get_credentials():
# produces different values sometimes. Ie: retrieves values from a db
return db.get(some_query)
dc.connect(credentials_and_stuff)
This fails on import, not runtime, defining the __init__()
, saying 'the database has no connection'. This doesn't make sense to me, since the __init__()
function hasn't even been run yet!
Upvotes: 1
Views: 47
Reputation: 2883
I found this: http://docs.python-guide.org/en/latest/writing/gotchas/#mutable-default-arguments
Turns out, values for optional arguments are evaluated at their definition, not when executed. Here's an extreme example:
def default_value():
print("ERMAGERD DIS FUNCTION WAS RUN!")
return False
def fake_function(an_arg=default_value()):
pass
Before this discovery, I'd say this code would do nothing; no functions are run, just some definitions. Now I know (and have tested) that this code alone will print out ERMAGERD DIS FUNCTION WAS RUN!
.
This means two things for the code in the above question:
db.connect()
would have to be run before the class thingy(object)
definition happens.get_credentials()
would only get run once, and said db query would never get re-run (stale credentials!)So to make this actually do what it's intended to do (query the database for credentials when none are provided), you have to do something akin to this:
class thingy(object):
def __init__(creds=None):
if creds is None:
creds = get_credentials()
# does stuff
pass
This way get_credentials()
is run at runtime, not at definition. This both allows the db.connect()
to be done elsewhere (so long as it's before an instance of thingy
is instantiated), and fixes the stale credentials issue.
Upvotes: 4