【问题标题】:reload/reimport a file/class that is imported using from * import *重新加载/重新导入使用 from * import * 导入的文件/类
【发布时间】:2013-06-18 21:10:30
【问题描述】:

好吧,正如标题所说,我得到了一组导入,所有导入一个类,都与运行它的脚本在同一个文件夹中:

from lvl import lvl
from get import get
from image import image
from video import vid
from video import MLStripper
from system import system
from setting import setting
from listsearch import lists

python3 没有 reload iirc 但有 imp.reload() 但它似乎不起作用,

它只是抛出一个错误,说它不是一个模块(它是一个类,所以它不起作用)

在导入的那些类中的每一个小编辑之后,我都需要重新启动脚本

有没有办法重新加载/重新导入类以显示编辑的效果而无需启动脚本或重写大部分脚本以便 imp.reload() 工作?

python3, linux(但如果它也适用于窗口则更喜欢)

编辑1:

示例:如果我使用:

import system
system.system.temp()

返回:

65°C

如果我将其更改为显示 °F 并使用 imp.reload 重新加载

imp.reload(system)
system.system.temp()

返回:

149°F

所以,如果我使用它,它可以工作

import system as _system
from system import system
system.temp()

返回:

65°C

然后我将其更改为显示 °F 并使用 imp.reload 重新加载

imp.reload(_system)
from system import system
system.temp()

它仍然返回

65°C

但是,如果我这样称呼它:

_system.system.temp()

返回

149°F

我知道为什么会这样,但它是因为它发生在 while 循环中?

编辑2:

文件名:system.py:

在更改测试之前:

class system:
  def temp():
    temperature = open("/sys/class/thermal/thermal_zone0/temp","r").read()
    temperature = temperature[:2]
    return(temperature+"°C")

更改测试后:

class system:
  def temp():
    temperature = open("/sys/class/thermal/thermal_zone0/temp","r").read()
    temperature = temperature[:2]
    temperature = str(9.0 / 5.0 * int(temperature) + 32).split(".")[0]
    return(temperature+"°C")

【问题讨论】:

  • 为了将来参考,如果您使用 PEP8 风格的命名,您的类将命名为 Lvl、Image 等,并且不会与您的模块名称冲突,这会使事情变得更简单。
  • 好吧,我从来没有真正使用过 Python 增强提案,我认为最好以易于理解和记忆的方式编写代码,并且我尽量减少所有内容,但将其排序为缩进/换行符和变量/function(使用 2 个空格缩进,如果文件只有几行,有时使用 1 个空格缩进),因为我的任何代码都不太可能被其他人使用,基本上任何意味着更少的输入和错误导致的错误案例
  • “我从来没有真正使用过 Python 增强提案”没有任何意义。 PEP 记录了 Python 2.0 及更高版本中所做的每个重大更改。见What is a PEP?。无论如何,即使您认为您的代码不太可能被其他人使用,如果您想将其发布在 SO 上并要求帮助它。最后,发明自己的命名风格直接导致了您的问题——您不能在范围内同时拥有两个名为 lvl 的东西。
  • @abarnert 我的命名风格并不直接导致我的问题在这里,事实上我没有导入模块导致这些模块中只有一个类有名称冲突所以我认为不需要它,因为它最终会被类覆盖
  • 我不太明白你的编辑版本。如果您认为“这是因为它发生在 while 循环中”,您将不得不向我们展示 while 循环的样子。而且,一般来说,您必须向我们展示您的实际代码。我的猜测是你实际上没有一个类方法system.system.temp(),而是一个实例方法;你已经创建了一个system.system 实例,并且你在that 上调用temp()。在这种情况下,重新加载 system 没有任何好处;您必须创建新类的新实例。

标签: python linux python-3.x python-import


【解决方案1】:

你只能reload一个模块:

参数必须是模块对象,所以之前必须成功导入。

在您的情况下,您没有对模块对象的任何引用。你必须import它,即使你不想将它用于其他任何事情,只是为了稍后调用reload。

另外,在reload 之后,你有重新import 的名字:

对旧对象的其他引用(例如模块外部的名称)不会重新绑定以引用新对象,如果需要,必须在它们出现的每个命名空间中进行更新。

