gpt4 book ai didi

python - 修改 __main__ 模块的属性访问(名称解析的详细信息)

转载 作者:行者123 更新时间:2023-11-28 19:28:40 25 4
gpt4 key购买 nike

从文档中收集的信息

关于name resolution文档不是很清楚。它使用术语 scopenamespace 但没有准确说明它们如何生效以及何时引发 NameError:

When a name is used in a code block, it is resolved using the nearest enclosing scope. The set of all such scopes visible to a code block is called the block’s environment.

When a name is not found at all, a NameError exception is raised.

然而,这并没有解释在何处搜索该名称。关于 namespace ,我们得到以下信息:

Names are resolved in the top-level namespace by searching the global namespace, i.e. the namespace of the module containing the code block, [...]

此外,关于__main__:

The namespace for a module is automatically created the first time a module is imported. The main module for a script is always called __main__.

This part of the docs进一步指出

'__main__' is the name of the scope in which top-level code executes.

相关代码

结合以上陈述,我想每当在“顶层脚本环境”(“顶层命名空间”)中解析名称时,这通过检查 sys.modules['__main__'] 发生这种情况(类似于模块的属性访问如何工作以及如何修改它,如 PEP 562 所指出的)。然而,以下片段表明情况并非如此:

import sys

class Wrapper:
def __init__(self):
self.main = sys.modules['__main__']

def __getattr__(self, name):
try:
return getattr(self.main, name)
except AttributeError:
return 'Fallback for "{}"'.format(name)

sys.modules['__main__'] = Wrapper()
print(undefined)

引发 NameError: name 'undefined' is not defined

另一方面,我们可以通过修改 sys.modules['__main__'].__dict__ 或使用 setattr 添加名称:

import sys

# Either ...
sys.modules['__main__'].__dict__['undefined'] = 'not anymore'
# Or ...
setattr(sys.modules['__main__'], 'undefined', 'not anymore')

print(undefined) # Works.

所以我怀疑可能是模块的 __dict__ 属性(或等效的 __builtins__.globals)被直接检查,回避了 getattr模块对象。然而,扩展上面的示例表明情况并非如此:

import sys

class Wrapper:
def __init__(self):
self.main = sys.modules['__main__']

def __getattr__(self, name):
try:
return getattr(self.main, name)
except AttributeError:
return 'Fallback for "{}"'.format(name)

@property
def __dict__(self):
class D:
def __contains__(*args):
return True

def __getitem__(__, item):
return getattr(self, item)

return D()

sys.modules['__main__'] = Wrapper()
sys.modules['builtins'].globals = lambda: sys.modules['__main__'].__dict__
print(globals()['undefined']) # Works.
print(undefined) # Raises NameError.

问题

  1. 作用域命名空间的确切定义是什么?
  2. 如何准确解析名称(采取哪些步骤以及检查哪些资源以确定名称是否存在)?
  3. 名称解析以何种方式涉及范围和命名空间?
  4. 为什么上述使用 Wrapper 的示例会失败(虽然它确实适用于“通用”模块属性访问,根据 PEP 562)?

最佳答案

你的问题很有趣,因为我没有明确的答案让我们进行一些实验。

首先让我们稍微更改一下您的代码:

# file main.py
import sys
print(sys.modules['__main__'])
class Wrapper:
def __init__(self):
self.main = sys.modules['__main__']

def __getattr__(self, name):
try:
return getattr(self.main, name)
except AttributeError:
return 'Fallback for "{}"'.format(name)

sys.modules['__main__'] = Wrapper()
print(sys.modules['__main__'])
print(undefined)

它会打印

<module '__main__' from 'main.py'>
<__main__.Wrapper object at 0x000001F87601BE48>
Traceback (most recent call last):
File "main.py", line 15, in <module>
print(undefined)
NameError: name 'undefined' is not defined

所以我们仍然有 __main__ 作为模块,Wrapper 类在其中。

文档说:

A module’s __name__ is set equal to __main__ when read from standard input, a script, or from an interactive prompt.

所以这意味着我们的 sys.modules['__main__'] = Wrapper() 行是用来替换一个已经加载的模块,使用来自该模块内部的东西 (!!)。

OTOH,从 REPL 导入 main.py(创建 __main__ 模块的另一种情况),完全弄乱了一切 所以那时正在发生一些替代。

总结:

据我所知,从正在运行的模块内部更改 __main__ 需要一些深奥的魔法,也许如果我们使用 importlib.reload 并弄乱缓存模块?

从其他模块这样做似乎没问题,但是(示例)弄乱了事情,并且名称解析中断,即 Wapper 类没有按照您认为应该的方式解析以前的名称。

PD.

很抱歉,如果这不是您想要的有经验的答案并且看起来更像是评论。我这样做是为了检验您的假设并可能找到一些结果。

关于python - 修改 __main__ 模块的属性访问(名称解析的详细信息),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53817898/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com