Skip to content Skip to sidebar Skip to footer

Is It Ok To Use Module Constants As Default Function Arguments In Python?

Basically something like this: DEFAULT_TIMEOUT = 10 # or even: from my_settings import DEFAULT_TIMEOUT def get_google(timeout=DEFAULT_TIMEOUT): return requests.get('google.com

Solution 1:

There's no problem with using the "constant" as a default value. As you say, as long as the "constant" is really constant, it won't matter. The only thing is that you do have to make sure the constant is defined before the function, but usually people put all their constants at the top of the file so that's not an issue.

The second pattern you describe is common when the desired default is a mutable value, such as a list. You often see things like this:

deffoo(x=None):
    if x isNone:
        x = []

instead of def foo(x=[]). You can find many questions about this, but essentially it is because if you don't do it this way, the mutable default will persist across multiple calls to the function, which usually isn't desirable.

However, using this pattern for a mutable module-level constant wouldn't fix that problem. If you have:

SOME_CONSTANT = []

deffoo(x=None):
    if x isNone:
        x = SOME_CONSTANT

. . . then you're still reusing the same mutable value across multiple calls. (Of course, defining a mutable value as a "constant" is probably not a good idea anyway.) That's why I was asking in the comments if you've seen someone specifically doing this kind of thing with module constants.

This None-then-if pattern would also be used if the module-level default were actually not a constant, but a value intended to be changed by other code. If you do def foo(x=DEFAULT_TIMEOUT), the default value of x is whatever DEFAULT_TIMEOUT was at the time you defined the function. But if you use the None-then-if pattern, the default will be whatever DEFAULT_TIMEOUT is at the time you call the function. Some libraries define module-level values that aren't meant to be constant, but are rather configuration values that may be changed in the course of execution. This allows users to do things like set DEFAULT_TIMEOUT = 20 to change the default timeout for all subsequent calls, rather than having to pass timeout=20 every time. In this case you would want the if check inside the function, to ensure that every call uses the "current" value of DEFAULT_TIMEOUT.

Solution 2:

UPDATE: I highly recommend reading this post about instance and class attributes, it includes best practices of using both types of attributes, and when one is preferred over the other.

As you mentioned, second pattern can occur in modules, where keyword self is used, to define constant as instance attribute (you can read more about attributes there and there) for instance:

classModule:
    def__init__(self):
        self.DEFAULT_TIMEOUT = 10defget_google(timeout=self.DEFAULT_TIMEOUT):
        return requests.get('google.com', timeout=timeout)

would produce an error: NameError: name 'self' is not defined

classModule:
    def__init__(self):
        self.DEFAULT_TIMEOUT = 10defget_google(timeout=None):
        if timeout isNone:
            timeout = self.DEFAULT_TIMEOUT
        return requests.get('google.com', timeout=timeout)

In another question problem is solved by mgilson in more clever way. It suggests creating sentinels:

The common idiom here is to set the default to some sentinel value (None is typical, although some have suggested Ellipsis for this purpose) which you can then check.

classExample(object): #inherit from object.  It's just a good idea.  def__init__(self, data = None):
        self.data = self.default_data() if data isNoneelse data

    defdefault_data(self):  #probably need `self` here, unless this is a @staticmethod ...# ....return something

You might also see an instance of object() used for the sentinel.

SENTINEL = object()
classExample(object):
    def __init__(self, data = SENTINEL):
        self.data = self.default_data() ifdatais SENTINEL elsedata

This latter version has the benefit that you can pass None to your function but has a few downsides (see comments by @larsmans below). If you don't forsee the need to pass None as a meaningful argument to your methods, I would advocate using that.

Post a Comment for "Is It Ok To Use Module Constants As Default Function Arguments In Python?"