33

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
4

5 に答える 5

51

Pythonの標準ライブラリのモジュールinspectはあなたの友達です-オンラインドキュメントを参照してください! inspect.getargspec(func)4つの項目を持つタプルを返します。args, varargs, varkw, defaultslen(args)は「プライマリアリティ」ですが、アリティは、ある場合とない場合はそれから無限大までの任意の値にすることができ、varargsそうvarkwでない場合はNone一部の引数を省略(およびデフォルト)できます。どのようにそれを単一の数字に変えるか、私を打ち負かします、しかしおそらくあなたは問題にあなたの考えを持っています!-)defaultsNone

これはPythonでコード化された関数に適用されますが、Cでコード化された関数には適用されません。Python C APIには、docstring(またはオプションでPython 3のアノテーション)を介する場合を除いて、Cコード化された関数(組み込みを含む)がイントロスペクションのために署名を公開することはできません。したがって、他のアプローチが失敗した場合は、最後の溝としてdocstring解析にフォールバックする必要があります(もちろん、docstringも欠落している可能性があり、その場合、関数は謎のままになります)。

于 2009-06-13T05:13:14.700 に答える
6

デコレータを使用してメソッドを装飾します。

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次に、ニーズに基づいて引数カウントを計算する関数を実装します

于 2009-06-13T05:21:57.207 に答える
2

理想的には、Pythonファンクターのメソッドとしてarity関数にモンキーパッチを適用することをお勧めします。方法は次のとおりです。

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()関数が正しいことを前提としています。可変個引数関数はどうですか?あなたも答えが欲しいですか?

于 2009-06-13T05:23:51.400 に答える
2

これは、関数の (最小) アリティを決定する際に (少なくとも関数がユーザー定義か C で記述されているかに関して) 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 引数も考慮されません。

正直なところ、愚かなエラーの余地がたくさんあるので、どの関数が呼び出されるかを正確に知っていない限り、本番環境ではこの関数を使用しません。これは目的に反する可能性があります。

于 2009-06-13T16:08:13.503 に答える
0

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()
于 2009-06-13T05:39:34.123 に答える