2009-04-12 8 views
31

Estaba creando una utilidad de línea de comandos simple y utilizando un diccionario como una especie de declaración de caso con palabras clave que enlazan a su función apropiada. Las funciones tienen diferentes cantidades de argumentos requeridos para que actualmente se compruebe si el usuario ingresó la cantidad correcta de argumentos necesarios para cada función. Puse la cantidad requerida dentro de la declaración de caso del diccionario en el formulario {Keyword:(FunctionName, AmountofArguments)}.Determinación programática de la cantidad de parámetros que requiere una función: Python

Esta configuración actual funciona perfectamente bien, sin embargo, me preguntaba por el interés de la autoevaluación si había una manera de determinar el número requerido de argumentos en una función y mis intentos de google han devuelto nada de valor, pero veo cómo args y kwargs podrían atornillar tal comando debido a la cantidad ilimitada de argumentos que permiten.

Respuesta

36

inspect.getargspec():

obtener los nombres y valores por defecto de los argumentos de una función. Se devuelve una tupla de cuatro cosas: (args, varargs, varkw, por defecto). args es una lista de los nombres de los argumentos (puede contener listas anidadas). varargs y varkw son los nombres de los argumentos * y ** o Ninguno. los valores predeterminados son una tupla de valores de argumento predeterminados o Ninguno si no hay argumentos predeterminados; si esta tupla tiene n elementos, corresponden a los últimos n elementos listados en args.

+0

Gracias Acabo de descubrir que muy función ya que publicaste esa respuesta. Eso es exactamente lo que necesito. – tomeedee

+0

Además, dado que no tengo el representante para editar su respuesta, tiene un error tipográfico en su respuesta es getargspec() no getargspect() – tomeedee

+0