当您执行from foo import bar 时,bar 是“模块外部名称”,因此您必须明确重新绑定它。

如果您考虑一下,它必须以这种方式工作。 reload 无法枚举其定义依赖于先前版本的模块的所有对象来更新它们。即使可以,也可能有无限循环。如果新版本甚至没有为旧版本中的类定义会发生什么?或者如果类是动态定义的?

换个角度看,from foo import bar 与import foo; bar = foo.bar 非常相似。 bar 是你命名空间中的名字,不是foo 的命名空间,所以reload(foo) 不会碰它;您需要重新复制新的foo.bar。

解决所有这些问题的简单方法是在reload 之后重复所有from foo import bar 行。


对于简单的情况:

import video
from video import MLStripper

# ... later

imp.reload(video)
from video import MLStripper

但是,您的大多数示例都有明显的命名冲突:一旦您from video import video,您就不能再reload(video)。因此,您需要另一个对 video 模块对象的引用。

Python 为你保留了一个,你可以使用它:

imp.reload(sys.modules['video'])
from video import MLStripper

或者,或者,您可以使用as 子句,或者只是一个= 赋值,给它起任何你想要的名字。

import video as _video
from video import video

# ... later

imp.reload(_video)
from video import video

从您的 cmets 和您编辑的问题来看,听起来您还有一个问题。让我们用一个简单的非碰撞案例来讨论它。

我相信你实际上是在做这样的事情:

import video
from video import MLStripper

stripper = MLStripper('foo")

# ... later

imp.reload(video)
from video import MLStripper

第一行将成功重新加载video 模块,第二行将其MLStripper 类复制到您的全局变量中,因此您创建的任何新MLStripper 实例都将属于新类型。

但这不会影响任何现有的MLStripper 实例,例如stripper。

就像MLStripper 一样,stripper 是“模块外部名称”之一。但实际上情况更糟。为了调整它,reload 必须弄清楚它的状态会是什么,如果新版本的代码在创建时就已经生效。应该很明显,这是一个无法解决的问题。


如果您知道要修补的实例,则可以像处理类一样有效地处理它们:只需再次创建它们:

imp.reload(video)
from video import MLStripper
stripper = MLStripper('foo")

如果这还不够好,您可能需要以下三种 hacky 可能性:

  • Monkeypatch 方法、属性等到实例及其__class__(es)。
  • 直接修补实例的__class__ 属性,因此从类继承的所有内容现在都将从新类继承。
  • 在reload 之前使用pickle 序列化实例,然后在之后反序列化。

对于非常简单的情况,这三个都可以。对于更复杂的情况,您必须了解自己在做什么。


请注意,您可以将大量此类内容封装在一个函数中,但您必须了解局部变量和全局变量的工作方式(以及导入和重新加载的工作方式),否则您最终会感到困惑。

一个更简单的解决方案是只创建“转储所有状态”和“加载所有状态”函数。然后,您可以转储所有内容、退出、重新启动和恢复。 Python 教程和 ipython 文档都描述了几种不同的方法来代替使用reload;可能值得回去重读。

【讨论】:

  • 好像一旦导入了,再次执行from * import * 命令什么都不会做
  • @freeforalltousez:它确实做了一些事情:它将名称从模块重新复制到全局变量。仅仅重新加载模块并不能做到这一点;只做from … import … 不会重新加载。这就是为什么你需要两者。
  • 我都做了,重新加载的模块中的类仍然是旧类,该类没有更新,但是如果我使用导入视频,并使用 video.video.link(arg),然后执行 imp.reload(_video),然后更新带有函数链接的类
【解决方案2】:

通过sys.modules访问模块,重新加载那个,然后重新分配导入的名称:

imp.reload(sys.modules['lvl'])
from lvl import lvl

imp.reload(sys.modules['get'])
from get import get

等等

所有from something import name 语法所做的就是导入something,然后将name 绑定到something.name 所指的同一个对象。

通过使用sys.modules,您不必再次显式导入模块,并且可以在重新加载后达到重新绑定对象的新定义。

【讨论】:

  • 你不需要sys.modules['get'].get,你可以(重新)做from get import get。
猜你喜欢
  • 2011-07-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-07
  • 2018-06-14
  • 2011-12-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多