在尝试创建类似于Sage的var()
或function()
的Python函数时,我在Python中遇到了一个明显不那么重要的问题。本质上,在Sage中调用var('x')
不仅返回一个Sage符号表达式,而且还相当于x = SR.var('x')
,即它将表达式对象分配给当前全局命名空间中的变量(调用模块的命名空间) )。
我的问题是,它是如何做到的?如果我做这样的事情:
B.py
中的:
def func():
globals()['x'] = something
A.py
中的
from B import func
func()
我只能影响模块B
的全局命名空间中的变量,而不是调用模块A
的全局命名空间。
然而,与我的Sage版本一起分发的文件var.pyx
如下所示:
...
def var(*args, **kwds):
if len(args)==1:
name = args[0]
else:
name = args
G = globals() # this is the reason the code must be in Cython.
if 'ns' in kwds:
# ...
# not relevant
v = SR.var(name, **kwds)
if isinstance(v, tuple):
for x in v:
G[repr(x)] = x
else:
G[repr(v)] = v
return v
...
特别是关于Cython的评论似乎很有趣。我对Cython知之甚少,所以也许这就是我的问题。如果这是Cython的一些特殊方面,那么如何在“常规Python”/ CPython中复制这个函数呢?
PS:是的,我意识到,一般来说,这种行为是个坏主意。我主要是出于好奇而问。答案 0 :(得分:1)
在https://groups.google.com/d/topic/sage-devel/J-kDHlnT4/discussion
中找到了解释我引用Volker Braun:
在src / setup.py中我们设置
Cython.Compiler.Options.old_style_globals = True
导致Cython回归旧行为。
答案 1 :(得分:0)
查看Cython 1.5's changelog,我们可以看到
globals()现在返回Cython模块的全局变量的只读字典,而不是堆栈中第一个非Cython模块的全局变量
因此,这是一个仅适用于非常古老的Cython编译器的技巧。
您可以使用此代码来模拟它:
import inspect
def run():
outer_frame = inspect.stack()[1][0]
outer_frame_locals = inspect.getargvalues(outer_frame).locals
outer_frame_locals["new_variable"] = "I am new"
虽然注意它是非常实现定义。