【问题标题】:Misleading(?) TypeError when passing keyword arguments to function defined with positional arguments将关键字参数传递给使用位置参数定义的函数时出现误导(?)类型错误
【发布时间】:2010-02-01 20:10:31
【问题描述】:

在 cPython 2.4 中:

def f(a,b,c,d):
    pass

>>> f(b=1,c=1,d=1)
TypeError: f() takes exactly 4 non-keyword arguments (0 given)

但是:

>>> f(a=1,b=1,c=1)
TypeError: f() takes exactly 4 non-keyword arguments (3 given)

显然,我真的很了解 Python 的函数参数处理机制。有人愿意分享一些关于这方面的信息吗?我看到了正在发生的事情(比如填充参数槽,然后放弃),但我认为这会搞砸新手。

(另外,如果人们有更好的问题关键字——比如“guts”——请重新标记)

【问题讨论】:

  • 只是出于好奇,您希望 Python 在您给出的两个示例中做什么?
  • 我希望它说 3 给了两者。

标签: python arguments


【解决方案1】:

当你说

def f(a,b,c,d):

你告诉 python f 需要 4 个 positional 参数。每次调用f 时都必须准确地给出4 个参数,第一个值将分配给a,第二个值将分配给b,等等。

您可以使用类似的方式致电f

f(1,2,3,4)f(a=1,b=2,c=3,d=4),甚至f(c=3,b=2,a=1,d=4)

但在所有情况下,都必须提供 4 个参数。

f(b=1,c=1,d=1) 返回错误,因为没有为 a 提供值。 (0 给定) f(a=1,b=1,c=1) 返回错误,因为没有为 d 提供值。 (3)

给出的参数数量表示python在意识到错误之前走了多远。

顺便说一句,如果你说

def f(a=1,b=2,c=3,d=4):

那么你告诉 python f 需要 4 个 可选 参数。如果某个 arg 没有给出,那么它的默认值会自动提供给你。然后你就可以打电话了

f(a=1,b=1,c=1)f(b=1,c=1,d=1)

【讨论】:

  • 所以,这正是不明显的部分:给出的参数数量表示python在意识到错误之前已经走了多远。我会在“可以咬新手的疣”,因为事实上,两种情况下都给出了 3 个。
  • 如何制作一个调用包装器来捕获此类错误并引发更合适的错误?我已经尝试过这样做,但是有大量有问题的小细节;有其他人尝试过吗? (另外,我想知道为什么它没有在默认的 CPython 中完成)
  • "给出的参数数量表示python在意识到错误之前已经走了多远。"是这里的关键。
【解决方案2】:

理论上可以用更清晰和信息更丰富的东西来包装生成的 TypeError。但是,有很多小细节我不知道如何解决。

注意:下面的代码是一个勉强能用的例子,不是一个完整的解决方案。

try:
    fn(**data)
except TypeError as e:
    ## More-sane-than-default processing of a case `parameter ... was not specified`
    ## XXX: catch only top-level exceptions somehow?
    ##  * through traceback?
    if fn.func_code.co_flags & 0x04:  ## XXX: check
        # it accepts `*ar`, so not the case
        raise
    f_vars = fn.func_code.co_varnames
    f_defvars_count = len(fn.func_defaults)
    ## XXX: is there a better way?
    ##  * it catches `self` in a bound method as required. (also, classmethods?)
    ##  * `inspect.getargspec`? Imprecise, too (for positional args)
    ##  * also catches `**kwargs`.
    f_posvars = f_vars[:-f_defvars_count]
    extra_args = list(set(data.keys()) - set(f_vars))
    missing_args = list(set(f_posvars) - set(data.keys()))
    if missing_args:  # is the case, raise it verbosely.
        msg = "Required argument(s) not specified: %s" % (
          ', '.join(missing_args),)
        if extra_args:
            msg += "; additionally, there are extraneous arguments: %s" % (
              ', '.join(extra_args))
        raise TypeError(msg, e)
        #_log.error(msg)
        #raise
    raise

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-09
    • 2016-12-13
    • 2019-05-29
    相关资源
    最近更新 更多