Python子进程无法使用父进程导入的模块

22 投票
3 回答
24072 浏览
提问于 2025-04-17 10:48

我在使用Python的继承时遇到了一个有趣的导入错误。

在父类中,我导入了sqlite3这个模块,然后在子类中尝试使用sqlite3的一个函数,但出现了一个错误,提示“NameError: global name 'sqlite3' is not defined”。 这是为什么呢?我该怎么解决这个问题?

这两个类在不同的文件中:

Parent.py

import sqlite3

class Parent:
    def __init__(self):

        self.create_database()

    def create_database(self):
        """ Virtual function to be overriden in child classes """
        pass

    ...more class functions that use sqlite3 functions

Child.py

import Parent

class Child( Parent.Parent ):
    def create_database(self):
        self.db = sqlite3.connect("test.db") # Error occurs HERE

c = Child()

3 个回答

4

你没有把 sqlite3 模块导入到 Parent 类里(虽然可以这么做,但那样很奇怪)。你是把 sqlite3 导入到了 Parent.py 这个模块里,而这个模块包含了 Parent 类,并在定义中使用了 sqlite3 模块。

然后,另一个模块导入了 Parent.py 模块,并定义了一个 Parent 的子类。这并不会自动把 Parent 类里的所有东西都引入到当前的作用域[1],而且也不会把在定义 Parent 类时的所有内容引入到当前的作用域。如果你在 Parent.py 里定义了其他类,你不会指望这些名字在 Child 里可用,仅仅因为它们和父类在同一个模块里,那为什么你会期待在定义 Parent 的某些方法时用到的模块会自动引入呢?

你已经有了一个指向 sqlite3 被导入的命名空间的引用;你在说 class Child(Parent.Parent) 时就是从中获取了 Parent 类。所以你可以Parent.sqlite3 来访问 sqlite3,但这在 Python 中是一种很奇怪的用法。

通常,最好是在 Child.py 的开头直接加上 import sqlite3。这样任何阅读代码的人都会看到它使用了 sqlite3。如果他们看到你使用的是从 Parent.py 导入的 sqlite3,他们会想,为什么你不使用正常的方式,可能会觉得你在做一些复杂的事情,比如用你自己加的代码包裹了 sqlite3 模块。如果你只是用了 import * from Parent,那么甚至不清楚 sqlite3 这个名字是从哪里来的,读代码的人会很困惑。而且当你决定不再需要在 Parent.py 里导入 sqlite3 时,你的代码会神秘地停止工作,但错误信息不会告诉你任何关于 Parent.py 的事情。

一般来说,如果你在做一些简单明显的事情,比如导入一个标准模块,最好用简单明了的方式来做。人们习惯于这样阅读代码,会很容易理解,而不需要停下来思考。最有可能造成困惑的读者,往往是几个月后你自己,当你忘记了这段代码是怎么工作的;所以你想尽量让自己在重新理解时变得简单。


[1] 从父类继承和作用域没有任何关系,也就是说,你可以在不加修饰的情况下访问哪些名字。在定义子类的类块内,你无法访问父类的方法和类变量。这意味着在子类创建后,实例和类变量的名称解析协议会在子类找不到的情况下去查找父类。这是一个比较微妙的点,基本上可以归结为(1)在子类的类块内(包括方法的定义) some_parent_method() 会报错,但(2)在子类存在后(包括方法实际运行时) Child.some_parent_method()(或者在方法内用 self.some_parent_method())会找到父类的方法。

9

这个子文件(Child.py)有自己的命名空间,你还没有把sqlite3这个库导入到里面。所以你需要在Child.py里导入sqlite3。你也可以用 import Parent.sqlite3 的方式,然后调用 Parent.sqlite3.connect。不过这样做其实没有什么好处,因为模块只会在第一次导入的时候真正被加载,后面的导入只是把这个模块添加到当前的命名空间里。

23

sqlite3模块被导入到了父模块里,所以你需要通过父模块来访问它。

self.db = Parent.sqlite3.connect("test.db")

它并没有直接导入到子模块里,除非你告诉Python这样做,比如说:

from Parent import *

这样做可以让你在子模块中访问父模块里的所有内容。

撰写回答