Нахождение параметров функции в Python

Я хочу смочь спросить класс __init__ метод, что это - параметры. Простой подход следующий:

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

Однако это не будет работать, если класс будет иметь каких-либо декораторов. Это даст список параметров для функции, возвращенной декоратором. Я хочу перейти к оригиналу __init__ метод и получает те исходные параметры. В случае декоратора функция декоратора будет найденной в закрытии функции, возвращенной декоратором:

cls.__init__.__func__.__closure__[0]

Однако это более сложно, если существуют другие вещи в закрытии, которое декораторы могут время от времени делать:

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

И затем я должен решить, хочу ли я к параметрам от декоратора или параметрам от исходной функции. Функция, возвращенная декоратором, могла иметь *args и **kwargs для его параметров. Что, если существует несколько декораторов и я должен решить, который является тем, о котором я забочусь?

Таким образом, что лучший способ состоит в том, чтобы найти параметрами функции, даже когда функция может быть украшена? Кроме того, что лучший способ состоит в том, чтобы спуститься по цепочке декораторов назад к украшенной функции?

Обновление:

Вот эффективно, как я делаю это прямо сейчас (имена изменились для защиты идентификационных данных обвиняемого):

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())
# {}

В этой точке Основа сообщает о неинтересных значениях для этих четырех констант, и dict версия экземпляров пуста. Однако, если Вы разделяете Вас на подклассы, может переопределить любой из этих четырех, или можно включать другие параметры в __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}
5
задан efotinis 19 November 2010 в 21:42
поделиться