Как узнать арность метода в Python

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

person Federico Builes    schedule 13.06.2009    source источник
comment
Что не так с help ()? Что не делает то, что вам нужно?   -  person S.Lott    schedule 13.06.2009
comment
Мне нужно использовать его программно, поэтому мне не хочется анализировать текст, чтобы вернуть число.   -  person Federico Builes    schedule 13.06.2009
comment
В чем вы утверждаете арность def a( *args ):?   -  person S.Lott    schedule 14.06.2009


Ответы (5)


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

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

person Alex Martelli    schedule 13.06.2009
comment
Это работает для моих методов, которые я определяю, но не для встроенных, см. Inspect.getargspec (str.split) - person Federico Builes; 13.06.2009
comment
@Federico, вы правы: ничто в Python C API не позволяет C-кодированным функциям (которые являются встроенными) выставлять свою подпись для интроспекции, кроме как через их строку документации (или, возможно, через аннотации в Python 3); так что, хотите вы этого или нет, вам нужно будет вернуться к синтаксическому анализу строки документации в качестве последней черты, если другие подходы потерпят неудачу (конечно, строка документации также может отсутствовать). - person Alex Martelli; 13.06.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 для вычисления количества аргументов в соответствии с вашими потребностями.

person Anurag Uniyal    schedule 13.06.2009
comment
на самом деле вы можете использовать метакласс для украшения всех методов класса вместо того, чтобы делать это вручную - person Anurag Uniyal; 13.06.2009

Это единственный способ, который я могу представить, который должен быть на 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 метода класса или экземпляра.

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

person Jason Baker    schedule 13.06.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 () верна. А как насчет вариативных функций? Тогда ты вообще хочешь получить ответ?

person Nikhil Chelliah    schedule 13.06.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()
person Anurag Uniyal    schedule 13.06.2009