2017-06-14 28 views
3

help(getattr),两个或三个参数被接受:是否可以编写一个像getattr()那样工作的函数签名?

getattr(...) 
    getattr(object, name[, default]) -> value 

做一些简单的测试,我们可以证实这一点:

>>> obj = {} 
>>> getattr(obj, 'get') 
<built-in method get of dict object at 0x7f6d4beaf168> 
>>> getattr(obj, 'bad', 'with default') 
'with default' 

太少/太多的争论也像预期的那样:

>>> getattr() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: getattr expected at least 2 arguments, got 0 
>>> getattr(obj, 'get', 'with default', 'extra') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: getattr expected at most 3 arguments, got 4 

帮助文本中指定的参数名似乎不被接受为关键字参数:

>>> getattr(object=obj, name='get') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: getattr() takes no keyword arguments 

inspect模块是没有帮助在这里:

>>> import inspect 
>>> inspect.getargspec(getattr) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "/usr/lib/python2.7/inspect.py", line 816, in getargspec 
    raise TypeError('{!r} is not a Python function'.format(func)) 
TypeError: <built-in function getattr> is not a Python function 

(messaging is a little different in python3, but the gist is the same) 

现在的问题是:是否有写我自己的Python功能与​​行为完全像getattr的签名的签名一个简单的方法?也就是说,关键字参数是不允许的,并且最小数量/最大数量的参数被执行?我来最接近的是以下几点:

def myfunc(*args): 
    len_args = len(args) 
    if len_args < 2: 
     raise TypeError('expected at least 2 arguments, got %d' % len_args) 
    elif len_args > 3: 
     raise TypeError('expected at most 3 arguments, got %d' % len_args) 
    ... 

但现在,而不是像objectname有意义的参数名称,我们得到args[0]args[1]。这也是很多的样板,感觉很不愉快。我知道,作为内置的,getattr必须与典型的Python代码有很大的不同,并且可能没有办法完美地模拟它的行为方式。但这是我一段时间以来的好奇心。

+0

@Makoto:不重复;可选参数很容易,但是这个问题特别针对仅限位置的参数行为。 – user2357112

回答

3

这些类型的函数签名是用C编写的函数特有的,使用C级PyArg_Parse*函数族和Argument Clinic预处理器。在Python中没有内置的方式来编写这种签名。你可以得到的最接近的是你已经使用的*args

(顺便说一句,有已经语法的情况下,挑选出他们决定来实现这个功能,在PEP 457描述,但现在,这种语法在文档中只使用,并有轻微变种用于论证诊所。)

+0

但它通常可以完成。 – martineau

+0

@martineau:据我所知,最接近你可以手动解析'* args',这是提问者已经提出的。 – user2357112

+0

是的,但他们这样做的方式可能并不是最简单的。对于他们的问题中的特定示例,在我看来,就像一个简单的'def myfunc(obj,name,default = None):'就足够了 - 尽管如果它们需要用某种唯一值替换“None”一个合法的参数值。这也很容易做到...... – martineau

3

此代码蜱你的大部分要求:

def anonymise_args(fn): 
    @functools.wraps(fn) 
    def wrap(*args): 
     return fn(*args) 
    return wrap 


@anonymise_args 
def myfunc(obj, name, default=None): 
    print obj, name, default 
  • 关键字参数是不允许的参数

    x.myfunc(obj=1, name=2) 
    TypeError: wrap() got an unexpected keyword argument 'obj' 
    
  • 一个minumum /最大数量的强制

    x.myfunc(1,2,3,4) 
    TypeError: myfunc() takes at most 3 arguments (4 given) 
    
  • 有意义的参数名称

  • 不是很多的样板

+0

你在测试中漏掉了'functools.wraps'吗?第一个错误信息看起来不正确。 – user2357112

+0

另外,在wrap函数中使用有意义的返回值时,您应该'wrap'中的'return'来正确传递返回值。 – user2357112

+0

1)我在我的测试中加入了'functools.wraps'。我不知道为什么错误信息包含'wrap()'。 2)是的,'wrap'应该包含'return'。 –

相关问题