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)
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)
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你可以这样做,它似乎是函数 - 特别是类定义的技巧......但这本身不是一个函数.这完全取决于你真正需要做什么.
作为一种解决方法,您可以使用工厂函数来修复范围:
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 次 |
| 最近记录: |