Sage的“var”如何工作?

4
在尝试创建类似于Sagevar()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:是的,我意识到通常这样的行为是一个坏主意。我主要是出于好奇问的。
2个回答

1

啊,原来有一个编译器标志可以获得Cython < 0.15的行为。 - Socob

0

看一下Cython 1.5的变更日志,我们可以看到

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"

需要注意的是,这非常取决于具体实现。


啊,这很有趣,但是如果Sage使用了如此旧的Cython版本,那对我来说似乎很奇怪。有没有办法找出他们实际使用的版本?我搜索了很多都没有什么运气。 - Socob
1
这不正确。Sage在两年前就更新到了Cython 0.15 http://trac.sagemath.org/ticket/11761,而全局技巧仍在使用https://github.com/sagemath/sagelib/blob/master/sage/calculus/var.pyx。在“Sage版本5.10,发布日期:2013-06-17”中,我们使用的是“Cython版本0.19.1”。 - hivert
1
有一个计划在Sage 6.2中升级到Cython 0.20 http://trac.sagemath.org/ticket/15755 - hivert
我认为如果Sage仅为此使用旧版本的Cython会很奇怪。但是,如果Cython的globals()与CPython的相同,那么它是如何工作的呢? - Socob
Cython 可能只是向上遍历了堆栈(从 inspect.stack() 返回的东西),直到遇到一个非 Cython 模块为止。 - Veedrac
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接