Cómo averiguar la aridad de un método en Python

Me gustaría averiguar la aridad de un método en Python (la cantidad de parámetros que recibe). Ahora mismo estoy haciendo esto:

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 

Me gustaría poder lograr esto:

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

Actualización : En este momento, la función anterior falla con los tipos incorporados, cualquier ayuda sobre esto también sería apreciada:

  # Traceback (most recent call last): # File "bla.py", line 10, in  # 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 

El módulo de inspect de la biblioteca estándar de Python es tu amigo. ¡Consulta los documentos en línea ! inspect.getargspec(func) devuelve una tupla con cuatro elementos, args, varargs, varkw, defaults : len(args) es la “aridad principal”, pero la aridad puede ser desde eso hasta el infinito si tiene varargs y / o varkw no None , y algunos argumentos pueden omitirse (y predeterminarse) si los defaults no son None . ¿Cómo convertir eso en un solo número, me gana, pero presumiblemente tienes tus ideas en el asunto? -)

Esto se aplica a las funciones codificadas en Python, pero no a las codificadas en C. Nada en la API de Python C permite que las funciones codificadas en C (incluidas las integradas) expongan su firma para la introspección, excepto a través de su cadena de documentación (u opcionalmente a través de anotaciones en Python 3); por lo tanto, tendrá que recurrir al análisis de la cadena de documentos como última zanja si otros enfoques fallan (por supuesto, la cadena de documentos también puede faltar, en cuyo caso la función seguirá siendo un misterio).

Utilice un decorador para decorar métodos por ejemplo

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

Ahora implemente la función _arity para calcular el conteo de arg en función de sus necesidades

Esta es la única forma en que puedo pensar que debería ser 100% efectivo (al menos con respecto a si la función está definida por el usuario o escrita en C) para determinar la arity (mínima) de una función. Sin embargo, debe asegurarse de que esta función no cause ningún efecto secundario y que no genere un 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 

Como puede ver, esto determinará la aridad mínima si una función toma una cantidad variable de argumentos. Tampoco tendrá en cuenta el argumento self o cls de un método de clase o instancia.

Sin embargo, para ser totalmente honesto, no usaría esta función en un entorno de producción a menos que supiera exactamente qué funciones se llamarán, ya que hay mucho espacio para errores estúpidos. Esto puede derrotar el propósito.

Idealmente, querría aplicar un parche a la función de aridad como un método en los funtores de Python. Así es cómo:

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

Pero, CPython implementa los funtores en C, en realidad no se pueden modificar. Esto puede funcionar en PyPy, sin embargo.

Eso es todo asumiendo que su función arity () es correcta. ¿Qué pasa con las funciones variables? ¿Incluso quieres una respuesta entonces?

Aquí hay otro bash de usar metaclase, ya que uso Python 2.5, pero con 2.6 puede decorar la clase fácilmente.

La metaclase también se puede definir a nivel de módulo, por lo que funciona para todas las clases.

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