12

[原始版本中的代码严重混乱。即使在我修复了代码之后,帖子中仍然存在几个非常令人困惑的错别字。我相信我最终也修复了所有这些问题。深表歉意。]

下面的两个调用会alias产生不同的输出,因为与变量关联的对象my_own_id在两个调用之间会发生变化:

>>> def my_own_id():
...     me = my_own_id
...     return id(me)
... 
>>> alias = my_own_id
>>> alias()
4301701560
>>> my_own_id = None
>>> alias()
4296513024

我可以me在定义中分配什么,my_own_id以便其输出在变量的后续重新定义中保持不变my_own_id?(IOW,所以内部me变量总是引用同一个函数对象?)

(我可以得到当前帧(用inspect.currentframe()),但它只包含对当前代码对象的引用,而不是对当前函数的引用。)

PS这个问题的动机只是为了更好地了解Python。

4

7 回答 7

3

似乎引用my_own_id'my_own_id'在全局命名空间字典中查找,因此它始终是函数定义中使用的名称。由于该名称可以分配给不同的值,因此检索到的值也可以更改。如果您创建me一个默认参数,您可以在函数定义时将其分配给函数本身,以保持对实际函数的引用。

您可以使用这个装饰器,它隐式地将原始函数本身作为第一个参数传递。

>>> from functools import wraps
>>> def save_id(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            return func(func, *args, **kwargs)
        return wrapper


>>> @save_id
def my_own_id(me): # me is passed implicitly by save_id
    return id(me)

>>> alias = my_own_id
>>> alias()
40775152
>>> my_own_id = 'foo'
>>> alias()
40775152
于 2012-08-10T20:37:50.490 回答
2

实际上,如果您只依赖函数名,如果该名称在全局变量空间中被覆盖(在定义函数的模块中),则使用函数名的引用将失败

更简单、更易于维护的方法是为此编写一个装饰器,它将提供一个nonlocal包含对函数本身的引用的变量。

from functools import wraps

def know_thyself(func):
   @wraps(func):
   def new_func(*args, **kwargs):
        my_own_id = func
        return func(*args, **kwargs)
   return new_func

并可用作:

>>> @know_thyself
... def my_own_id():
...     me = my_own_id
...     return id(me)
... 

还有另一种可能的方法,远非如此干净,使用帧内省并重新使用相同的目标代码重新构建一个新函数。我在这篇关于 Python 中的自引用 lambda 表达式的帖子中使用了这个:http: //metapython.blogspot.com.br/2010/11/recursive-lambda-functions.html

于 2012-08-10T19:58:21.013 回答
1
import inspect
def _this_fn():
    try:
        frame = inspect.currentframe().f_back
        code = frame.f_code
        return frame.f_globals[code.co_name]
    finally:
        del code
        del frame

def myfunc(*parms):
    print _this_fn()

>>> myfunc(1)
<function myfunc at 0x036265F0>
>>> myfunc
<function myfunc at 0x036265F0>
于 2013-02-18T09:55:28.130 回答
1

好吧,如果您不介意调用一个函数(将所需的函数放入全局范围),您可以包装该函数以保护其定义:

>>> def be_known():
...     global my_own_id
...     def _my_own_id():
...         return id(_my_own_id)
...     my_own_id = _my_own_id
... 
>>> be_known()
>>> my_own_id()
140685505972568
>>> alias, my_own_id = my_own_id, None
>>> alias()
140685505972568

请注意,受保护的函数必须使用非本地名称而不是全局名称来调用自身。

于 2012-08-10T20:11:20.160 回答
1

装饰器方法可能是最好的方法。这里还有一些好玩的:

  1. 劫持函数参数之一以提供静态变量。

    def fn(fnid=None):
        print "My id:", fnid
    fn.func_defaults = (id(fn),)
    
  2. 这里有几种获取当前函数的方法:Python code to get current function into a variable? ; 其中大部分涉及在各种地方搜索 currentframe.f_code。这些工作无需对原始功能进行任何修改。

于 2012-08-10T20:15:58.227 回答
0

Luke有一个想法,但似乎没有开发它:使用可变的默认参数来保存函数对象中的值。默认参数值仅在定义函数时评估一次,之后保留其先前的值。

>>> def my_own_id(me=[None]):
    if not me[0]:
        me[0] = my_own_id
    return id(me[0])

>>> alias = my_own_id
>>> alias()
40330928
>>> my_own_id = None
>>> alias()
40330928

这需要您小心,不要使用参数调用函数。

于 2012-08-10T20:59:35.853 回答
0

这是由于范围

>>> def foo():
...     x = foo
...     print x
... 
>>> foo()
<function foo at 0x10836e938>
>>> alias = foo
>>> alias()
<function foo at 0x10836e938>
>>> foo = None
>>> alias()
None
>>> foo = []
>>> alias()
[]
>>> del foo
>>> alias()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in foo
NameError: global name 'foo' is not defined
>>> 
于 2012-08-10T20:00:31.917 回答