Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/360.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何检查是否设置了可选功能参数_Python_Function_Optional Parameters - Fatal编程技术网

Python 如何检查是否设置了可选功能参数

Python 如何检查是否设置了可选功能参数,python,function,optional-parameters,Python,Function,Optional Parameters,Python中是否有一种简单的方法可以检查可选参数的值是否来自其默认值,或者是因为用户在函数调用时显式地设置了它?不太可能。标准方法是使用预期用户不会通过的默认值,例如对象实例: DEFAULT = object() def foo(param=DEFAULT): if param is DEFAULT: ... 通常您可以使用None作为默认值,如果它作为用户希望传递的值没有意义的话 另一种方法是使用kwargs: def foo(**kwargs): if

Python中是否有一种简单的方法可以检查可选参数的值是否来自其默认值,或者是因为用户在函数调用时显式地设置了它?

不太可能。标准方法是使用预期用户不会通过的默认值,例如
对象
实例:

DEFAULT = object()
def foo(param=DEFAULT):
    if param is DEFAULT:
        ...
通常您可以使用
None
作为默认值,如果它作为用户希望传递的值没有意义的话

另一种方法是使用
kwargs

def foo(**kwargs):
    if 'param' in kwargs:
        param = kwargs['param']
    else:
        ...

但是,这过于冗长,使您的函数更难使用,因为它的文档不会自动包含
param
参数。

我同意volatile的评论。但您可以通过以下方式进行检查:

def function(arg1,...,**optional):
    if 'optional_arg' in optional:
        # user has set 'optional_arg'
    else:
        # user has not set 'optional_arg'
        optional['optional_arg'] = optional_arg_default_value # set default

有点奇怪的做法是:

class CheckerFunction(object):
    def __init__(self, function, **defaults):
        self.function = function
        self.defaults = defaults

    def __call__(self, **kwargs):
        for key in self.defaults:
            if(key in kwargs):
                if(kwargs[key] == self.defaults[key]):
                    print 'passed default'
                else:
                    print 'passed different'
            else:
                print 'not passed'
                kwargs[key] = self.defaults[key]

        return self.function(**kwargs)

def f(a):
    print a

check_f = CheckerFunction(f, a='z')
check_f(a='z')
check_f(a='b')
check_f()
哪些产出:

passed default
z
passed different
b
not passed
z

正如我提到的,这是非常奇怪的,但它确实起到了作用。但是,这是非常不可读的,与的类似,不会自动记录。

下面的函数装饰器,
explicit\u checker
,为所有显式给定的参数创建一组参数名。它将结果作为额外参数(
explicit_params
)添加到函数中。只需在explicit_params中执行
'a',检查参数
a
是否显式给出

def explicit_checker(f):
    varnames = f.func_code.co_varnames
    def wrapper(*a, **kw):
        kw['explicit_params'] = set(list(varnames[:len(a)]) + kw.keys())
        return f(*a, **kw)
    return wrapper

@explicit_checker
def my_function(a, b=0, c=1, explicit_params=None):
    print a, b, c, explicit_params
    if 'b' in explicit_params:
        pass # Do whatever you want


my_function(1)
my_function(1, 0)
my_function(1, c=1)

我有时使用一个通用的唯一字符串(比如UUID)


这样,如果用户尝试,甚至没有人能猜到默认值,因此我可以非常有信心,当我看到
arg
的值时,它没有被传入。

我已经见过这种模式好几次了(例如library
unittest
py flags
jinja
):

…或同等的一个班轮…:

DEFAULT = type( 'Default', (), { '__repr__': lambda x: 'DEFAULT' } )()

DEFAULT=object()
不同,这有助于类型检查,并在出现错误时提供信息——通常是字符串表示形式(
“DEFAULT”
)或类名(
“DEFAULT”
)在错误消息中使用。

您可以从
foo.\uuuuu默认值\uuuuuu
foo.\uuuuuuu默认值\uuuuu

参见下面的一个简单示例

def foo(a, b, c=123, d=456, *, e=789, f=100):
    print(foo.__defaults__)
    # (123, 456) 
    print(foo.__kwdefaults__)
    # {'e': 789, 'f': 100}
    print(a, b, c, d, e, f)

#and these variables are also accessible out of function body
print(foo.__defaults__)    
# (123, 456)  
print(foo.__kwdefaults__)  
# {'e': 789, 'f': 100}

foo.__kwdefaults__['e'] = 100500

foo(1, 2) 
#(123, 456)
#{'f': 100, 'e': 100500}
#1 2 123 456 100500 100
然后通过使用运算符
=
is
可以比较它们

对于某些情况,下面的代码就足够了

例如,您需要避免更改默认值,然后可以检查是否相等,如果相等,则进行复制

    def update_and_show(data=Example):
        if data is Example:
            data = copy.deepcopy(data)
        update_inplace(data) #some operation
        print(data)

另外,使用
inspect
中的
getcallargs
非常方便,因为它返回调用函数的实际参数。将函数、args和kwargs传递给它(
inspect.getcallargs(func,/,*args,**kwds)
),它将返回用于调用的实际方法参数,并考虑默认值和其他内容。请看下面的一个例子

from inspect import getcallargs

# we have a function with such signature
def show_params(first, second, third=3):
    pass

# if you wanted to invoke it with such params (you could get them from a decorator as example)
args = [1, 2, 5]
kwargs = {}
print(getcallargs(show_params, *args, **kwargs))
#{'first': 1, 'second': 2, 'third': 5}

