有没有一种通用的方式让一个函数引用它自身?

45
我可以通过以下代码在Python函数内部访问该函数的属性:

我可以通过以下代码在Python函数内部访问该函数的属性:

def aa():
    print aa.__name__
    print aa.__hash__
    # other simliar

然而,如果上述aa()函数是用来编写其他代码的模板,比如说bb(),那么我就必须这样写:

def bb():
    print bb.__name__
    print bb.__hash__
    # other simliar
有没有类似于类方法中的self参数的“指针”,这样我就可以像这样编写代码?
def whatever():
    print self.__name__
    print self.__hash__
    # other simliar

我搜索过发现有人说可以使用类来解决这个问题,但这可能会麻烦到需要重新定义所有现有的函数。是否有其他建议?


6
self 不是一个神奇的关键词,它被明确定义为基于实例的函数的第一个参数。 - Sapph
我很好奇,为什么你需要在许多函数中都加入这个序言?仅仅是为了调试吗? - Beni Cherniavsky-Paskin
@Beni,是的,为了以非常“快速”的方式原型化一个基本模型。 - user478514
4个回答

38

没有通用的方法让函数引用自身。考虑使用装饰器代替。如果你只是想打印有关函数的信息,那么可以很容易地使用装饰器实现:

from functools import wraps
def showinfo(f):
    @wraps(f)
    def wrapper(*args, **kwds):
         print(f.__name__, f.__hash__)
         return f(*args, **kwds)
    return wrapper

@showinfo
def aa():
    pass

如果您确实需要引用该函数,则只需将其添加到函数参数中:

def withself(f):
    @wraps(f)
    def wrapper(*args, **kwds):
        return f(f, *args, **kwds)
    return wrapper

@withself
def aa(self):
      print(self.__name__)
      # etc.

编辑添加备用装饰器:

您还可以编写一个更简单(且可能更快)的装饰器,使包装函数能够正确地使用Python的内省功能:

def bind(f):
    """Decorate function `f` to pass a reference to the function
    as the first argument"""
    return f.__get__(f, type(f))

@bind
def foo(self, x):
    "This is a bound function!"
    print(self, x)


>>> foo(42)
<function foo at 0x02A46030> 42
>>> help(foo)
Help on method foo in module __main__:

foo(self, x) method of builtins.function instance
    This is a bound function!

这利用了 Python 的描述符协议:函数具有一个 __get__ 方法,用于创建绑定方法。装饰器只是使用现有的方法将函数变为其自身的绑定方法。它仅适用于独立函数,如果您想让一个方法能够引用自身,则需要像原始解决方案那样执行某些操作。


正如@akira的回答所解释的那样,函数引用自身的通用方法是使用sys._getframe().f_code.co_name。因此,尽管这个答案可能有用,但它以错误的声明开始,如果对问题有一个正确、更精确和令人满意的答案,就不应该被选为“最佳答案”。 - Max
@Max 抱歉,但我仍然坚持我的说法。globals()[sys._getframe().f_code.co_name]对于嵌套函数或已从声明它的模块中删除的函数是无效的。例如,在装饰函数内部,它将给出包装器而不是原始函数。 - Duncan

19

http://docs.python.org/library/inspect.html 看起来很有前途:

import inspect
def foo():
     felf = globals()[inspect.getframeinfo(inspect.currentframe()).function]
     print felf.__name__, felf.__doc__

你还可以使用 sys 模块来获取当前函数的名称:

import sys
def bar():
     felf = globals()[sys._getframe().f_code.co_name]
     print felf.__name__, felf.__doc__

1
比仅仅显式地引用函数要做的工作多得多。 - Florian Mayer
@FLorian Mayer:那是真的,这就是为什么我支持 @Duncan 的 self 装饰器。 - akira
2
这正是所要求的内容...虽然它并不是直接简单的,但我认为装饰版本并不那么清晰。(个人观点) - tom stratton
这个答案应该被选为“最佳答案”,因为它确切地回答了问题,而当前的“最佳答案”则以错误的断言开始,即函数没有通用的方法可以获取对自身的引用:上面的最后一段代码证明了这一点!(也就是说:sys._getframe().f_code.co_name 是函数的名称) - Max
@Florian Mayer:这并不是“更多的工作”,而恰好是所需的。一方面,在平均情况下(对于所有可能的函数名称),与打出函数名称相比,此代码极短。[没有最大长度=>无限平均长度] 另一方面,当某个函数不再分配给其最初分配的同一变量(也称为函数名称)时,问题显然变得最相关。(它可能已被复制到不同的变量中,并重新使用原始名称,例如在函数重载的上下文中。) - Max

0

你至少可以在第一行写上 self = bb,这样当你改变函数名时,只需要修改这一行,而不是每个引用都要修改。

我的代码编辑器也会像对待类一样突出显示变量 self


-5

怎么样用一个快捷方式来创建自己的“self”名称,像这样:

>>> def f():
...     self = f
...     print "My name is ", self.__name__, "and I am", self.__hash__
...
>>> f()
My name is  f and I am <method-wrapper '__hash__' of function object at 0x00B50F30>
>>> x = f
>>> x()
My name is  f and I am <method-wrapper '__hash__' of function object at 0x00B50F30>
>>>

这与 OP 引用 f.__name__ 有什么不同吗? - Andy Hayden
所以x的名字是f...这根本没有回答问题 - Sheena

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接