【问题标题】:Avoiding multiple import in Kivy when calling a function from a different file从不同文件调用函数时避免在 Kivy 中多次导入
【发布时间】:2018-04-11 07:36:36
【问题描述】:

我正在使用 kivy 和 python3.6 开发一个小应用程序(我还是个初学者)。为了清楚起见,我打算将代码分开在不同的文件中,但是我在特定情况下遇到了问题。我做了最小的工作示例来说明。

我有以下文件:

  • main.py
  • main.kv
  • module.py
  • module.kv

这里有一个最小的代码:

main.py:

from kivy.app import App
from kivy.uix.button import Button
from kivy.lang import Builder
import module

Builder.load_file('module.kv')

class MainApp(App):
    pass

def function():
    print('parent function')

if __name__ == '__main__':
    MainApp().run()

main.kv:

CallFunction

模块.py:

from kivy.uix.button import Button

class CallFunction(Button):
    def call_function(self):
        from main import function
        function()

module.kv:

<CallFunction>:
    id : parent_button
    text: 'Call parent button'
    on_press: self.call_function()

所以问题是当我运行这段代码时,我收到了一个警告

文件 /home/kivy/python_exp/test/module.kv 被多次加载,你可能会有不想要的行为。

什么有效

  • 如果我要调用的函数是主应用类的一部分,则没有问题
  • 如果该函数是 module.py 的一部分,则没有问题
  • 如果该函数是另一个模块的一部分,则没有问题

什么不起作用 我无法调用 main.py 中的函数。如果我使用 import 函数作为 module.py 的开头,kivy 会出现奇怪的行为并调用所有内容两次。在这个 call_function 中调用允许有一个正确的接口,但我收到文件已被多次加载的警告。

有一些简单的解决方法,我很清楚这一点,所以更多的是出于好奇和更好地理解 kivy 中的导入是如何工作的。有没有办法让它工作?

我想在应用启动时使用 main.py 来初始化不同的东西。特别是我想在 main.py 中创建另一个类(不是 kivy 类)的实例,并在单击界面上的按钮时,调用此实例上的方法。

谢谢:)

【问题讨论】:

  • 如果您使用多个 kv,而不是在 main.py 中构建它们,您应该将它们包含在 main.kv 的开头。
  • 好点,但有什么区别?我真的不明白。实际上我只是尝试过,它解决了多重导入的问题......你能不能把它作为一个答案? (也许解释一下包含另一个 kv 文件和构建另一个 kv 文件有什么区别)

标签: python-3.x kivy


【解决方案1】:

当你从另一个 python 模块导入一些东西时,python 虚拟机会执行这个模块。在 call_function 中,您从 ma​​in 文件中导入 function,因此每次按下 CallFunction 时,模块。 kv 已加载。

为了解决这个问题,建议在你的主 kv 文件中包含其他 kv 文件。

您还可以将导入语句从方法移到模块文件的顶部。

【讨论】:

  • 如果模块还没有被导入一次,导入模块只会执行模块代码。在文档中不是很清楚,但 this page 描述了事物是如何导入状态的:“当第一次导入模块时,Python 搜索模块,如果找到,它会创建一个模块对象 [1],并对其进行初始化。”
【解决方案2】:

你的 kv 文件被加载了两次,因为代码被执行了两次。这是由于 pythons 模块系统的工作原理,kivy 刚刚意识到加载 kv 两次可能不是您想要的。

通常,python 对象存在于命名空间中。因此,当模块foo 中的函数查找变量时,会在模块的命名空间中搜索该变量。这样,如果您定义两个变量 foo.varbar.var(分别在模块 foobar 中),它们就不会相互冲突和混淆。

棘手的是你执行的python文件很特别:它不创建模块命名空间,而是创建__main__命名空间。因此,如果您将正在执行的文件导入为__main__,它将创建一个带有新对象的全新命名空间并执行模块代码。如果您导入已在当前会话中导入的模块,则模块代码不会再次执行,但已创建的命名空间可用。您甚至不需要两个文件,将以下内容放入test.py

print("hello!")
print(__name__)
import test

如果您现在执行python test.py,您将看到两个hello!,一个是__main__,一个是test

您可以在the documentation 中找到有关命名空间以及变量查找如何在 python 中工作的更多信息。

此外,如果您的 function 确实做了一些工作并改变了 main.py 中的对象,您可能需要重新考虑信息流。通常最好将状态和处理它们的函数在类中绑定在一起,然后将对象传递到调用它们的位置,即在您的示例中传递给 CallFunction

【讨论】:

  • 感谢您的进一步解释。我想我有点意识到这一点,但我一开始无法让它工作(之前的答案很好),因为导入执行了构建器两次。 kivy 的问题比我认为的导入问题更大。
  • 构建器会执行两次:第一次是以__main__ 执行main.py 文件,然后是当您将main 导入为main 模块时再次执行。如果您将导入移动到文件顶部,您仍然会执行两次 main.py(并且仍然应该得到错误,但在不同的“位置”并且可能隐藏在其他初始化消息后面)。如果您在主模块中存储状态(比如说一个列表),这将导致难以调试的错误,因为没有任何东西会引发错误或警告。如果你没有在那里存储任何东西,python 端应该“工作正常”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-09
相关资源
最近更新 更多