开发者

Misleading(?) TypeError when passing keyword arguments to function defined with positional arguments

In 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)

but:

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

Clearly, I don't really really understand Python's function-argument processing mechanism. Anyone care to share some light on this? I see what's happening (something like filling argum开发者_JS百科ent slots, then giving up), but I think this would foul up a newbie.

(also, if people have better question keywords -- something like "guts" -- please retag)


When you say

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

you are telling python that f takes 4 positional arguments. Every time you call f it must be given exactly 4 arguments, and the first value will be assigned to a, the second to b, etc.

You are allowed to call f with something like

f(1,2,3,4) or f(a=1,b=2,c=3,d=4), or even f(c=3,b=2,a=1,d=4)

but in all cases, exactly 4 arguments must be supplied.

f(b=1,c=1,d=1) returns an error because no value has been supplied for a. (0 given) f(a=1,b=1,c=1) returns an error because no value has been supplied for d. (3 given)

The number of args given indicates how far python got before realizing there is an error.

By the way, if you say

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

then your are telling python that f takes 4 optional arguments. If a certain arg is not given, then its default value is automatically supplied for you. Then you could get away with calling

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


It is theoretically possible to wrap the resulting TypeError with something more clear and informative. However, there are many little details some of which I don't know how to solve.

NOTE: the code below is a barely-working example, not a complete solution.

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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