Debe usar inspect.getfullargspec (func) (http://docs.python.org/3.1/ library/inspect.html # inspect.getfullargspec) para Python 3.x – Casebash

14

Lo que queremos es, en general, no es posible, debido a la utilización de varargs y kwargs, pero inspect.getargspec (Python 2.x) y inspect.getfullargspec (3.x Python) se acercan. 2.x

  • Python:

    >>> import inspect 
    >>> def add(a, b=0): 
    ...  return a + b 
    ... 
    >>> inspect.getargspec(add) 
    (['a', 'b'], None, None, (0,)) 
    >>> len(inspect.getargspec(add)[0]) 
    2 
    
  • Python 3.x:

    >>> import inspect 
    >>> def add(a, b=0): 
    ...  return a + b 
    ... 
    >>> inspect.getfullargspec(add) 
    FullArgSpec(args=['a', 'b'], varargs=None, varkw=None, defaults=(0,), kwonlyargs=[], kwonlydefaults=None, annotations={}) 
    >>> len(inspect.getfullargspec(add).args) 
    2 
    
+3

'inspect.getargspec (función)' es una tupla con nombre, por lo que en lugar de 'len (inspeccionar.getargspec (función) [0])' puede usar el 'leg (inspect.getargspec (función) .args)' más legible '' . –

+0

'getargspec' ha quedado en desuso desde Python 3, use' signature' o 'getfullargspec' en su lugar. – Akshay

+0

@ 1 '': Eso es cierto a partir de la versión 2.6, no estaba en la versión anterior que usaba en ese momento. – Stephan202

1

hacer que cada comando una clase, derivado de una base abstracta que define la estructura general de un comando. En la medida de lo posible, la definición de propiedades de comando debe colocarse en variables de clase con métodos definidos en la clase base que maneja esos datos.

Registre cada una de estas subclases con una clase de fábrica. Esta clase de fábrica obtiene la lista de argumentos y decide qué comando ejecutar al crear instancias de la subclase de comando adecuada.

La comprobación de los argumentos es manejada por las subclases de comandos mismas, utilizando métodos generales definidos correctamente a partir de la clase base del comando.

De esta manera, nunca necesita codificar repetidamente las mismas cosas, y realmente no hay necesidad de emular la instrucción switch. También hace que extender y agregar comandos sea muy fácil, ya que simplemente puede agregar y registrar una nueva clase. Nada más que cambiar

+2

+1; Usted, señor, es gracioso. Dado que usted escribió esto después de que se haya publicado la respuesta aceptada, asumo que usted está intencionalmente expresando puntos de vista muy convincentes sobre programación defensiva en lugar de intentar describir la forma más fácil de contar cuántos argumentos toma la función. – user1612868

0

Excelente pregunta. Acabo de tener el problema de que quería escribir una función que toma un argumento de devolución de llamada. Dependiendo de la cantidad de argumentos de esa devolución de llamada, debe llamarse de manera diferente.

Comencé con la respuesta de gimel, luego expandí para poder tratar con edificaciones que no reaccionan bien con el módulo inspect (raise TypeError).

Así que aquí está el código para comprobar si una función de espera exactamente un argumento:

def func_has_one_arg_only(func, typical_argument=None, ignore_varargs=False): 
    """True if given func expects only one argument 

    Example (testbench): 
    assert not func_has_one_arg_only(dict.__getitem__), 'builtin 2 args' 
    assert func_has_one_arg_only(lambda k: k), 'lambda 1 arg' 
    assert not func_has_one_arg_only(lambda k,x: k), 'lambda 2 args' 
    assert not func_has_one_arg_only(lambda *a: k), 'lambda *a' 
    assert not func_has_one_arg_only(lambda **a: k), 'lambda **a' 
    assert not func_has_one_arg_only(lambda k,**a: k), 'lambda k,**a' 
    assert not func_has_one_arg_only(lambda k,*a: k), 'lambda k,*a' 

    assert func_has_one_arg_only(lambda k: k, ignore_varargs=True), 'lambda 1 arg' 
    assert not func_has_one_arg_only(lambda k,x: k, ignore_varargs=True), 'lambda 2 args' 
    assert not func_has_one_arg_only(lambda *a: k, ignore_varargs=True), 'lambda *a' 
    assert not func_has_one_arg_only(lambda **a: k, ignore_varargs=True), 'lambda **a' 
    assert func_has_one_arg_only(lambda k,**a: k, ignore_varargs=True), 'lambda k,**a' 
    assert func_has_one_arg_only(lambda k,*a: k, ignore_varargs=True), 'lambda k,*a' 
    """ 

    try: 
     import inspect 
     argspec = inspect.getargspec(func) 
    except TypeError:     # built-in c-code (e.g. dict.__getitem__) 
     try: 
      func(typical_argument) 
     except TypeError: 
      return False 
     else: 
      return True 
    else: 
     if not ignore_varargs: 
      if argspec.varargs or argspec.keywords: 
       return False 
     if 1 == len(argspec.args): 
      return True 
     return False 
    raise RuntimeError('This line should not be reached') 

Se puede controlar el comportamiento relacionado con varargs argumentos *args y **kwargs con el parámetro ignore_varargs.

El parámetro typical_argument es un kludge: Si inspect no funciona, p. en las construcciones antes mencionadas, entonces tratamos de llamar a la función con un argumento y ver qué pasa.

El problema con este enfoque es que hay varias razones para raise TypeError: Se usa el número de argumentos incorrecto o se usa un tipo de argumento incorrecto. Al permitir que el usuario proporcione un typical_argument estoy tratando de eludir este problema.

Esto no es agradable. Pero puede ayudar a las personas que tienen la misma pregunta y también al hecho de que inspect no pueden inspeccionar las implementaciones de funciones codificadas en C. Tal vez la gente tiene una mejor sugerencia?

2

Esto ya ha sido respondida, pero sin el módulo de inspección también se puede utilizar someMethod.func_code.co_argcount

3

En Python 3, use someMethod.__code__.co_argcount

(ya someMethod.func_code.co_argcount ya no funciona)

+0

esto ya ha sido respondido hace 3 meses. Verifique las otras respuestas antes de responder (y por supuesto no copie otras respuestas) –

+0

Estaba buscando una respuesta a esta pregunta, y ninguna de las enumeradas fue satisfactoria para mí. Encontré esta respuesta en otra parte y la ubiqué aquí en caso de que alguien viniera buscando este problema a través de Google. otras respuestas. No copié ninguna respuesta. – Bobort

+0

mira la respuesta Josep Valls lo hizo el 4 de agosto: "Esto ya ha sido respondido pero sin el módulo de inspección también puedes usar' someMethod.func_code.co_argcount' ". Bastante similar no es así? tal vez es por eso que fue marcado por la atención del moderador. –

Cuestiones relacionadas