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

R S*_*R S 325 python python-3.x

似乎他们在Python 3中取消了通过删除快速加载脚本的所有简单方法 execfile()

有没有一个明显的选择我错过了?

Ped*_*ner 353

根据文档,而不是

execfile("./filename") 
Run Code Online (Sandbox Code Playgroud)

使用

exec(open("./filename").read())
Run Code Online (Sandbox Code Playgroud)

看到:

  • 知道为什么他们会做这样的事情吗?这比以前更加冗长.此外,它在Python3.3上对我不起作用.当我执行exec(open('./ some_file').read())时,我得到"没有这样的文件或目录".我尝试过包含'.py'扩展名,也不包括'./' (50认同)
  • 你也需要'关闭'那个文件句柄.另一个不喜欢python 2变化的原因. (31认同)
  • 不那么简单,当引发异常时,这不提供行号,execfile()也是如此. (24认同)
  • CPython对象中的@Rebs一旦引用计数变为0就被垃圾收集,只有循环引用才会延迟这一点(http://stackoverflow.com/questions/9449489/when-are-objects-garbage-collected-in-蟒蛇).在那种情况下,应该在read()返回后立即发生.并且文件对象在删除时关闭(注意:我意识到这个链接明确地说"总是关闭文件",这确实是一般的好习惯) (4认同)
  • @Rebs你不需要在那个例子中关闭文件句柄,它会自动完成(至少在常规的CPython中) (3认同)
  • `with open('./filename') as f: exec(f.read())` 处理打开和关闭文件 (2认同)
  • @JonathanHartley “为什么要做这样的事情”的问题仍未得到解答。这些东西确实加起来了。 (2认同)

Ben*_*son 209

您应该自己阅读文件并执行代码.2to3当前替换

execfile("somefile.py", global_vars, local_vars)
Run Code Online (Sandbox Code Playgroud)

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

(编译调用不是严格需要的,但它将文件名与代码对象相关联,使调试​​更容易一些.)

看到:

  • 那......真的很难看.知道为什么他们摆脱3.x中的execfile()?execfile也很容易通过命令行args. (12认同)
  • 这适合我.但是,我注意到你以错误的顺序编写了本地和全局参数.它实际上是:exec(object [,globals [,locals]]).当然,如果你在原版中翻转了参数,那么2to3将完全按照你的说法生成.:) (3认同)
  • 很高兴地发现,如果你可以省略global_vars和local_vars,那么python3替换也可以在python2下运行.即使`exec`是python2中的一个语句,`exec(code)`也可以,因为parens会被忽略. (3认同)
  • 如果“ somefile.py”使用与“ locale.getpreferredencoding()”不同的字符编码,则“ open(“ somefile.py”)”可能不正确。可以使用`tokenize.open()`代替。 (3认同)
  • +1用于编译.我的``somefile.py'`包含`inspect.getsourcefile(lambda _:None)`,它没有编译就失败了,因为`inspect`模块无法确定代码的来源. (2认同)

ide*_*n42 65

虽然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")
Run Code Online (Sandbox Code Playgroud)

笔记:

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

  • 不同于Python2的execfile这并没有修改默认的当前命名空间.为此你必须明确传入globals()&locals().


Jon*_*fer 63

正如最近在python-dev邮件列表中所建议那样,runpy模块可能是一个可行的选择.引用该消息:

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

import runpy
file_globals = runpy.run_path("file.py")
Run Code Online (Sandbox Code Playgroud)

有一些细微差别execfile:

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

    execfile在当前命名空间或给定命名空间中执行.localsglobals(如果给出)的语义类似于类定义中的locals和globals.

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

  • @Adriaan``globals().update(file_globals)`` (3认同)

Noa*_*oam 20

这个更好,因为它接受来自调用者的全局变量和本地变量:

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)
Run Code Online (Sandbox Code Playgroud)


Eva*_*ark 17

你可以编写自己的函数:

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

如果你真的需要...

  • -1:在函数定义时计算默认参数值,使`globals`和`locals`都指向包含`execfile()`定义的模块的全局名称空间,而不是指向全局和本地名称空间.呼叫者.正确的方法是使用`None`作为默认值,并通过`inspect`模块的内省功能确定调用者的全局和本地. (6认同)
  • -1:exec 语句不能以这种方式工作。代码不能在任何版本的 python 中运行。 (2认同)
  • -1:不可靠.execfile的某些用法是不兼容的. (2认同)

asc*_*bol 11

如果要加载的脚本与您运行的脚本位于同一目录中,那么"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!'
Run Code Online (Sandbox Code Playgroud)

test.py:

def run():
        return "Hello world!"
Run Code Online (Sandbox Code Playgroud)

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

  • 由于我删除了我的 dev.to 帐户,此链接已失效。转发于 https://hackberry.xyz/dynamic-importing-stuff-in-python (2认同)

Art*_*are 9

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

execfile(file)
Run Code Online (Sandbox Code Playgroud)

这是我用它取代的:

exec(compile(open(file).read(), file, 'exec'))
Run Code Online (Sandbox Code Playgroud)

我最喜欢的部分:第二个版本在Python 2和3中都运行得很好,这意味着没有必要添加依赖于版本的逻辑.


Eri*_*ric 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)
Run Code Online (Sandbox Code Playgroud)

  • 什么是"上述模式"?在引用StackOverflow上的其他帖子时请使用链接.像"上面"这样的相对定位术语不起作用,因为有三种不同的方式来排序答案(通过投票,按日期或按活动),最常见的一种(通过投票)是不稳定的.随着时间的推移,您的帖子和帖子会以不同的分数结束,这意味着它们会被重新排列,这样的比较将不那么有用. (3认同)
  • 一般来说,当我想从我的答案中引用同一问题的其他答案时,我输入"Noam's Answer"(例如)并将文本链接到我所指的答案,以防万一答案与答案脱离用户将来,IE,因为用户更改了他们的帐户名称或帖子变成了公共wiki,因为已经对其进行了太多编辑. (2认同)

Arm*_*ius 5

exec()如果可以,请避免。对于大多数应用程序,使用 Python 的导入系统更干净。

此函数使用内置函数将importlib文件作为实际模块执行:

from importlib import util

def load_file_as_module(name, location):
    spec = util.spec_from_file_location(name, location)
    module = util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module
Run Code Online (Sandbox Code Playgroud)

使用示例

让我们有一个文件foo.py

def hello():
    return 'hi from module!'
print('imported from', __file__, 'as', __name__)
Run Code Online (Sandbox Code Playgroud)

并将其作为常规模块导入:

>>> mod = load_file_as_module('mymodule', './foo.py')
imported from /tmp/foo.py as mymodule
>>> mod.hello()
hi from module!
>>> type(mod)
<class 'module'>
Run Code Online (Sandbox Code Playgroud)

好处

这种方法不污染命名空间或弄乱你的$PATH,而exec()在目前的功能的情况下直接运行的代码,可能导致名称冲突。此外,模块属性如__file____name__将被正确设置,并保留代码位置。因此,如果您附加了调试器或者模块引发了异常,您将获得可用的回溯。

请注意,与静态导入的一个细微差别是每次运行时都会导入(执行)模块load_file_as_module(),而不是像import关键字那样只导入一次。