在Python中查找函数的参数 [英] Finding a function's parameters in Python

查看:215
本文介绍了在Python中查找函数的参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想要一个类的 __ init __ 方法,它的参数是什么。直接的方法如下:

I want to be able to ask a class's __init__ method what it's parameters are. The straightforward approach is the following:

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

但是,如果类有装饰器,这将不工作。它将给出装饰器返回的函数的参数列表。我想下来到原来的 __ init __ 方法,并得到那些原始的参数。在装饰器的情况下,装饰器函数将在由装饰器返回的函数的闭包中找到:

However, that won't work if the class has any decorators. It will give the parameter list for the function returned by the decorator. I want to get down to the original __init__ method and get those original parameters. In the case of a decorator, the decorator function is going to be found in the closure of the function returned by the decorator:

cls.__init__.__func__.__closure__[0]

但是,在装饰者可以不时做的事情:

However, it is more complicated if there are other things in the closure, which decorators may do from time to time:

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 作为其参数。如果有多个装饰器,我必须决定哪一个是我关心的?

And then I have to decide if I want to the parameters from decorator or the parameters from the original function. The function returned by the decorator could have *args and **kwargs for its parameters. What if there are multiple decorators and I have to decide which is the one I care about?

那么什么是最好的方法来查找函数的参数,该功能可以装饰吗?

更新:

这里是我现在这样做的名称(名称已更改以保护被告的身份):

Here is effectively how I am doing this right now (names have been changed to protect the identity of the accused):

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

但是,如果你的子类可以重写任何四个,或者你可以包括其他参数到 __ init __

At this point Base reports uninteresting values for the four contants and the dict version of instances is empty. However, if you subclass you can override any of the four, or you can include other parameters to the __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}


推荐答案

p>考虑这个装饰器:

Consider this decorator:

def rickroll(old_function):
    return lambda junk, junk1, junk2: "Never Going To Give You Up"

class Foo(object):
    @rickroll
    def bar(self, p1, p2):
        return p1 * p2

print Foo().bar(1, 2)

rickroll装饰器采用bar方法,丢弃它,用一个新的函数替换它,忽略它的不同命名(可能还有编号!)参数,而不是从经典歌曲返回一行。

In it, the rickroll decorator takes the bar method, discards it, replaces it with a new function that ignores its differently-named (and possibly numbered!) parameters and instead returns a line from a classic song.

没有对原始函数的进一步引用,垃圾收集器可以随时喜欢它。

There are no further references to the original function, and the garbage collector can come and remove it any time it likes.

在这种情况下,我不能看看如何找到参数名称p1和p2。在我的理解,甚至Python解释器本身不知道他们以前被称为。

In such a case, I cannot see how you could find the parameter names p1 and p2. In my understanding, even the Python interpreter itself has no idea what they used to be called.

这篇关于在Python中查找函数的参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