是否可以编写一个行为与getattr()相似的函数签名?

时间:2017-06-14 16:27:10

标签: python signature built-in

根据help(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代码完全不同的实现,并且可能无法完美地模拟其行为方式。但这是我一段时间以来的好奇心。

2 个答案:

答案 0 :(得分:3)

这些函数签名特别适用于使用C级PyArg_Parse*函数族和Argument Clinic预处理器编写的函数。在Python中没有内置的方式来编写这种签名。您可以使用*args获得最接近的内容。

(顺便说一句,如果 决定实现此功能, 已经选择了语法,如PEP 457中所述,但就目前而言,该语法仅用于文档,并且在Argument Clinic中使用了一个轻微的变体。)

答案 1 :(得分: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'
    
  • 强制执行最小/最大参数数量

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

  • 不是很多样板