Как узнать арность метода в Python - PullRequest
29 голосов
/ 13 июня 2009

Я бы хотел выяснить арность метода в 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 ]

47 голосов
/ 13 июня 2009

Модуль inspect из стандартной библиотеки Python - ваш друг - смотрите онлайн-документы ! inspect.getargspec(func) возвращает кортеж с четырьмя элементами, args, varargs, varkw, defaults: len(args) - это "первичная арность", но арность может быть чем угодно от бесконечности, если у вас есть varargs и / или varkw не None, и некоторые аргументы могут быть опущены (и по умолчанию), если defaults не None. То, как вы превращаете это в одно число, бьет меня, но, вероятно, у вас есть свои идеи по этому вопросу! -)

Это относится к кодированным на Python функциям, но не к кодированным на C. Ничто в Python C API не позволяет C-кодированным функциям (включая встроенные) выставлять свою подпись для самоанализа, кроме как через их строку документации (или опционально через аннотации в Python 3); таким образом, вам потребуется вернуться к парсингу строки документации в качестве последнего канала, если другие подходы не пройдут (конечно, строка документа может отсутствовать, и в этом случае функция останется загадкой).

5 голосов
/ 13 июня 2009

Используйте декоратор для декорирования, например,

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 функцию для вычисления количества аргументов в зависимости от ваших потребностей

2 голосов
/ 13 июня 2009

Это единственный способ, который я могу придумать, который должен быть на 100% эффективным (по крайней мере, в отношении того, определена ли функция пользователем или написана на С) при определении (минимальной) арности функции. Тем не менее, вы должны быть уверены, что эта функция не вызовет никаких побочных эффектов и что она не вызовет ошибку 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 метода класса или экземпляра.

Честно говоря, я бы не использовал эту функцию в производственной среде, если бы точно не знал, какие функции будут вызываться, поскольку в ней много места для глупых ошибок. Это может победить цель.

2 голосов
/ 13 июня 2009

В идеале, вы хотите, чтобы функция 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 () верна. А как насчет вариадных функций? Тебе хоть ответ нужен?

0 голосов
/ 13 июня 2009

Вот еще одна попытка использования метакласса, так как я использую 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()
...