简体   繁体   English

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

[英]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', timeout=timeout)

I would assume that as long as the constant really stays constant this should work just fine. 我认为只要常量真正保持不变,就可以正常工作。 Yet I sometimes see a pattern like this: 但是我有时会看到这样的模式:

DEFAULT_TIMEOUT = 10

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

Are these equivalent or should I prefer one over the other? 这些是对等的还是我应优先选择另一种?

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: 您经常看到这样的事情:

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

instead of def foo(x=[]) . 而不是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 = []

def foo(x=None):
    if x is None:
        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. 如果您执行def foo(x=DEFAULT_TIMEOUT) ,则x的默认值是您定义函数时的DEFAULT_TIMEOUT But if you use the None-then-if pattern, the default will be whatever DEFAULT_TIMEOUT is at the time you call the function. 但是,如果使用None-then-if模式,则默认值为调用函数时的DEFAULT_TIMEOUT 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. 这允许用户执行诸如设置DEFAULT_TIMEOUT = 20来更改所有后续调用的默认超时,而不必每次都传递timeout=20 In this case you would want the if check inside the function, to ensure that every call uses the "current" value of DEFAULT_TIMEOUT . 在这种情况下,您需要在函数内部进行if检查,以确保每个调用都使用DEFAULT_TIMEOUT的“当前”值。

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: 如你所提到的,可以发生在模块,其中关键字第二图案self被使用,将其定义为实例属性常数( 你可以阅读更多的关于属性的存在存在 ),例如:

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

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

would produce an error: NameError: name 'self' is not defined 会产生错误: 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)

In another question problem is solved by mgilson in more clever way. 在另一个问题,问题是要解决mgilson更巧妙的方式。 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. 此处的常用习惯是将默认值设置为一些前哨值(没有典型值,尽管有些人为此建议使用省略号 ),然后可以进行检查。

 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 

You might also see an instance of object() used for the sentinel. 您可能还会看到用于哨兵的object()实例。

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

This latter version has the benefit that you can pass None to your function but has a few downsides (see comments by @larsmans below). 后一个版本的好处是您可以将None传递给函数,但有一些缺点(请参见下面@larsmans的评论)。 If you don't forsee the need to pass None as a meaningful argument to your methods, I would advocate using that. 如果您不希望将None作为有意义的参数传递给您的方法,那么我建议您使用它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM