函数如何访问自己的属性?

发布于 2024-09-06 16:58:48 字数 432 浏览 4 评论 0原文

是否可以从函数作用域内访问 python 函数对象属性?

例如

def f():
    return SOMETHING

f._x = "foo"
f()           # -> "foo"

,如果我们想要返回 _x 属性内容“foo”,那么现在我们必须要做什么?如果有可能(简单地)

谢谢

更新:

我也想要以下工作:

g = f
del f
g()          # -> "foo"

更新2:

声明这是不可能的(如果是这样的话),为什么,比提供一种如何伪造它的方法更令人满意,例如使用与函数不同的对象

is it possible to access the python function object attributes from within the function scope?

e.g. let's have

def f():
    return SOMETHING

f._x = "foo"
f()           # -> "foo"

now, what SOMETHING has to be, if we want to have the _x attribute content "foo" returned? if it's even possible (simply)

thanks

UPDATE:

i'd like the following work also:

g = f
del f
g()          # -> "foo"

UPDATE 2:

Statement that it is not possible (if it is the case), and why, is more satisfying than providing a way how to fake it e.g. with a different object than a function

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(16

心的憧憬 2024-09-13 16:58:49

这使用了一些黑客方法,但鉴于它也适用于 g() 调用,因此它可能是迄今为止最正确的方法。它之所以有效,是因为它依赖于 dis 模块,作为快捷方式。

它看起来比实际情况更黑客化,部分原因是 dis.disassemble() 调用打印到 stdout,所以我将其重定向到 StringIO。我使用 disassemble() 来突出显示最后一条指令(在其中添加 print text 行以查看其外观),这样可以更轻松地获取上一条指令LOAD_NAME 及其使用的变量。

可以使用更干净的字节码检查库来完成此操作,而无需使用 dis 模块,但这证明了这是可能的。这可能不是最可靠的方法,但也许它在大多数情况下都有效。我还没有花足够的时间深入研究 Python 内部结构或字节码来了解大多数 CALL_FUNCTION 字节码是否紧接着正则表达式技巧会挑选出的指令。

import inspect
import dis
import re
import sys
import StringIO

def f():
    caller = inspect.stack()[1][0]
    sys.stdout = StringIO.StringIO()
    dis.disassemble(caller.f_code, caller.f_lasti)
    text = sys.stdout.getvalue()
    sys.stdout = sys.__stdout__
    match = re.search(r'LOAD_NAME.*\((.*?)\)\s+-->', text)
    name = match.group(1)
    try:
        func = caller.f_locals[name]
    except KeyError:
        func = caller.f_globals[name]
    return func._x

f._x = 'foo'
print 'call f():', f()
g = f
del f
print 'call g():', g()

这会生成以下输出:

call f(): foo
call g(): foo

This uses a bit of a hackish approach, but it's possibly the most correct so far given that it works with the g() call as well. It works because it's relying on whatever bytecode inspection is performed by the dis module, as a shortcut.

It looks more hackish than it really is partly because the dis.disassemble() call prints to stdout, so I redirect that into a StringIO. I use disassemble() for its feature of highlighting the last instruction (add a print text line in there to see how it looks) and that makes it easier to grab the previous LOAD_NAME and the variable it used.

It would be possible to use a cleaner bytecode inspection library to do this without using the dis module at all, but this proves that it's possible. This might not be the most robust approach, but then again maybe it will work in most cases. I haven't spent enough time poking into Python internals or bytecode to know whether most CALL_FUNCTION bytecodes are preceded immediately by instructions that the regex trick would pick out.

import inspect
import dis
import re
import sys
import StringIO

def f():
    caller = inspect.stack()[1][0]
    sys.stdout = StringIO.StringIO()
    dis.disassemble(caller.f_code, caller.f_lasti)
    text = sys.stdout.getvalue()
    sys.stdout = sys.__stdout__
    match = re.search(r'LOAD_NAME.*\((.*?)\)\s+-->', text)
    name = match.group(1)
    try:
        func = caller.f_locals[name]
    except KeyError:
        func = caller.f_globals[name]
    return func._x

f._x = 'foo'
print 'call f():', f()
g = f
del f
print 'call g():', g()

This generates the following output:

call f(): foo
call g(): foo
难忘№最初的完美 2024-09-13 16:58:49

使用类而不是函数并滥用 __new__ 方法使类可作为函数调用怎么样?由于 __new__ 方法获取类名作为第一个参数,因此它可以访问所有类属性,

class f(object):
        def __new__(cls, x):
            print cls.myattribute
            return x

就像在this

f.myattribute = "foo"
f(3)
foo
3

一样,你可以这样做。

g=f
f=None
g(3)
foo
3

问题是,即使对象的行为像函数,它不是。因此 IDE 无法为您提供签名。

How about using a class instead of a function and abusing the __new__ method to make the class callable as a function? Since the __new__ method gets the class name as the first parameter, it can access all the class attributes

like in

class f(object):
        def __new__(cls, x):
            print cls.myattribute
            return x

this works as in

f.myattribute = "foo"
f(3)
foo
3

then you can do

g=f
f=None
g(3)
foo
3

The issue is that even if the object behaves like a function, it is not. Hence IDEs fail to provide you with the signature.

赠我空喜 2024-09-13 16:58:49

实现此目的的另一种方法是在另一个函数内定义该函数,并让外部函数返回内部函数。然后内部函数可以通过闭包访问自身。这是一个简单的例子:

def makeFunc():
    def f():
        return f._x
    return f

那么:

>>> f = makeFunc()
>>> f._x = "foo"
>>> f()
'foo'
>>> g = f
>>> del f
>>> g()
'foo'

Another way to accomplish this is to define the function inside another function, and have the outer function return the inner one. Then the inner function can access itself via a closure. Here's a simple example:

def makeFunc():
    def f():
        return f._x
    return f

Then:

>>> f = makeFunc()
>>> f._x = "foo"
>>> f()
'foo'
>>> g = f
>>> del f
>>> g()
'foo'
明天过后 2024-09-13 16:58:49

如果只需要一种方法,但您想要一个具有共享类状态和单独实例状态的轻量级类,您可以尝试像这样的闭包模式:

# closure example of light weight object having class state,
#    local state, and single method
# This is a singleton in the sense that there is a single class
#    state (see Borg singleton pattern notebook)
#    BUT combined with local state
# As long as only one method is needed, this one way to do it
# If a full class singleton object is needed with multiple 
#    methods, best look at one of the singleton patterns

def LW_Object_Factory(localState):

    # class state - doesn't change
    lwof_args = (1, 2, 3)
    lwof_kwargs =  {'a': 4, 'b': 5}

    # local instance - function object - unique per
    # instantiation sharing class state
    def theObj(doc, x):
        print doc, 'instance:'
        print '\tinstance class state:\n\t\targs -', \
              lwof_args, ' kwargs -', lwof_kwargs
        print '\tinstance locals().items():'
        for i in locals().items():
            print '\t\t', i
        print '\tinstance argument x:\n\t\t', '"{}"'.format(x)
        print '\tinstance local state theObj.foo:\n\t\t',\
              '"{}"'.format(theObj.foo)
        print ''

    # setting local state from argument
    theObj.foo = localState

    return(theObj)

lwo1 = LW_Object_Factory('foo in local state for first')
lwo2 = LW_Object_Factory('foo in local state for second')

# prove each instance is unique while sharing class state
print 'lwo1 {} distinct instance from lwo2\n'\
      .format(id(lwo1) <> id(lwo2) and "IS" or "IS NOT")

# run them
lwo1('lwo1', 'argument lwo1') 
lwo2('lwo2', 'argument lwo2')

If there is only one method needed but you want a light-weight class with shared class state plus individual instance state, you might try the closure pattern like this:

# closure example of light weight object having class state,
#    local state, and single method
# This is a singleton in the sense that there is a single class
#    state (see Borg singleton pattern notebook)
#    BUT combined with local state
# As long as only one method is needed, this one way to do it
# If a full class singleton object is needed with multiple 
#    methods, best look at one of the singleton patterns

def LW_Object_Factory(localState):

    # class state - doesn't change
    lwof_args = (1, 2, 3)
    lwof_kwargs =  {'a': 4, 'b': 5}

    # local instance - function object - unique per
    # instantiation sharing class state
    def theObj(doc, x):
        print doc, 'instance:'
        print '\tinstance class state:\n\t\targs -', \
              lwof_args, ' kwargs -', lwof_kwargs
        print '\tinstance locals().items():'
        for i in locals().items():
            print '\t\t', i
        print '\tinstance argument x:\n\t\t', '"{}"'.format(x)
        print '\tinstance local state theObj.foo:\n\t\t',\
              '"{}"'.format(theObj.foo)
        print ''

    # setting local state from argument
    theObj.foo = localState

    return(theObj)

lwo1 = LW_Object_Factory('foo in local state for first')
lwo2 = LW_Object_Factory('foo in local state for second')

# prove each instance is unique while sharing class state
print 'lwo1 {} distinct instance from lwo2\n'\
      .format(id(lwo1) <> id(lwo2) and "IS" or "IS NOT")

# run them
lwo1('lwo1', 'argument lwo1') 
lwo2('lwo2', 'argument lwo2')
静谧 2024-09-13 16:58:49

这是一个可能比 func_defaults 想法更糟糕的策略,但仍然很有趣。这很老套,但我想不出它有什么实际问题。

我们可以实现一个函数,该函数可以使用单个 __new__ 方法(​​通常创建该类的新对象的方法)将自身引用为类。

class new:
    """Returns True the first time an argument is passed, else False."""
    seen = set()
    def __new__(cls, x):
        old = x in cls.seen
        cls.seen.add(x)
        return not old

def main():
    print(new(1))  # True
    print(new(2))  # True
    print(new(2))  # false
    is_new = new
    print(is_new(1))  # False

也许这种模式对于日志记录功能很有用......

class log_once:
    """Log a message if it has not already been logged.

    Args:
        msg: message to be logged
        printer: function to log the message
        id_: the identifier of the msg determines whether the msg
          has already been logged. Defaults to the msg itself.

    This is useful to log a condition that occurs many times in a single
    execution. It may be relevant that the condition was true once, but
    you did not need to know that it was true 10000 times, nor do you
    desire evidence to that effect to fill your terminal screen.
    """
    seen = set()
    def __new__(cls, msg, printer=print, id_=None):
        id_ = id_ or msg
        if id_ not in cls.seen:
            cls.seen.add(id_)
            printer(id_)


if __name__ == '__main__':
    log_once(1)
    log_once(1)
    log_once(2)

Here is a strategy that is probably worse than the func_defaults idea, but is interesting nonetheless. It's hacky but I can't think of anything practically wrong with it.

We can implement a function that can refer to itself as a class with a single __new__ method (the method that normally creates a new object of that class).

class new:
    """Returns True the first time an argument is passed, else False."""
    seen = set()
    def __new__(cls, x):
        old = x in cls.seen
        cls.seen.add(x)
        return not old

def main():
    print(new(1))  # True
    print(new(2))  # True
    print(new(2))  # false
    is_new = new
    print(is_new(1))  # False

Perhaps this pattern could be useful for a logging function...

class log_once:
    """Log a message if it has not already been logged.

    Args:
        msg: message to be logged
        printer: function to log the message
        id_: the identifier of the msg determines whether the msg
          has already been logged. Defaults to the msg itself.

    This is useful to log a condition that occurs many times in a single
    execution. It may be relevant that the condition was true once, but
    you did not need to know that it was true 10000 times, nor do you
    desire evidence to that effect to fill your terminal screen.
    """
    seen = set()
    def __new__(cls, msg, printer=print, id_=None):
        id_ = id_ or msg
        if id_ not in cls.seen:
            cls.seen.add(id_)
            printer(id_)


if __name__ == '__main__':
    log_once(1)
    log_once(1)
    log_once(2)
筱果果 2024-09-13 16:58:49

只需在闭包中定义您的函数:

def generate_f():
    def f():
        return f.x
    return f

f = generate_f()

f.x = 314
g = f

del f
print g()
# => 314

Just define your function inside a closure:

def generate_f():
    def f():
        return f.x
    return f

f = generate_f()

f.x = 314
g = f

del f
print g()
# => 314
段念尘 2024-09-13 16:58:49

我非常喜欢这个。

from functools import update_wrapper

def dictAsGlobals(f):
    nf = type(f)(f.__code__, f.__dict__, f.__name__, f.__defaults__, f.__closure__)
    try: nf.__kwdefaults__ = f.__kwdefaults__
    except AttributeError: pass
    nf.__dict__ = f.__dict__
    nf.__builtins__ = f.__globals__["__builtins__"]
    return update_wrapper(nf, f)

@dictAsGlobals
def f():
    global timesCalled
    timesCalled += 1
    print(len.__doc__.split("\n")[0])
    return factor0 * factor1

vars(f).update(timesCalled = 0, factor0 = 3, factor1 = 2)

print(f())
print(f())
print(f.timesCalled)

I like this alot.

from functools import update_wrapper

def dictAsGlobals(f):
    nf = type(f)(f.__code__, f.__dict__, f.__name__, f.__defaults__, f.__closure__)
    try: nf.__kwdefaults__ = f.__kwdefaults__
    except AttributeError: pass
    nf.__dict__ = f.__dict__
    nf.__builtins__ = f.__globals__["__builtins__"]
    return update_wrapper(nf, f)

@dictAsGlobals
def f():
    global timesCalled
    timesCalled += 1
    print(len.__doc__.split("\n")[0])
    return factor0 * factor1

vars(f).update(timesCalled = 0, factor0 = 3, factor1 = 2)

print(f())
print(f())
print(f.timesCalled)
桜花祭 2024-09-13 16:58:49

很抱歉回复晚了,但我只是偶然发现了这一点。我不得不说“g”的工作方式是非Pythonic的。在函数内部,名称“f”指的是调用函数时全局变量的值。鉴于此,请考虑以下事项:

def f():
    print(f)
f, g = 42, f
g()  # prints 42
del f
g()  # raises an exception 

希望没有人认为这是不正确的行为。鉴于这一事实,我只投票支持任何需要在函数内使用不同变量名称(例如“self”)的答案。

Sorry for the late reply but I just stumbled upon this. I would have to argue that the way that “g” is asked to work is non-Pythonic. Inside function, the name “f“ refers to the value of a global variable at the time the function is called. Given that, consider the following:

def f():
    print(f)
f, g = 42, f
g()  # prints 42
del f
g()  # raises an exception 

Hopefully, no one argues that this is incorrect behavior. Given that fact, I cam only vote for any answer that requires the use of a different variable name (e.g. “self”) inside the function.

遗心遗梦遗幸福 2024-09-13 16:58:48

解决方案

使函数的默认参数之一成为对函数本身的引用。

def f(self):
    return self.x
f.func_defaults = (f,)

用法示例:

>>> f.x = 17
>>> b = f
>>> del f
>>> b()
17

解释

原始发布者想要一个不需要全局名称查找的解决方案。简单的解决方案

def f():
    return f.x

在每次调用时都执行全局变量f的查找,这不满足要求。如果 f 被删除,则该函数将失败。更复杂的 inspect 提案也以同样的方式失败。

我们想要的是执行早期绑定并将绑定引用存储在对象本身内。从概念上讲,以下是我们正在做的事情:

def f(self=f):
    return self.x

在上面,self 是一个局部变量,因此不执行全局查找。但是,我们不能按原样编写代码,因为当我们尝试将 self 的默认值绑定到它时,f 尚未定义。相反,我们在定义 f 后设置默认值。

装饰器

这是一个简单的装饰器可以为您完成此操作。请注意,self 参数必须位于最后,这与 self 位于最前面的方法不同。这也意味着如果任何其他参数采用默认值,则必须给出默认值。

def self_reference(f):
    f.func_defaults = f.func_defaults[:-1] + (f,)
    return f

@self_reference
def foo(verb, adverb='swiftly', self=None):
    return '%s %s %s' % (self.subject, verb, adverb)

例子:

>>> foo.subject = 'Fred'
>>> bar = foo
>>> del foo
>>> bar('runs')
'Fred runs swiftly'

Solution

Make one of the function's default arguments be a reference to the function itself.

def f(self):
    return self.x
f.func_defaults = (f,)

Example usage:

>>> f.x = 17
>>> b = f
>>> del f
>>> b()
17

Explanation

The original poster wanted a solution that does not require a global name lookup. The simple solution

def f():
    return f.x

performs a lookup of the global variable f on each call, which does not meet the requirements. If f is deleted, then the function fails. The more complicated inspect proposal fails in the same way.

What we want is to perform early binding and store the bound reference within the object itself. The following is conceptually what we are doing:

def f(self=f):
    return self.x

In the above, self is a local variable, so no global lookup is performed. However, we can't write the code as-is, because f is not yet defined when we try to bind the default value of self to it. Instead, we set the default value after f is defined.

Decorator

Here's a simple decorator to do this for you. Note that the self argument must come last, unlike methods, where self comes first. This also means that you must give a default value if any of your other arguments take a default value.

def self_reference(f):
    f.func_defaults = f.func_defaults[:-1] + (f,)
    return f

@self_reference
def foo(verb, adverb='swiftly', self=None):
    return '%s %s %s' % (self.subject, verb, adverb)

Example:

>>> foo.subject = 'Fred'
>>> bar = foo
>>> del foo
>>> bar('runs')
'Fred runs swiftly'
何以畏孤独 2024-09-13 16:58:48

你可以只使用一个类来做到这一点

>>> class F(object):
...     def __call__(self, *args, **kw):
...         return self._x
... 
>>> f=F()
>>> f._x = "foo"
>>> f()
'foo'
>>> g=f
>>> del f
>>> g()
'foo'

You could just use a class to do this

>>> class F(object):
...     def __call__(self, *args, **kw):
...         return self._x
... 
>>> f=F()
>>> f._x = "foo"
>>> f()
'foo'
>>> g=f
>>> del f
>>> g()
'foo'
街角迷惘 2024-09-13 16:58:48

好吧,让我们看看函数是什么:

>>> def foo():
...     return x
... 
>>> foo.x = 777
>>> foo.x
777
>>> foo()
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
  File "<interactive input>", line 2, in foo
NameError: global name 'x' is not defined
>>> dir(foo)
['__call__', '__class__', '__delattr__', '__dict__', '__doc__', '__get__', 
'__getattribute__', '__hash__', '__init__', '__module__', '__name__', '__new__', 
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', 
'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 
'func_globals', 'func_name', 'x']
>>> getattr(foo, 'x')
777

啊哈!因此,该属性已添加到函数对象中,但它不会看到它,因为它正在寻找全局 x

我们可以尝试抓住函数执行的框架并尝试查看那里有什么(本质上是 Anthony Kong 建议的,但没有 inspect 模块):

>>> def foo():
...     import sys
...     return sys._getframe()
... 
>>> fr = foo()
>>> dir(fr)
['__class__', '__delattr__', '__doc__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', 'f_back', 'f_builtins', 'f_code', 'f_exc_traceback', 'f_exc_type', 'f_exc_value', 'f_globals', 'f_lasti', 'f_lineno', 'f_locals', 'f_restricted', 'f_trace']
>>> fr.f_locals
{'sys': <module 'sys' (built-in)>}
>>> fr.f_code
<code object foo at 01753020, file "<interactive input>", line 1>
>>> fr.f_code.co_code
'd\x01\x00d\x00\x00k\x00\x00}\x00\x00|\x00\x00i\x01\x00\x83\x00\x00S'
>>> fr.f_code.co_name
'foo'

啊哈!那么也许我们可以从代码块的名称中获取函数的名称,然后以迂回的方式查找属性?果然:

>>> getattr(fr.f_globals[fr.f_code.co_name], 'x')
777
>>> fr.f_globals[fr.f_code.co_name].x
777
>>> def foo():
...     import sys
...     frm = sys._getframe()
...     return frm.f_globals[frm.f_code.co_name].x
... 
>>> foo.x=777
>>> foo()
777

太好了!但它能忍受原有功能的重命名和删除吗?

>>> g = foo
>>> g.func_name
'foo'
>>> g.func_code.co_name
'foo'

啊,非常怀疑。函数对象及其代码对象仍然坚持称为 foo。果然,问题就在这里:

>>> g.x
777
>>> g.x=888
>>> foo.x
888
>>> g()
888
>>> del foo
>>> g()
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
  File "<interactive input>", line 4, in foo
KeyError: 'foo'

当当!所以一般来说它不能通过执行框架的内省来完成。问题似乎在于函数对象代码对象之间存在差异 - 代码对象是执行的对象,并且只是一个属性func_code函数对象的属性,因此无法访问 func_dict 属性,其中我们的属性 x 是:

>>> g
<function foo at 0x0173AE30>
>>> type(g)
<type 'function'>
>>> g.func_code
<code object foo at 017532F0, file "<interactive input>", line 1>
>>> type(g.func_code)
<type 'code'>
>>> g.func_dict
{'x': 888}

当然,您还可以采取其他诡计,使其看起来像函数 - 特别是类定义的技巧......但这本身不是一个函数。这完全取决于您真正需要做什么。

Well, let's look at what function is:

>>> def foo():
...     return x
... 
>>> foo.x = 777
>>> foo.x
777
>>> foo()
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
  File "<interactive input>", line 2, in foo
NameError: global name 'x' is not defined
>>> dir(foo)
['__call__', '__class__', '__delattr__', '__dict__', '__doc__', '__get__', 
'__getattribute__', '__hash__', '__init__', '__module__', '__name__', '__new__', 
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', 
'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 
'func_globals', 'func_name', 'x']
>>> getattr(foo, 'x')
777

Aha! So the attribute was added to the function object but it won't see it because it is looking for global x instead.

We can try to grab the frame of the function execution and try to look what's there (essentially what Anthony Kong suggested but w/o inspect module):

>>> def foo():
...     import sys
...     return sys._getframe()
... 
>>> fr = foo()
>>> dir(fr)
['__class__', '__delattr__', '__doc__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', 'f_back', 'f_builtins', 'f_code', 'f_exc_traceback', 'f_exc_type', 'f_exc_value', 'f_globals', 'f_lasti', 'f_lineno', 'f_locals', 'f_restricted', 'f_trace']
>>> fr.f_locals
{'sys': <module 'sys' (built-in)>}
>>> fr.f_code
<code object foo at 01753020, file "<interactive input>", line 1>
>>> fr.f_code.co_code
'd\x01\x00d\x00\x00k\x00\x00}\x00\x00|\x00\x00i\x01\x00\x83\x00\x00S'
>>> fr.f_code.co_name
'foo'

Aha! So maybe we can get the name of the function from the name of the code block and then look in round-about way for the attribute? Sure enough:

>>> getattr(fr.f_globals[fr.f_code.co_name], 'x')
777
>>> fr.f_globals[fr.f_code.co_name].x
777
>>> def foo():
...     import sys
...     frm = sys._getframe()
...     return frm.f_globals[frm.f_code.co_name].x
... 
>>> foo.x=777
>>> foo()
777

That's great! But would it stand the renaming and deletion of original function?

>>> g = foo
>>> g.func_name
'foo'
>>> g.func_code.co_name
'foo'

Ah, very doubtful. The function object and its code object still insist they are called foo. Sure enough, here is where it breaks:

>>> g.x
777
>>> g.x=888
>>> foo.x
888
>>> g()
888
>>> del foo
>>> g()
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
  File "<interactive input>", line 4, in foo
KeyError: 'foo'

Dang! So in general it can't be done through introspection via the execution frames. The problems seems to be that there is a difference between function object and code object - code objects are what is executed and is just one attribute func_code of the function-object and as such has no access to the func_dict attribute, where our attribute x is:

>>> g
<function foo at 0x0173AE30>
>>> type(g)
<type 'function'>
>>> g.func_code
<code object foo at 017532F0, file "<interactive input>", line 1>
>>> type(g.func_code)
<type 'code'>
>>> g.func_dict
{'x': 888}

There is of course other chicanery you can do so that it seems as function - in particular the trick with class definition... but that is not a function per se. It all depends on what do you really need to do with that.

左岸枫 2024-09-13 16:58:48

作为解决方法,您可以使用工厂函数来修复您的范围:

def factory():
    def inner():
        print inner.x
    return inner


>>> foo=factory()
>>> foo.x=11
>>> foo()
11
>>> bar = foo
>>> del foo
>>> bar()
11

As a workaround you could use a factory function to fix your scope:

def factory():
    def inner():
        print inner.x
    return inner


>>> foo=factory()
>>> foo.x=11
>>> foo()
11
>>> bar = foo
>>> del foo
>>> bar()
11
笑忘罢 2024-09-13 16:58:48

我怀疑这是实现此目的的最佳方法,但您可以通过在方法中使用方法名称来访问属性:

>>> def foo():
...   print foo.x
... 
>>> foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in foo
AttributeError: 'function' object has no attribute 'x'
>>> foo.x = 5
>>> foo()
5

I doubt this is the best way to accomplish this, but you can access the attributes by using the method's name within the method:

>>> def foo():
...   print foo.x
... 
>>> foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in foo
AttributeError: 'function' object has no attribute 'x'
>>> foo.x = 5
>>> foo()
5
守望孤独 2024-09-13 16:58:48

这是一个装饰器,它在执行函数之前将 current_fun 注入到函数全局变量中。这是非常巧妙的做法,但也非常有效。

from functools import wraps


def introspective(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        exists = 'current_fun' in f.func_globals
        old = f.func_globals.get('current_fun',None)
        f.func_globals['current_fun'] = wrapper
        try:
            return f(*args, **kwargs)
        finally:
            if exists:
                f.func_globals['current_fun'] = old
            else:
                del f.func_globals['current_fun']
    return wrapper

@introspective
def f():
    print 'func_dict is ',current_fun.func_dict
    print '__dict__ is ',current_fun.__dict__
    print 'x is ',current_fun.x

这是一个使用示例

In [41]: f.x = 'x'

In [42]: f()
func_dict is  {'x': 'x'}
__dict__ is  {'x': 'x'}
x is  x

In [43]: g = f

In [44]: del f

In [45]: g()
func_dict is  {'x': 'x'}
__dict__ is  {'x': 'x'}
x is  x

Here's a decorator that injects current_fun into the functions globals before executing the function. It's quite the hack, but also quite effective.

from functools import wraps


def introspective(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        exists = 'current_fun' in f.func_globals
        old = f.func_globals.get('current_fun',None)
        f.func_globals['current_fun'] = wrapper
        try:
            return f(*args, **kwargs)
        finally:
            if exists:
                f.func_globals['current_fun'] = old
            else:
                del f.func_globals['current_fun']
    return wrapper

@introspective
def f():
    print 'func_dict is ',current_fun.func_dict
    print '__dict__ is ',current_fun.__dict__
    print 'x is ',current_fun.x

Here's a usage example

In [41]: f.x = 'x'

In [42]: f()
func_dict is  {'x': 'x'}
__dict__ is  {'x': 'x'}
x is  x

In [43]: g = f

In [44]: del f

In [45]: g()
func_dict is  {'x': 'x'}
__dict__ is  {'x': 'x'}
x is  x
荭秂 2024-09-13 16:58:48

答案相当简单。只需使用在执行时查找的事实名称,而不是编译时:

def f():
    return f._x

f._x = "foo"
f()           # -> "foo"

The answer is rather simple. Just use the fact name is looked for at execution time, not compile time:

def f():
    return f._x

f._x = "foo"
f()           # -> "foo"
不可一世的女人 2024-09-13 16:58:48

如果你希望它完全独立于函数名称,你需要一些框架魔法。例如:

def f2():
    import inspect
    frame = inspect.currentframe()
    fname = frame.f_code.co_name
    fobj = frame.f_globals[fname]
    print fobj._x


f2._x = 2
f2() 

If you want it to be totally independent of the function name, you need some frame magic. For example:

def f2():
    import inspect
    frame = inspect.currentframe()
    fname = frame.f_code.co_name
    fobj = frame.f_globals[fname]
    print fobj._x


f2._x = 2
f2() 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文