Python子进程无法使用父进程导入的模块
我在使用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 个回答
你没有把 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()
)会找到父类的方法。
这个子文件(Child.py)有自己的命名空间,你还没有把sqlite3这个库导入到里面。所以你需要在Child.py里导入sqlite3。你也可以用 import Parent.sqlite3
的方式,然后调用 Parent.sqlite3.connect
。不过这样做其实没有什么好处,因为模块只会在第一次导入的时候真正被加载,后面的导入只是把这个模块添加到当前的命名空间里。
sqlite3模块被导入到了父模块里,所以你需要通过父模块来访问它。
self.db = Parent.sqlite3.connect("test.db")
它并没有直接导入到子模块里,除非你告诉Python这样做,比如说:
from Parent import *
这样做可以让你在子模块中访问父模块里的所有内容。