Finden der Parameter einer Funktion in Python

Ich möchte in der Lage sein, eine Klasse zu fragen,__init__ -Methode, was es Parameter sind. Der einfache Ansatz ist der folgende:

cls.__init__.__func__.__code__.co_varnames[:code.co_argcount]

Das geht aber nicht, wenn die Klasse Dekorateure hat. Es wird die Parameterliste für die vom Dekorateur zurückgegebene Funktion angezeigt. Ich möchte zum Original zurückkehren__init__ -Methode und erhalten Sie diese ursprünglichen Parameter. Im Fall eines Dekorateurs befindet sich die Dekoratorfunktion im Abschluß der vom Dekorator zurückgegebenen Funktion:

cls.__init__.__func__.__closure__[0]

Es ist jedoch komplizierter, wenn der Verschluss andere Dinge enthält, die die Dekorateure von Zeit zu Zeit tun können:

def Something(test):
    def decorator(func):
        def newfunc(self):
            stuff = test
            return func(self)
        return newfunc
    return decorator

def test():
    class Test(object):
        @Something(4)
        def something(self):
            print Test
    return Test

test().something.__func__.__closure__
(<cell at 0xb7ce7584: int object at 0x81b208c>, <cell at 0xb7ce7614: function object at 0xb7ce6994>)

Und dann muss ich mich entscheiden, ob ich die Parameter vom Dekorateur oder die Parameter von der ursprünglichen Funktion haben will. Die vom Dekorateur zurückgegebene Funktion könnte @ hab*args und**kwargs für seine Parameter. Was ist, wenn es mehrere Dekorateure gibt und ich mich entscheiden muss, welchen ich mag?

Was ist der beste Weg, um die Parameter einer Funktion zu finden, auch wenn die Funktion dekoriert ist? Und was ist der beste Weg, um eine Reihe von Dekorateuren auf die dekorierte Funktion zurückzuführen?

Aktualisieren

Hier ist praktisch, wie ich das jetzt tue (die Namen wurden geändert, um die Identität des Angeklagten zu schützen):

import abc
import collections

IGNORED_PARAMS = ("self",)
DEFAULT_PARAM_MAPPING = {}
DEFAULT_DEFAULT_PARAMS = {}

class DICT_MAPPING_Placeholder(object):
    def __get__(self, obj, type):
        DICT_MAPPING = {}
        for key in type.PARAMS:
            DICT_MAPPING[key] = None
        for cls in type.mro():
            if "__init__" in cls.__dict__:
                cls.DICT_MAPPING = DICT_MAPPING
                break
        return DICT_MAPPING

class PARAM_MAPPING_Placeholder(object):
    def __get__(self, obj, type):
        for cls in type.mro():
            if "__init__" in cls.__dict__:
                cls.PARAM_MAPPING = DEFAULT_PARAM_MAPPING
                break
        return DEFAULT_PARAM_MAPPING

class DEFAULT_PARAMS_Placeholder(object):
    def __get__(self, obj, type):
        for cls in type.mro():
            if "__init__" in cls.__dict__:
                cls.DEFAULT_PARAMS = DEFAULT_DEFAULT_PARAMS
                break
        return DEFAULT_DEFAULT_PARAMS

class PARAMS_Placeholder(object):
    def __get__(self, obj, type):
        func = type.__init__.__func__
        # unwrap decorators here
        code = func.__code__
        keys = list(code.co_varnames[:code.co_argcount])
        for name in IGNORED_PARAMS:
            try: keys.remove(name)
            except ValueError: pass
        for cls in type.mro():
            if "__init__" in cls.__dict__:
                cls.PARAMS = tuple(keys)
                break
        return tuple(keys)

class BaseMeta(abc.ABCMeta):
    def __init__(self, name, bases, dict):
        super(BaseMeta, self).__init__(name, bases, dict)
        if "__init__" not in dict:
            return
        if "PARAMS" not in dict:
            self.PARAMS = PARAMS_Placeholder()
        if "DEFAULT_PARAMS" not in dict:
            self.DEFAULT_PARAMS = DEFAULT_PARAMS_Placeholder()
        if "PARAM_MAPPING" not in dict:
            self.PARAM_MAPPING = PARAM_MAPPING_Placeholder()
        if "DICT_MAPPING" not in dict:
            self.DICT_MAPPING = DICT_MAPPING_Placeholder()


class Base(collections.Mapping):
    __metaclass__ = BaseMeta
    """
    Dict-like class that uses its __init__ params for default keys.

    Override PARAMS, DEFAULT_PARAMS, PARAM_MAPPING, and DICT_MAPPING
    in the subclass definition to give non-default behavior.

    """
    def __init__(self):
        pass
    def __nonzero__(self):
        """Handle bool casting instead of __len__."""
        return True
    def __getitem__(self, key):
        action = self.DICT_MAPPING[key]
        if action is None:
            return getattr(self, key)
        try:
            return action(self)
        except AttributeError:
            return getattr(self, action)
    def __iter__(self):
        return iter(self.DICT_MAPPING)
    def __len__(self):
        return len(self.DICT_MAPPING)

print Base.PARAMS
# ()
print dict(Base())
# {}

An diesem Punkt meldet Base uninteressante Werte für die vier Kon- tanten, und die diktierte Version von Instanzen ist leer. Wenn Sie jedoch eine Unterklasse bilden, können Sie eine der vier überschreiben oder dem @ andere Parameter hinzufüge__init__:

class Sub1(Base):
    def __init__(self, one, two):
        super(Sub1, self).__init__()
        self.one = one
        self.two = two

Sub1.PARAMS
# ("one", "two")
dict(Sub1(1,2))
# {"one": 1, "two": 2}

class Sub2(Base):
    PARAMS = ("first", "second")
    def __init__(self, one, two):
        super(Sub2, self).__init__()
        self.first = one
        self.second = two

Sub2.PARAMS
# ("first", "second")
dict(Sub2(1,2))
# {"first": 1, "second": 2}

Antworten auf die Frage(2)

Ihre Antwort auf die Frage