python 3中execfile的替代方法是什么?


352

看来他们在Python 3中取消了通过删除以下命令快速加载脚本的所有简便方法 execfile()

我是否有明显的替代品?


1
reload又回来了,因为imp.reload,由于3.2。
道加尔

18
如果以交互方式使用Python,请考虑使用IPython:%run script_name与所有版本的Python一起使用。
2014年

1
由于3.4 impimportlib (必须导入),因此:importlib.reload(mod_name)import和executes mod_name
P. Wormer

3
runfile(“ filename.py”)有什么问题?
mousomer '17

1
谢谢@mousomer!我一直在寻找功能,runfile()因为我需要运行一个在自己的名称空间中执行的Python脚本(而不是在调用名称空间中执行)。我的应用程序:sys.path使用以下__file__属性将被调用脚本的目录添加到系统路径():如果我们execfile()在Python 3(exec(open('file.py').read()))中使用其等效语言,则所包含的脚本在调用名称空间中运行,从而__file__解析为调用文件名。
mastropi

Answers:


389

根据文档,而不是

execfile("./filename") 

采用

exec(open("./filename").read())

看到:


54
知道为什么他们会这样做吗?这比以前更加冗长。另外,它在Python3.3上对我不起作用。我执行(open('./ some_file')。read())时得到“没有这样的文件或目录”。我曾尝试包括“的.py”扩展,也排除了“./”,以及
JoeyC

25
不那么平常的是,当引发异常时,它不提供行号,就像execfile()一样。
KDN

35
您也需要close该文件句柄。另一个理由不喜欢从蟒蛇2.改变
个篮板

3
@Rebs在该示例中不需要关闭文件句柄,它将自动完成(至少在常规CPython中如此)
tiho

4
一旦CPython对象中的@Rebs的引用计数变为0,就会对其进行垃圾回收,只有循环引用才可以延迟此延迟(stackoverflow.com/questions/9449489/…)。在这种情况下,应该在read()返回之后立即发生。并且文件对象在删除时会被关闭(注意:我意识到此链接明确表示“总是关闭文件”,这的确是遵循常规的好习惯)
tiho

219

您只应该自己读取文件并执行代码。2to3电流替代

execfile("somefile.py", global_vars, local_vars)

with open("somefile.py") as f:
    code = compile(f.read(), "somefile.py", 'exec')
    exec(code, global_vars, local_vars)

(并非严格要求进行编译调用,但是它将文件名与代码对象相关联,从而使调试更加容易。)

看到:


3
这对我有用。但是,我注意到您以错误的顺序编写了局部和全局参数。实际上是:exec(object [,globals [,locals]])。当然,如果您将原始参数翻转了,那么2to3将会产生您所说的内容。:)
Nathan Shively-Sanders,2009年

3
很高兴发现,如果您可以省略global_vars和local_vars,那么这里的python3替代品也可以在python2下使用。即使exec在python2中是一条语句,它也exec(code)可以工作,因为parens会被忽略。
medmunds 2013年

2
+1用于使用编译。我的"somefile.py"包含inspect.getsourcefile(lambda _: None)在没有编译的情况下失败了,因为inspect模块无法确定代码的来源。
ArtOfWarfare 2014年

16
那真是太丑了。知道为什么他们在3.x中删除了execfile()吗?execfile还使传递命令行参数变得容易。
轶事,2015年

3
open("somefile.py")如果somefile.py使用的字符编码不同于,则可能不正确locale.getpreferredencoding()tokenize.open()可以代替使用。
jfs

73

尽管exec(open("filename").read())通常被用作的替代方案execfile("filename"),但它错过了execfile支持的重要细节。

Python3.x的以下功能与直接执行文件具有相同的行为。匹配运行python /path/to/somefile.py

def execfile(filepath, globals=None, locals=None):
    if globals is None:
        globals = {}
    globals.update({
        "__file__": filepath,
        "__name__": "__main__",
    })
    with open(filepath, 'rb') as file:
        exec(compile(file.read(), filepath, 'exec'), globals, locals)

