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

myk*_*hal 39 python attributes closures scope function

是否可以从函数范围内访问python函数对象属性?

比如我们有

def f():
    return SOMETHING

f._x = "foo"
f()           # -> "foo"
Run Code Online (Sandbox Code Playgroud)

现在,如果我们想让_x属性内容"foo"返回,那么SOMETHING必须是什么?如果它甚至可能(简单)

谢谢

更新:

我也想做以下工作:

g = f
del f
g()          # -> "foo"
Run Code Online (Sandbox Code Playgroud)

更新2:

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

Mar*_*ato 48

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

def f(self):
    return self.x
f.func_defaults = (f,)
Run Code Online (Sandbox Code Playgroud)

用法示例:

>>> f.x = 17
>>> b = f
>>> del f
>>> b()
17
Run Code Online (Sandbox Code Playgroud)

说明

原始海报想要一个不需要全局名称查找的解决方案.简单的解决方案

def f():
    return f.x
Run Code Online (Sandbox Code Playgroud)

f在每次调用时执行全局变量的查找,这不符合要求.如果f删除,则该功能失败.更复杂的inspect提案以同样的方式失败.

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

def f(self=f):
    return self.x
Run Code Online (Sandbox Code Playgroud)

在上面,self是一个局部变量,因此不执行全局查找.但是,我们不能按原样编写代码,因为f当我们尝试将默认值绑定self到它时尚未定义.相反,我们在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)
Run Code Online (Sandbox Code Playgroud)

例:

>>> foo.subject = 'Fred'
>>> bar = foo
>>> del foo
>>> bar('runs')
'Fred runs swiftly'
Run Code Online (Sandbox Code Playgroud)

  • 我们可以补充说`f.func_defaults`在Python 3中被重命名为`f .__ defaults__`以保持一致性吗?此外[其他人也有疑惑](http://stackoverflow.com/questions/13784840/are-you-allowed-to-modify-func-defaults-defaults-in-python-3-x-in-python) (3认同)
  • @Steven,我不喜欢装饰器,因为在我的小脑袋里,我不记得这个装饰器不能应用于任意函数,而只能应用于那些倾向于接受 `self` 作为最后一个参数的函数。猜猜我将不得不做通常的包装以获得另一个封闭 (2认同)

Joh*_*ooy 24

你可以用一个类来做这件事

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

  • +1,如果这是一个真实的项目,明智的做法. (4认同)
  • 这就是我在实际场景中使用的内容 (2认同)

Nas*_*nov 15

那么,让我们来看看它是什么功能:

>>> 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
Run Code Online (Sandbox Code Playgroud)

啊哈!因此该属性被添加到函数对象中,但它不会看到它,因为它正在寻找全局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'
Run Code Online (Sandbox Code Playgroud)

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

>>> 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
Run Code Online (Sandbox Code Playgroud)

那很棒!但它是否会重新命名和删除原始功能?

>>> g = foo
>>> g.func_name
'foo'
>>> g.func_code.co_name
'foo'
Run Code Online (Sandbox Code Playgroud)

啊,非常怀疑.函数对象及其代码对象仍然坚持要求它们被调用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'
Run Code Online (Sandbox Code Playgroud)

党!所以通常不能通过执行帧的内省来完成.问题似乎是函数对象代码对象之间存在差异- 代码对象是执行的对象,它只是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}
Run Code Online (Sandbox Code Playgroud)

当然还有其他的chicanery你可以这样做,它似乎是函数 - 特别是类定义的技巧......但这本身不是一个函数.这完全取决于你真正需要做什么.


Ant*_*Ego 8

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

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


>>> foo=factory()
>>> foo.x=11
>>> foo()
11
>>> bar = foo
>>> del foo
>>> bar()
11
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

19990 次

最近记录:

7 年 前