如何在Python中找出方法的arity

时间:2009-06-13 05:07:18

标签: python metaprogramming

我想找出Python中方法的优点(它接收的参数数量)。 现在我正在这样做:

def arity(obj, method):
  return getattr(obj.__class__, method).func_code.co_argcount - 1 # remove self

class Foo:
  def bar(self, bla):
    pass

arity(Foo(), "bar")   # => 1

我希望能够实现这一目标:

Foo().bar.arity()   # => 1

更新:目前上述功能因内置类型而失败,对此的任何帮助也将受到赞赏:

 # Traceback (most recent call last):
 #   File "bla.py", line 10, in <module>
 #     print arity('foo', 'split')  # =>
 #   File "bla.py", line 3, in arity
 #     return getattr(obj.__class__, method).func_code.co_argcount - 1 # remove self
 # AttributeError: 'method_descriptor' object has no attribute 'func_co

5 个答案:

答案 0 :(得分:46)

来自Python标准库的

模块inspect是您的朋友 - 请参阅the online docsinspect.getargspec(func)会返回一个包含四个项目的元组,args, varargs, varkw, defaultslen(args)是“主要的arity”,但如果你有varargs和/或arity,那么arity可以是从无到有的任何东西varkw不是None,如果defaults不是None,则可能会省略(并默认)某些参数。你怎么把它变成一个数字,打败了我,但大概你在这件事上有你的想法! - )

这适用于Python编码的函数,但不适用于C编码函数。 Python C API中没有任何内容允许C编码函数(包括内置函数)公开其内省签名,除非通过其docstring(或者可选地通过Python 3中的注释);所以,如果其他方法失败,需要回退到docstring解析作为最后一个沟渠(当然,文档字符串可能也会丢失,在这种情况下,函数仍然是个谜)。

答案 1 :(得分:5)

使用装饰器来装饰方法,例如

def arity(method):

  def _arity():
    return method.func_code.co_argcount - 1 # remove self

  method.arity = _arity

  return method

class Foo:
  @arity
  def bar(self, bla):
    pass

print Foo().bar.arity()

现在实施_arity功能,根据您的需要计算arg数

答案 2 :(得分:2)

理想情况下,您希望将arity函数作为Python仿函数的方法进行修补。方法如下:

def arity(self, method):
    return getattr(self.__class__, method).func_code.co_argcount - 1

functor = arity.__class__
functor.arity = arity
arity.__class__.arity = arity

但是,CPython在C中实现了仿函数,你无法实际修改它们。不过,这可能适用于PyPy。

这就是假设你的arity()函数是正确的。可变函数怎么样?你甚至想要一个答案吗?

答案 3 :(得分:2)

这是我能够想到的唯一一种在确定函数(最小)arity时应该100%有效的方法(至少关于函数是用户定义的还是用C语言编写的)。但是,您应该确保此函数不会导致任何副作用,并且它不会引发TypeError:

from functools import partial

def arity(func):
    pfunc = func
    i = 0
    while True:
        try:
            pfunc()
        except TypeError:
            pfunc = partial(pfunc, '')
            i += 1
        else:
            return i

def foo(x, y, z):
    pass

def varfoo(*args):
    pass

class klass(object):
    def klassfoo(self):
        pass

print arity(foo)
print arity(varfoo)

x = klass()
print arity(x.klassfoo)

# output
# 3
# 0
# 0

如您所见,如果函数采用可变数量的参数,这将确定 minimum arity。它也不会考虑类或实例方法的self或cls参数。

但是说实话,我不会在生产环境中使用这个功能,除非我确切知道会调用哪些函数,因为有很多愚蠢的错误空间。这可能会破坏目的。

答案 4 :(得分:0)

这是使用元类的另一个尝试,因为我使用python 2.5,但使用2.6你可以轻松地装饰类

元类也可以在模块级别定义,因此适用于所有类

from types import FunctionType

def arity(unboundmethod):
    def _arity():
        return unboundmethod.func_code.co_argcount - 1 # remove self
    unboundmethod.arity = _arity

    return unboundmethod

class AirtyMetaclass(type):
    def __new__(meta, name, bases, attrs):
        newAttrs = {}
        for attributeName, attribute in attrs.items():
            if type(attribute) == FunctionType:
                attribute = arity(attribute)

            newAttrs[attributeName] = attribute

        klass = type.__new__(meta, name, bases, newAttrs)

        return klass

class Foo:
    __metaclass__ = AirtyMetaclass
    def bar(self, bla):
        pass

print Foo().bar.arity()