# execute the file
execfile("/path/to/somefile.py")

笔记:

  • 使用二进制读取以避免编码问题
  • 保证关闭文件(Python3.x警告)
  • 定义__main__,某些脚本依赖于此来检查它们是否作为模块加载,例如。if __name__ == "__main__"
  • 设置__file__异常消息更好,某些脚本用于__file__获取其他文件相对于它们的路径。
  • 接受可选的globals和locals参数,就地进行修改execfile-您可以在运行后通过读回变量来访问定义的任何变量。

  • 与Python2不同,execfile这默认情况下不会修改当前名称空间。为此,您必须显式传入globals()locals()


68

正如最近在python-dev邮件列表上建议那样runpy模块可能是一个可行的替代方案。引用该消息:

https://docs.python.org/3/library/runpy.html#runpy.run_path

import runpy
file_globals = runpy.run_path("file.py")

与以下内容有细微的差异execfile

  • run_path总是创建一个新的名称空间。它作为模块执行代码,因此全局变量和局部变量之间没有区别(这就是为什么只有init_globals参数的原因)。返回全局变量。

    execfile在当前名称空间或给定名称空间中执行。的语义localsglobals,如果给定的,类似于当地人和全局类定义中。

  • run_path 不仅可以执行文件,还可以执行蛋和目录(有关详细信息,请参阅其文档)。


1
由于某种原因,它会在屏幕上输出许多不要求打印的信息(Anaconda Python 3中的“ builtins ”等)。有什么方法可以关闭此功能,以便仅显示我通过print()输出的信息吗?
John Donn

是否还可以获取当前工作空间中的所有变量,而不是将所有变量都存储在其中file_globals?这样可以省去file_globals['...']为每个变量键入的麻烦。
Adriaan

1
“此外,由运行的代码定义的任何函数和类都不能保证在运行函数返回后可以正常工作。” 值得注意的是,取决于您的用例
nodakai

@Adriaan执行“ globals()。update(file_globals)”。我个人最喜欢此解决方案,因为在决定更新当前工作区之前,我可能会发现错误。
罗恩·卡明斯基

@nodakai感谢您的信息,我错过了。从来没有像现在这样的问题,我想知道是什么会引起这种情况。
罗恩·卡明斯基

21

这是更好的方法,因为它从调用者那里获取了全局变量和本地变量:

import sys
def execfile(filename, globals=None, locals=None):
    if globals is None:
        globals = sys._getframe(1).f_globals
    if locals is None:
        locals = sys._getframe(1).f_locals
    with open(filename, "r") as fh:
        exec(fh.read()+"\n", globals, locals)

实际上,这个更接近py2 execfile。当我使用pytests时,它甚至对我有用,而上面发布的其他解决方案失败了。谢谢!:)
Boriel

17

您可以编写自己的函数:

def xfile(afile, globalz=None, localz=None):
    with open(afile, "r") as fh:
        exec(fh.read(), globalz, localz)

如果您真的需要...


1
-1:执行语句不能以这种方式工作。代码无法在任何版本的python中运行。
nosklo

6
-1:默认参数值在函数定义时间来评价,使得无论globalslocals指向全局命名空间FO含有的定义中的模块execfile(),而不是调用者的全局和局部命名空间。正确的方法是使用None默认值,并通过inspect模块的自检功能确定调用者的全局变量和局部变量。
Sven Marnach

12

如果要加载的脚本与运行的脚本位于同一目录中,则也许“ import”可以完成此工作?

如果您需要动态导入代码,则值得研究内置函数__ import__和模块imp

>>> import sys
>>> sys.path = ['/path/to/script'] + sys.path
>>> __import__('test')
<module 'test' from '/path/to/script/test.pyc'>
>>> __import__('test').run()
'Hello world!'

test.py:

def run():
        return "Hello world!"

如果您使用的是Python 3.1或更高版本,则还应该看看importlib


