【问题标题】:Importing functions that declare global variables导入声明全局变量的函数
【发布时间】:2017-03-01 21:39:37
【问题描述】:

编辑: 好吧,我几乎想通了(虽然它不是我想要的 100%,但它仍然有效)。这是execfile() 函数,我不敢相信我不知道它在那里。

我真的不知道如何表达这个问题(这就是我的谷歌搜索几乎没有出现这个问题的原因)......但是这里有。我将给出我正在尝试做的事情的简化版本。假设我有两个文件,main.pyextra.py。后者看起来像这样:

# extra.py

def setfoo(): # sets "foo" to 5 even if it is unassigned
    global foo
    foo = 5

如果我在控制台中运行它就非常简单:

>>> setfoo()
>>> foo
5

现在让我们回到main.py。我将设置一个导入语句,从extra.py 导入所有内容:

# main.py

from extra import *

setfoo()
print foo

在最后一行之前一切正常;但是,当main.py 尝试访问foo 时,它无法识别它,因为foo 实际上存储在文件extra.py 下。如果我在运行setfoo() 后再次导入所有内容,foo 将被导入并且一切正常。像这样:

# main.py

from extra import *

setfoo()
from extra import *
print foo

或者,如果我使用标准的import 语句而不是from...import 语句,则无需重新导入所有内容,因为来自extra.py 的数据被直接访问而不是复制过来。所以这也可以:

# main.py

import extra

extra.setfoo()
print extra.foo

但是,我真的不想每次运行setfoo() 时都必须输入extra.setfoo(),也不想每次使用上述功能时都重新导入extra.py。我想知道是否有解决方法。理想情况下,有一种方法可以修改extra.py,以便我在main.py 的原始版本中设置的代码可以正常工作。 (编辑:似乎很多人误解了这一点——我愿意修改extra.py,我不想修改main.py,除了顶部的导入语句,让这段代码工作。)如果这是不可能的,我也会考虑使用不同的方法来导入extra.py(我在想类似于PHP的includerequire语句,其中导入的代码是字面上只是复制并粘贴到文件中),但我非常希望这个修改后的导入语句仍然只有一行代码,而且不是很长。所以换句话说,使用文件处理加上exec 语句可能不会很方便。

说到 exec 语句,我真的一点也不在乎我使用的编码实践有多糟糕。我真的只需要一种方法来让它工作,因为这个extra.py 文件的真实版本是我计划从现在开始在几乎所有项目中使用的东西,我不想占用很多额外的东西每次导入此文件时,我的代码中都会有空格。任何帮助将不胜感激。

编辑: 似乎很多阅读本文的人并不清楚我要完成的工作,所以这里是我的代码的真实版本。这是我为实现“内联变量赋值”而组合起来的语法技巧。这是代码(如果您不尝试将其导入另一个文件,则可以正常工作):

class use_obj(object):
    def __rshift__(self, other): return other

def use(**kwargs):
    for var in kwargs: exec 'global ' + var + '; ' + var + ' = kwargs[var]'
    return use_obj()

语法如下:

print use(x = 5, y = 8) >> str(x) + " times " + str(y) + " equals " + str(x*y)

代码本身很糟糕,但我一直想要一种方法来执行内联变量赋值,因为我是内联 if 语句、列表推导、lambdas + reduce() 等的忠实粉丝。我能做到的原因'不仅仅是让函数返回分配的变量是因为use(x = 5, y = 8) 是一个表达式(不是语句),它返回一个奇怪的对象,然后我使用>> 运算符将它推入代码中,而@987654357 的奇怪对象由于我设置__rshift__() 函数的方式,返回的@ 函数神奇地消失了。

如果代码看起来像这样,我认为代码会失去很多美丽和新颖性:

print use(x = 5, y = 8) >> str(extras.x) + " times " + str(extras.y) + " equals " + str(extras.x*extras.y)

【问题讨论】:

  • 在初始化extra.py 时,您是否有任何理由没有设置foo?否则,可能的解决方案是设置foo = None
  • 就像我说的那样,这是我实际尝试做的过于简化的版本。该函数的真实版本以这个设置foo 的方式设置多个变量,我无法知道它将设置哪些变量。所以这就是窗外。

标签: python python-2.7 import global-variables python-import


【解决方案1】:

首先如何在不修改 extra.py 的情况下导入变量,如果你真的想要的话, 您必须借助 sys 模块来获取额外模块中对 foo 的引用。

import sys
from extra import *

print('1. Foo in globals ? {0}'.format('foo' in globals()))
setfoo()
print('2. Foo in globals ? {0}'.format('foo' in globals()))
# Check if extra has foo in it
print('2. Foo in extra ? {0}'.format(hasattr(sys.modules['extra'], 'foo')))
# Getting foo explicitly from extra module
foo = sys.modules['extra'].foo
print('3. Foo in globals ? {0}'.format('foo' in globals()))
print("Foo={0}".format(foo))

输出:

1. Foo in globals ? False
2. Foo in globals ? False
2. Foo in extra ? True
3. Foo in globals ? True
Foo=5

为以后的用例更新: 修改获取导入器并更新其全局变量的 extra.py,

# extra.py
import sys

def use(**kwargs):
    _mod = sys.modules['__main__']
    for k, v in kwargs.items():
        setattr(_mod, k, v)

