2009-06-13 10 views
28

Je voudrais connaître l'arité d'une méthode en Python (le nombre de paramètres qu'elle reçoit). En ce moment je fais ceci:Comment trouver l'arité d'une méthode en 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 

Je voudrais être en mesure d'atteindre cet objectif:

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

Mise à jour: En ce moment, la fonction ci-dessus échoue avec les types intégrés, toute aide sur ce serait également apprécié:

# 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 
+1

Qu'est-ce qui ne va pas avec help()? Qu'est-ce que cela ne fait pas ce dont vous avez besoin? –

+7

J'ai besoin de l'utiliser par programmation, donc je n'ai pas envie d'analyser le texte pour retourner un nombre. –

+2

Qu'est-ce que vous réclamez l'arité de 'def a (* args):'? –

Répondre

44

Module inspect de la bibliothèque standard de Python est votre ami - Voir the online docs! inspect.getargspec(func) retourne un tuple avec quatre articles, args, varargs, varkw, defaults: len(args) est le « arité primaire », mais arité peut être quelque chose de ce à l'infini si vous avez varargs et/ou varkw pas None, et certains arguments peuvent être omis (et en défaut) si defaults n'est pas None. Comment vous transformez cela en un seul nombre, me bat, mais probablement vous avez vos idées en la matière! -)

Ceci s'applique aux fonctions codées en Python, mais pas aux fonctions codées en C. Rien dans l'API Python C ne permet aux fonctions C-codées (y compris les fonctions intégrées) d'exposer leur signature pour l'introspection, sauf via leur docstring (ou éventuellement via annotations dans Python 3); Ainsi, aura besoin de revenir à l'analyse syntaxique en dernier recours si les autres approches échouent (bien sûr, la docstring peut aussi manquer, auquel cas la fonction restera un mystère).

+0

Cela fonctionne pour mes méthodes que je définis, mais pas pour les built-ins, voir inspect.getargspec (str.split) –

+0

@Federico, vous avez raison: rien dans l'API Python C ne permet aux fonctions C-codées (qui sont built-ins sont) exposent leur signature pour l'introspection, sauf via leur docstring (ou éventuellement via __annotations__ dans Python 3); Donc, "sentez-vous comme ça" ou non, vous aurez besoin de revenir à l'analyse syntaxique comme un dernier fossé si d'autres approches échouent (bien sûr la docstring pourrait manquer aussi). –

+0

en éditant ma réponse pour ajouter cette clarification. –

5

Utilisez un décorateur pour décorer des méthodes, par ex.

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

maintenant mettre en œuvre la fonction _arity pour calculer le nombre de arg en fonction de vos besoins

+0

réellement vous pouvez utiliser la métaclasse pour décorer toutes les méthodes d'une classe au lieu de le faire manuellement –

2

Idéalement, vous voudriez singe-patcher la fonction arité comme méthode sur foncteurs Python. Voici comment procéder:

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

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

Mais, CPython implémente des foncteurs en C, vous ne pouvez pas les modifier. Cela peut fonctionner dans PyPy, cependant. C'est tout en supposant que votre fonction arity() est correcte. Qu'en est-il des fonctions variadiques? Voulez-vous même une réponse alors?

0

ici est une autre tentative en utilisant métaclasse, comme je l'utilise Python 2.5, mais avec 2.6, vous pouvez facilement décorer la classe

métaclasse peut également être défini au niveau du module, il fonctionne pour toutes les classes

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

C'est la seule façon que je puisse penser qui devrait être efficace à 100% (du moins si la fonction est définie par l'utilisateur ou écrite en C) pour déterminer l'arité (minimum) d'une fonction.Cependant, vous devez vous assurer que cette fonction ne causera pas d'effets secondaires et qu'il ne jetterai pas 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 

Comme vous pouvez le voir, cela permettra de déterminer le minimum arité si une fonction prend une quantité variable d'arguments. Il ne prendra pas non plus en compte l'argument self ou cls d'une méthode de classe ou d'instance. Pour être tout à fait honnête, je n'utiliserais cette fonction dans un environnement de production que si je savais exactement quelles fonctions seraient appelées car il y a beaucoup de place pour des erreurs stupides. Cela peut aller à l'encontre du but.

Questions connexes