# here we didn't specify value for d
args = [1, 2, 3, 4]
kwargs = {}

# ----------------------------------------------------------
# but d has default value =7
def show_params1(first, *second, d = 7):
    pass


print(getcallargs(show_params1, *args, **kwargs))
# it will consider b to be equal to default value 7 as it is in real method invocation
# {'first': 1, 'second': (2, 3, 4), 'd': 7}

# ----------------------------------------------------------
args = [1]
kwargs = {"d": 4}

def show_params2(first, d=3):
    pass


print(getcallargs(show_params2, *args, **kwargs))
#{'first': 1, 'd': 4}

很多答案都有一些完整的信息,所以我想把它们和我最喜欢的模式结合起来

默认值是可更改的
类型
如果默认值是一个可变对象,那么您就很幸运了:您可以利用这样一个事实,即定义函数时Python的默认参数会被计算一次(在上一节的答案末尾会有更多关于这一点的信息)

这意味着您可以使用
is
轻松比较默认的可变值,以查看它是作为参数传递的还是默认保留的,如以下示例中的函数或方法:

def f(value={}):
    if value is f.__defaults__[0]:
        print('default')
    else:
        print('passed in the call')

不可变默认参数 现在,如果您的默认值是一个
不可变的
值(请记住,即使字符串也是不可变的!),那么它就不那么优雅了,因为您不能像现在这样利用这个技巧,但仍然可以做一些事情,仍然利用可变类型;基本上,在函数签名中放置一个可变的“假”默认值,在函数体中放置所需的“真”默认值

def f(value={}):
    """
    my function
    :param value: value for my function; default is 1
    """
    if value is f.__defaults__[0]:
        print('default')
        value = 1
    else:
        print('passed in the call')
    # whatever I want to do with the value
    print(value)
如果您真正的默认值是
None
,但
None
是不可变的,那么您会觉得特别有趣。。。您仍然需要显式使用mutable作为函数默认参数,并在代码中切换为None

使用
Default
类作为不可变的默认值 或者,与@c-z建议类似,如果python文档还不够:-),您可以在它们之间添加一个对象,使API更显式(无需阅读文档);使用的_proxy u Default类实例是可变的,并且将包含您想要使用的实际默认值

class Default:
    def __repr__(self):
        return "Default Value: {} ({})".format(self.value, type(self.value))

    def __init__(self, value):
        self.value = value

def f(default=Default(1)):
    if default is f.__defaults__[0]:
        print('default')
        print(default)
        default = default.value
    else:
        print('passed in the call')
    print("argument is: {}".format(default))
现在:

您可以随时运行
testme()
,始终可以看到对同一默认实例的引用(因此默认值基本上是不可变的:-))


请记住,Python中只有3个可变项:
set
list
dict
;其他一切-甚至字符串是不可变的。

@Ellioh的答案在Python2中有效。在python 3中,以下代码应该可以工作:

import inspect
def explicit_checker(f):
    varnames = inspect.getfullargspec(f)[0]
    def wrapper(*a, **kw):
        kw['explicit_params'] = set(list(varnames[:len(a)]) + list(kw.keys()))
        return f(*a, **kw)
    return wrapper

@explicit_checker
def my_function(a, b=0, c=1, explicit_params=None):
    print a, b, c, explicit_params
    if 'b' in explicit_params:
        pass # Do whatever you want

此方法可以保留参数名称和默认值(而不是**kwargs),具有更好的可读性

这是stefano答案的一个变体,但我发现更具可读性:

not_specified = {}

def foo(x=not_specified):
    if x is not_specified:
            print("not specified")
    else:
            print("specified")

因为我想在函数中检查它:)只需使用
None
作为默认值并检查它。如果您真的可以设置此测试,那么您还可以排除用户显式传递调用默认行为的值的任何可能性。这可以通过一种比您接受的答案更可重用、更美观的方式来完成,至少对于CPython来说是这样。请看下面我的答案。@Volatility:如果有两组默认值,这很重要。考虑一个递归类:<代码>类My():DEF.Y.IITITY(自已,p=无,A=true,B=true,C=false)< /代码>用户用<代码> x=我的(b= false)< />代码调用它。如果函数可以检测到b未显式设置,且未设置的变量将从顶层向下传递,则类方法可以使用
x=My(_p=self,c=True)
调用自身。但是如果不能,递归调用必须显式地传递每个变量:
x=My(a=self.a,b
class Default:
    def __repr__(self):
        return "Default Value: {} ({})".format(self.value, type(self.value))

    def __init__(self, value):
        self.value = value

def f(default=Default(1)):
    if default is f.__defaults__[0]:
        print('default')
        print(default)
        default = default.value
    else:
        print('passed in the call')
    print("argument is: {}".format(default))
>>> f()
default
Default Value: 1 (<class 'int'>)
argument is: 1

>>> f(2)
passed in the call
argument is: 2
def testme(default=[]):
    print(id(default))
import inspect
def explicit_checker(f):
    varnames = inspect.getfullargspec(f)[0]
    def wrapper(*a, **kw):
        kw['explicit_params'] = set(list(varnames[:len(a)]) + list(kw.keys()))
        return f(*a, **kw)
    return wrapper

@explicit_checker
def my_function(a, b=0, c=1, explicit_params=None):
    print a, b, c, explicit_params
    if 'b' in explicit_params:
        pass # Do whatever you want
not_specified = {}

def foo(x=not_specified):
    if x is not_specified:
            print("not specified")
    else:
            print("specified")