现在导入任何文件都保持不变,

#myfile.py
from extra import *
print use(x = 5, y = 8), str(x) + " times " + str(y) + " equals " + str(x*y)

输出:

None 5 times 8 equals 40

None 显示为使用函数不返回任何内容。

注意:如果你为你的用例选择更好的 Pythonic 解决方案会更好,除非你想用 Python 找点乐子。

参考python范围规则: Short Description of the Scoping Rules?

【讨论】:

  • “尝试用 Python 找点乐子”是准确的。见我上面的编辑——这些东西不可能被远程认为是“pythonic”。给我一点时间来尝试处理你上面写的内容。
  • 我现在并不打算理解globals() 的作用(我以前从未听说过该功能),但似乎你已经把它倒退了。我可以也想修改extra.py,我不想碰的是main.py
  • 检查更新。在这种情况下,您不需要修改 main.py。
  • 起初没有看到你的评论。我试过了,同样的问题还在发生。在运行use() 函数以访问变量后,我需要再次运行from extra import *。但是感谢您告诉我有关 setattr() 功能的信息,它摆脱了令人作呕的 exec 声明。
  • sys.modules 中是否有某些内容允许您访问导入当前正在运行的代码的文件?
【解决方案2】:

在不了解细节的情况下,一种可能的解决方案是在 extra.py setfoo() 函数中返回 foo,而不是声明为全局变量。

然后在 main.py 中声明全局 foo 并从外部函数 setfoo() 中输入值

这里是设置

#extra.py

def setfoo(): # sets "foo" to 5 even if it is unassigned
    #global foo
    foo = 5
    return foo

#main.py

from extra import setfoo

global foo

foo = setfoo()
print foo

结果:

Python 2.7.9 (default, Dec 10 2014, 12:24:55) [MSC v.1500 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> ================================ RESTART ================================
>>> 
5
>>> 

编辑 - 1
好的,在这个问题上采取 2。

我不认可它,但是如果有特定需要,如果您将变量添加到 __builtin__ 模块,只要它包含 __builtin__ ,它就可以从任何其他模块作为全局访问。

#extra.py

import __builtin__

def setfoo(): # sets "foo" to 5 even if it is unassigned
    global foo
    __builtin__.foo = 5


#main.py

from extra import *
setfoo()
print foo

输出:

>>> 
5
>>> 

【讨论】:

  • 不想这么说,但修改main.py 中的代码几乎是不可能的,除非我只是修改它的导入方式。这实际上是针对我正在研究的一个非常奇怪的语法“hack”,所以它有非常具体的需求,我不能使用像 foo = setfoo() 这样的东西,因为我需要那行代码作为表达式,而不是语句。
  • 如果您可以稍微修改extra,py,请参阅上面的修改解决方案。
  • 这似乎有点过了,如果我这样做,如果我将它发送给其他人,我的代码将无法工作。为了清楚起见,我可以修改extra.py,这是我问题的主要目的,我不想更改main.py以使此代码正常工作。
  • 你没有用新的解决方案改变main.py,只是extra.py
【解决方案3】:

模块具有名称空间,这些名称空间是绑定到对象的变量名称。当您执行from extra import * 时,您将在extra 的命名空间中找到对象并将它们绑定到新模块中的新变量。如果从未调用过setfoo,则extra 没有名为foo 的变量,并且在新的模块命名空间中没有要绑定的内容。

如果setfoo 被调用,那么from extra import * 就会找到它。但事情仍然很时髦。假设一些赋值将extra.foo 设置为42。好吧,另一个模块命名空间不知道这一点,所以在另一个模块中,foo 仍然是 5extra.foo 将是 42

始终牢记对象与在任何给定时间可能引用该对象的事物之间的区别。对象不知道哪些变量或容器碰巧引用了它们(尽管它们确实保留了引用数量的计数)。如果一个变量或容器被重新绑定到不同的对象,它不会改变其他变量或容器的绑定。

【讨论】:

  • 是的,这就是我从谷歌搜索中学到的。您知道extra.py 是否可以在导入它的任何文件的名称空间中设置变量(在这种情况下为main.py)?我认为这会解决我的问题。
  • 您不能这样做,但您可以将extra 放在它自己的容器中(例如,my_config = { 'foo':5 }。但是,更好的解决方案是不要这样做from extra import *。如果你刚刚做了@ 987654339@ 并将变量引用为 extra.foo 在任何地方,每个人都会在 extra 命名空间中引用同一个对象。也就是说,模块是容器,所以请继续引用它们。
  • 遗憾的是,我不认为这是一个选择。几乎要求我以foo 而不是extra.foo 访问它。把它放在像字典一样的容器中也不会完成我想要做的事情。
  • 我曾经有一个客户要求我交付给他的 Windows 程序没有像“.exe”这样的文件扩展名。但是 Windows 需要对可运行文件进行扩展。我不得不放弃他作为客户。我对您的要求知之甚少,但从表面上看,这似乎同样愚蠢。语言不是那样工作的。
  • 整个事情都是故意的白痴,这是我在一行中编写代码块的一种方式,我真的应该在多行中编写,哈哈。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-28
  • 2015-12-14
  • 2015-05-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多