33

我想找出 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
4

5 回答 5

51

Python 标准库中的模块inspect是您的朋友——请参阅在线文档inspect.getargspec(func)返回一个包含四个项目的元组,args, varargs, varkw, defaults:是“主要的 arity”,但如果你有和/或没有, len(args)arity 可以是从那个到无穷大的任何东西,如果is not ,一些参数可能会被省略(和默认)。你如何把它变成一个数字,打败了我,但大概你对这件事有你的想法!-)varargsvarkwNonedefaultsNone

这适用于 Python 编码的函数,但不适用于 C 编码的函数。Python C API 中的任何内容都不允许 C 编码函数(包括内置函数)公开其签名以进行自省,除非通过它们的文档字符串(或可选地通过Python 3 中的注释);因此,如果其他方法失败,您需要回退到文档字符串解析作为最后一道防线(当然,文档字符串也可能丢失,在这种情况下,该函数将仍然是一个谜)。

于 2009-06-13T05:13:14.700 回答
6

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

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 计数

于 2009-06-13T05:21:57.207 回答
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() 函数是正确的。可变参数函数呢?那你还想要答案吗?

于 2009-06-13T05:23:51.400 回答
2

这是我能想到的唯一方法,在确定函数的(最小)数量时应该 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

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

To be totally honest though, I wouldn't use this function in a production environment unless I knew exactly which functions would be called though as there is a lot of room for stupid errors. This may defeat the purpose.

于 2009-06-13T16:08:13.503 回答
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()
于 2009-06-13T05:39:34.123 回答