这对我来说是正确的答案。这个博客很好地解释了importlib dev.to/0xcrypto/dynamic-importing-stuff-in-python--1805
Nick Brady

9

这就是我所拥有的(file两个示例中的源代码都已将其分配给文件的路径):

execfile(file)

这是我替换为的内容:

exec(compile(open(file).read(), file, 'exec'))

我最喜欢的部分:第二个版本在Python 2和3中都可以正常工作,这意味着不必添加依赖于版本的逻辑。


5

请注意,如果您使用的不是非ascii或utf-8的PEP-263编码声明,上述模式将失败。您需要找到数据的编码,并在将其交给exec()之前对其进行正确的编码。

class python3Execfile(object):
    def _get_file_encoding(self, filename):
        with open(filename, 'rb') as fp:
            try:
                return tokenize.detect_encoding(fp.readline)[0]
            except SyntaxError:
                return "utf-8"

    def my_execfile(filename):
        globals['__file__'] = filename
        with open(filename, 'r', encoding=self._get_file_encoding(filename)) as fp:
            contents = fp.read()
        if not contents.endswith("\n"):
            # http://bugs.python.org/issue10204
            contents += "\n"
        exec(contents, globals, globals)

3
什么是“上述模式”?当引用StackOverflow上的其他帖子时,请使用链接。诸如“以上”之类的相对定位术语无效,因为有3种不同的方式(按投票,按日期或按活动)对答案进行排序,最常见的一种(按投票)是易变的。随着时间的流逝,您的帖子和您周围的帖子最终将获得不同的分数,这意味着它们将被重新排列,并且这样的比较将不再有用。
ArtOfWarfare 2014年

很好的一点。考虑到我将近六个月前写下了这个答案,我认为“以上模式”是指stackoverflow.com/a/2849077/165082(不幸的是,您必须单击以解决),或者更好的是Noam的答案:
Eric

2
通常,当我想从答案中引用同一问题的其他答案时,我键入“ Noam's Answer”(例如,Noam's Answer),然后将文本链接到我所指的答案,以防万一答案与主题无关。将来的用户,即IE,因为该用户更改了帐户名,或者该帖子成为公共Wiki,因为对其进行了过多的编辑。
ArtOfWarfare 2014年

如何获得帖子中带有特定“答案”的URL,但不包括发帖者的答案名称?
DevPlayer 2015年

查看源并获取ID。例如,您的问题将是stackoverflow.com/questions/436198/…。我一直在寻求更好的方法,但是当我在评论旁徘徊时看不到任何东西
Eric

4

另外,虽然不是纯粹的Python解决方案,但如果您使用的是IPython(无论如何也应该这样做),则可以执行以下操作:

%run /path/to/filename.py

这同样容易。


1

我只是这里的新手,所以如果发现了这个,也许纯粹是运气:

尝试使用命令从解释器提示符>>>运行脚本后

    execfile('filename.py')

为此,我得到了“ NameError:名称'execfile'未定义”,我尝试了一个非常基本的

    import filename

它运作良好:-)

我希望这会有所帮助,并感谢大家提供的出色提示,示例以及所有那些对新手有很大启发的精妙注释的代码!

我使用Ubuntu 16.014 LTS x64。 Linux上的Python 3.5.2(默认值,2016年11月17日,17:05:23)[GCC 5.4.0 20160609]


0

对我来说,最干净的方法是importlib按路径使用和导入文件作为模块,如下所示:

from importlib import util

def load_file(name, path):
    spec = util.spec_from_file_location(name, path)
    module = util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module

使用范例

让我们有一个文件foo.py

print('i got imported')
def hello():
    print('hello from foo')

现在就像导入普通模块一样使用它:

>>> foo = load_file('foo', './foo.py')
i got imported
>>> foo.hello()
hello from foo

与直接方法相比,我更喜欢这种技术,exec(open(...))因为它不会使您的名称空间混乱或不必要地造成混乱$PATH

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.