在Python中使用模块常量作为默认函数参数是否可以?

时间:2017-12-30 01:25:21

标签: python function function-parameter

基本上是这样的:

DEFAULT_TIMEOUT = 10
# or even: from my_settings import DEFAULT_TIMEOUT

def get_google(timeout=DEFAULT_TIMEOUT):
    return requests.get('google.com', timeout=timeout)

我认为只要常量保持不变,这应该可以正常工作。然而,我有时会看到这样的模式:

DEFAULT_TIMEOUT = 10

def get_google(timeout=None):
    if timeout is None:
        timeout = DEFAULT_TIMEOUT
    return requests.get('google.com', timeout=timeout)

这些是等同的还是我更喜欢一个?

2 个答案:

答案 0 :(得分:2)

使用“常量”作为默认值没有问题。如你所说,只要“常数”真的不变,就没关系了。唯一的事情是你必须确保在函数之前定义常量,但通常人们将所有常量放在文件的顶部,这不是问题。

当所需的默认值是可变值(例如列表)时,您描述的第二种模式很常见。你经常会看到这样的事情:

def foo(x=None):
    if x is None:
        x = []

而不是def foo(x=[])。你可以找到很多关于这个的问题,但实质上是因为如果你不这样做,可变默认值将持续多次调用函数,这通常是不可取的。

但是,将此模式用于可变模块级常量将无法解决该问题。如果你有:

SOME_CONSTANT = []

def foo(x=None):
    if x is None:
        x = SOME_CONSTANT

。 。 。那么你仍然在多个调用中重复使用相同的可变值。 (当然,将可变值定义为“常量”可能无论如何都不是一个好主意。)这就是为什么我在评论中询问你是否看到有人专门用模块常量做这种事情。

如果模块级默认值实际上不是常量,而是意图由其他代码更改的值,则也将使用此非if-if模式。如果您执行def foo(x=DEFAULT_TIMEOUT),则x的默认值是定义函数时的DEFAULT_TIMEOUT。但是,如果您使用None-then-if模式,则默认值为调用函数时的DEFAULT_TIMEOUT。有些库定义的模块级值不是常量,而是可以在执行过程中更改的配置值。这允许用户执行诸如设置DEFAULT_TIMEOUT = 20之类的操作来更改所有后续调用的默认超时,而不是每次都必须传递timeout=20。在这种情况下,您需要在函数内部进行if检查,以确保每个调用都使用DEFAULT_TIMEOUT的“当前”值。

答案 1 :(得分:0)

更新:我强烈建议您阅读this post about instance and class attributes,其中包括使用这两种属性的最佳做法,以及何时优先使用另一种属性。

正如您所提到的,第二种模式可以出现在使用关键字self的模块中,以将常量定义为实例属性(您可以阅读有关属性there和{{3}的更多信息} )例如:

class Module:
    def __init__(self):
        self.DEFAULT_TIMEOUT = 10

    def get_google(timeout=self.DEFAULT_TIMEOUT):
        return requests.get('google.com', timeout=timeout)

会产生错误:NameError: name 'self' is not defined

class Module:
    def __init__(self):
        self.DEFAULT_TIMEOUT = 10

    def get_google(timeout=None):
        if timeout is None:
            timeout = self.DEFAULT_TIMEOUT
        return requests.get('google.com', timeout=timeout)

另一个there问题由question以更聪明的方式解决。它建议创建mgilson

  

这里常见的习惯用法是将默认值设置为某些哨兵值   (没有什么是典型的,虽然sentinels为此   目的)然后你可以检查。

class Example(object): #inherit from object.  It's just a good idea.  
    def __init__(self, data = None):
        self.data = self.default_data() if data is None else data

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

您可能还会看到用于标记的object()实例。

SENTINEL = object()
class Example(object):
    def __init__(self, data = SENTINEL):
        self.data = self.default_data() if data is SENTINEL else data
     

后一版本的好处是您可以将None传递给您   功能但有一些缺点(见下面@larsmans的评论)。如果   你并不认为有必要将None作为一个有意义的论据   你的方法,我会主张使用它。