subject,timeit

stmp

点击文件图标

由于本节主要是在 Windows 平台上,并且有诸多限制,连作者本身也不推荐,所以暂时不翻译。原文也省略。

模块导入与重载

到目前为止,一直在谈论” 导入模块 “而没有真正解释这个术语意味着什么。将在 Part V 中深入研究模块和更大的程序架构,但因为导入也是一种启动程序的方式,本节将介绍足够的模块基础来让你入门。

模块导入与重载——导入与重载基础

简言之,每个以 .py 扩展名结尾的 Python 源码文件都是一个模块。让文件成为模块是不需要特殊代码或语法的:任何文件都可以。其他文件能通过导入那个模块(导入操作本质上是加载另一个文件并且授予对那个文件内容的访问权)来获得模块所定义的东西。通过模块的属性(一个将在下一部分定义的术语),模块的内容对外部世界可用。 这个基于模块的服务模型被证明是 Python 中程序架构下的核心理念。更大的程序通常采用多模块文件的形式,从其他模块文件导入工具。这些模块的其中一个被设计为主或顶层文件,或” 脚本 “—— 启动来开始整个程序的文件,它如常逐行运行。在这个级别下面,是所有导入模块的模块。 本书后面将详细深入研究这种架构问题。本章最关注的是导入操作将运行正被加载的文件中的代码作为最后一步(注:也就是被导入文件中的代码将在对应的 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">import</font> 语句被运行时被执行)。因为这个,导入文件也是另一个启动它的方式。 比如,如果开启一个交互式会话(从系统命令行或其他),可以用一个简单的导入运行之前创建的 script1.py 文件:
  1. C:\code> C:\python33\python
  2. >>> import script1
  3. win32
  4. 1267650600228229401496703205376
  5. Spam!Spam!Spam!Spam!Spam!Spam!Spam!Spam!
这是可以的,但默认一个会话只能一次(事实上,是进程 —— 一个程序运行)。在第一次导入后,随后的导入无效,即使你在另一个窗口再次修改并保存了模块的源文件: …Change script1.py in a text edit window to print 2 __16… php >>> import script1 >>> import script1 设计就是这样的;导入是一个太昂贵的操作以至于不能在一个程序运行中对每个文件重复超过一次。在第 22 章将学到,导入必须找到文件,编译它们为字节码,再运行代码。 如果真的想强制 Python 再次在同一个会话中运行文件,而不停止和重启会话,需要使用在 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">imp</font> 标准库模块中可用的 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">reload</font> 函数(此函数在 Python 2.X 也是一个简单的内置函数,但在 3.X 中不是):
  1. >>> from imp import reload # Must load from module in 3.X (only)
  2. >>> reload(script1)
  3. win32
  4. 65536
  5. Spam!Spam!Spam!Spam!Spam!Spam!Spam!Spam!
  6. <module 'script1' from '.\\script1.py'>
  7. >>>
这里的 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">from</font> 语句简单地从模块中拷贝一个名字出来(关于这点更多见后)。<font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">reload</font> 函数本身加载并运行文件代码的当前版本,如果在另一个窗口已经修改并保存,那么会重新加载新的变更内容。 这允许在当前的 Python 交互式会话中实时编辑并获取最新代码。比如,在这个会话中,在第一次 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">import</font> <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">reload</font> 调用之间, <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">script1.py</font> 中的第二个 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">print</font> 语句在另一个窗口中被修改为 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">print(2 ** 16)</font>—— 因此有了不同的结果。

<font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">reload</font> 函数期待一个已经被加载的模块对象的名称,所以在重载一个模块前,必须已经成功的导入过它(如果导入报错,就不能再重载,且必须重新导入)。注意 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">reload</font> 还期待在模块对象名周围的小括号,然而 import 不需要。 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">reload</font> 是一个被调用的函数,而 import 是一个语句。

这就是为何必须将模块名传递给 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">reload</font> 作为小括号中的一个参数的原因,且这是为什么当重载的时候会得到一个额外的输出行 —— 最后一个输出行只是 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">reload</font> 调用返回值的显示结果,一个 Python 模块对象。在第 16 章中将学习更多关于使用函数的知识;就现在来说,一听到” 函数 “,就记住需要小括号来调用。 注:在 Python 3.12 中, <font style="background-color:rgb(245, 248, 252);">imp</font> 将被移除,使用 <font style="background-color:rgb(245, 248, 252);">importlib</font> 作为代替即可。测试代码如下:
  1. >>> from importlib import reload
  2. >>> reload(script1)
  3. darwin
  4. 4294967296
  5. Spam!Spam!Spam!Spam!Spam!Spam!Spam!Spam!
本文章首发在 LearnKu.com 网站上。

上一篇下一篇

Markdown 文本

纠错改进


模块导入与重载——导入与重载基础——注意

版本差异说明:Python 3.X 将 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">reload</font> 内置函数移动到 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">imp</font> 标准库模块。它仍然像以前那样重载文件,但为了使用它必须先导入它。在 3.X 中, 运行 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">import imp</font> 然后使用 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">imp.reload(M)</font> 或运行如同这里显示的 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">from imp import reload</font> 然后使用 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">reload(M)</font>。下一节将讨论 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">import</font> <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">from</font> 语句,然后在本书后面会更正式地讨论。

如果正在使用 Python 2.X, <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">reload</font> 是作为内置函数可用的,所以不需要导入。在 Python 2.6 和 2.7 中, <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">reload</font> 则是两种形式都可用 —— 内置和模块函数 —— 这是为了帮助过渡到 3.X。换句话说,在 3.X 中重载仍是可用的,但需要一行额外的代码来获取 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">reload</font> 调用。 3.X 中的(将 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">reload</font>)移动(到 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">imp</font>)的部分原因很可能是一些著名的涉及 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">reload</font><font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">from</font> 语句的问题,这些问题将在下一章遇到。简而言之,用 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">from</font> 加载的名称不会直接被一次 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">reload</font> 更新,但用 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">import</font> 语句访问的名称却会被更新。如果在 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">reload</font> 后,名称看起来没有变化,尝试更换为 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">import</font><font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">module.attribute</font> 名称引用。

模块导入与重载——更大的模块故事:属性

因为导入操作在最后一步会执行文件,所以导入和重载提供了一个自然的程序启动选项。然而,从更广泛的角度来看,模块充当工具库的角色,将在 Part V 中详细学习。然而,基本概念是简单的:模块通常只是变量名包,被称作命名空间,且在这个包中的名称被称为属性。一个属性仅仅是附在一个特定对象(如一个模块)上的变量名。 更具体地说,导入者可以访问到在模块文件顶层被分配的所有名称。这些名称通常被分配给被模块导出的工具 —— 函数、类、变量等等 —— 它们计划在其他文件和程序中被使用。在外部,模块文件的名称可以用两个 Python 语句 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">import</font> <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">from</font>,还有 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">reload</font> 调用来获取。 使用文本编辑器在工作目录创建一个名为 myfile.py 的单行 Python 模块文件,内容如下:
  1. title = "The Meaning of Life"
这可能是世界上最简单的 Python 模块之一(它只包含了一行赋值语句),但足以说明问题。当这个文件被导入时,它的代码被运行来产生模块的属性。也就是说,赋值语句创建了一个名为 title 的变量和模块属性。 可以用两种不同方式在其他组件访问这个模块的 title 属性。首先,可以用 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">import</font> 语句来整体加载模块,然后使用属性名称限定模块名称来获取它(注意我们这里让解释器自动打印):
  1. % python # Start Python
  2. >>> import myfile # Run file; load module as a whole
  3. >>> myfile.title # Use its attribute names: '.' to qualify
  4. 'The Meaning of Life'
通常,点表达式语法 对象。属性 可以获取附在任何对象上的任何属性,且是 Python 代码中最常用的操作之一。这里,已经使用它来访问在模块 myfile 中的字符串变量 tilte—— 换句话说,myfile.title. 或者,可以用 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">from</font> 语句从模块中获取(其实是拷贝)名称:
  1. % python # Start Python
  2. >>> from myfile import title # Run file; copy its names
  3. >>> title # Use name directly: no need to qualify
  4. 'The Meaning of Life'
随后将更详细地看到,<font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">from</font> 就像是一个 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">import</font>,在导入组件中有一个额外的名称赋值。技术上讲,<font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">from</font> 拷贝了模块的属性,以至于这些属性在接收程序中都变成了简单的变量 —— 因此,可以简单地将导入的字符串引用为 title(变量)而不是 myfile.title(属性引用 [7])。 使用 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">import</font> <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">from</font> 来引发导入操作,模块文件 myfile.py 中的语句都会被执行,且导入组件(这里,指的是交互式提示符)可以访问分配在文件顶层的名称。在这个简单例子中只有一个这种名称 —— 变量 title,被赋值了一个字符串 —— 但在模块中开始定义对象如函数和类时,这里的概念会更加有用:这些对象成为可重用的软件组件,它们能从一个或多个客户端模块中通过名称访问。 在实践中,模块文件通常定义了不止一个名称在文件内外使用。下面是一个定义三个名称的例子:
  1. a = 'dead' # Define three attributes
  2. b = 'parrot' # Exported to other files
  3. c = 'sketch'
  4. print(a, b, c) # Also used in this file (in 2.X: print a, b, c)
这个文件,threenames.py,赋值了三个变量,因此对外部世界产生了三个属性。它还在一个 3.X <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">print</font> 语句中使用了它所拥有的三个变量,如其作为顶层文件运行时所看到的。(在 Python 2.X 中 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">print</font> 有一些不同,所以这里需要省略它的外部小括号来精确匹配输出;在第 11 章关于这点的更完整解释):
  1. % python threenames.py
  2. dead parrot sketch
在其他地方,当这个文件被首次导入时,不管是通过 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">import</font> <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">from</font>,其中所有代码是照常运行。使用 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">import</font> 本文件的客户端得到了模块和属性,然而使用 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">from</font> 的客户端得到了文件中名称的拷贝:
  1. % python
  2. >>> import threenames # Grab the whole module: it runs
  3. here
  4. dead parrot sketch
  5. >>>
  6. >>> threenames.b, threenames.c # Access its attributes
  7. ('parrot', 'sketch')
  8. >>>
  9. >>> from threenames import a, b, c # Copy multiple names out
  10. >>> b, c
  11. ('parrot', 'sketch')
这里的结果被打印在小括号中,因为它们其实是元组 —— 一种被输出中逗号创建的对象(在本书的下一部分将会谈到)—— 因此可以暂时安全的忽略它们。 一旦开始使用像这样的多个名称编码模块,内置的 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">dir</font> 函数就开始了有了用武之地 —— 可以用它来获取在模块中的所有可用名称的列表。下面在方括号中返回了一个 Python 字符串列表(在下一章中开始学习列表):
  1. >>> dir(threenames)
  2. ['__builtins__', '__doc__', '__file__', '__name__', '__package__',
  3. 'a', 'b', 'c']
这里,这个列表的内容已经被编辑过了,因为它们根据 Python 版本而异。这里要注意的点是当像这样在括号中使用导入模块名称来调用 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">dir</font> 函数时,它返回在那个模块中的所有属性。它返回的一些名称是 “免费” 得到的:前后带有双下划线的名称 (<font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">__X__</font>) 是内置名称,它们总是被 Python 预定义且对解释器有特殊含义,但在本书的这个节点上它们并不重要。代码中通过赋值定义的变量 ——a,b 和 c—— 在 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">dir</font> 结果中最后出现。
[7]: 注意 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">import</font><font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">from</font> 都将模块文件名简单地列为 myfile 而不带其 .py 扩展名后缀。将在 Part V 中学习到:当 Python 查找真实文件时,它知道在其搜索程序中包含后缀。再次提醒:必须在系统 shell 命令行包含 .py 后缀,但在 **<font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">import</font>** 中不需要

Python 如何从另一个目录导入.py文件 ++

在本文中,我们将介绍如何在Python中从另一个目录导入.py文件。有时我们需要在自己的Python程序中使用其他目录中的模块或代码文件。可以通过一些方法实现这个目标,并且我们将在下文中提供详细的说明和示例。

阅读更多:Python 教程

方法一:使用sys.path.append()

第一种方法是使用sys.path.append()函数。sys.path是Python的搜索路径列表,它包含了模块搜索路径。我们可以向sys.path列表中添加另一个目录,使得Python可以在该目录中寻找模块文件。下面是使用sys.path.append()导入另一个目录的示例代码:
  1. import sys
  2. sys.path.append('/path/to/another/directory')
  3. import another_module
Python Copy 在这个示例中,我们首先导入了sys模块,然后使用sys.path.append()添加了另一个目录。接下来,我们可以导入位于另一个目录的模块,并进行相应的操作。

方法二:使用相对路径

第二种方法是使用相对路径。如果要导入位于同一根目录下的另一个文件夹中的模块,可以使用相对路径。下面是使用相对路径导入的示例代码:
  1. from ..another_directory import another_module
Python Copy 在这个示例中,..表示上一级目录,我们可以使用它来导入另一个文件夹中的模块。这种方法特别适用于项目中多个子文件夹的情况。

方法三:使用包

第三种方法是使用包。Python中的包是一个包含init.py文件的目录,它可以包含多个模块文件。如果我们将另一个目录转换为一个包,就可以直接使用包来导入其中的模块。下面是使用包导入的示例代码:
  1. from package_name import module_name
Python Copy 在这个示例中,package_name是我们要导入的包的名称,module_name是包中的模块名称。通过这种方式,我们可以方便地导入其他目录中的模块,并使用其中的函数和变量。

方法四:使用importlib

第四种方法是使用importlib模块。importlib模块可以以编程方式导入模块,我们可以利用它来导入其他目录中的模块。下面是使用importlib导入的示例代码:
  1. import importlib
  2. module = importlib.import_module('.module_name', '/path/to/another/directory')
Python Copy 在这个示例中,我们首先导入了importlib模块。然后,使用importlib.import_module()函数导入另一个目录中的模块,参数中的module_name是要导入的模块名,/path/to/another/directory是另一个目录的路径。通过这种方式,我们可以动态地导入任意目录中的模块。

总结

在本文中,我们介绍了四种方法可以在Python中从另一个目录导入.py文件。无论是通过sys.path.append()函数、相对路径、包还是importlib模块,我们都可以实现这个目标。根据具体的需求和项目结构,我们可以选择最适合的方法。希望本文的内容对你有所帮助,可以在你的Python项目中灵活使用。

Python 3.x | 史上最详解的 导入(import)「建议收藏」-腾讯云开发者社区-腾讯云 (tencent.com)

模块导入与重载——更大的模块故事:属性——模块和命名空间

模块导入是运行代码文件的一种方式,但本书后面还会扩展:模块是 Python 程序中最大的程序结构,且是语言中最关键概念之一。 已经看到,Python 程序由多个模块文件构成,通过导入语句链接在一起,且每个模块文件是一个变量包 —— 也就是说,一个命名空间。同样重要的是,每个模块是一个自包含的命令空间:一个模块文件无法看到定义在另一个文件中的名称除非它明确导入那个文件。因为这个原因,模块用来将代码中的命令冲突 降到最小程度 —— 因为每个文件是一个自包含的命令空间,在一个文件中的名称和另一个文件中的名称即使同名也不会冲突。 事实上,模块是 Python 尽力包裹变量进入不同区域来避免名称冲突的一些方式之一。本书后面将讨论模块和其他命名空间结构 —— 包括被类和函数定义的本地范围。就目前而言,模块将作为一个多次运行代码而无需重新输入的有用方式,且防止了文件中的名称意外地替换彼此。

模块导入与重载——更大的模块故事:属性——注意

_<font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">import</font>_ _<font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">from</font>_ __相比:我应该指出 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">from</font> 语句在某种意义上破坏了模块的命名空间的分隔目的 —— 因为 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">from</font> 将变量从一个文件拷贝到另一个,它能造成同名的变量在导入文件中被覆盖,并且如果覆盖了也不会警告你。这本质上让命令空间挤压到一起,至少对被拷贝的变量如此。

因为这个原因,有些人推荐总是使用 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">import</font> 而非 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">from</font>。然而,我不会那么极端;不仅因为 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">from</font> 输入更少(在交互式提示符那里,这是一个优点),而且它所谓的问题在实践中也相对稀少。另外,这是通过在 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">from</font> 中列出你想要的变量来控制的;只要你理解它们将被分配给目标模块中的值,这就并不比编码赋值语句(另一个你很可能想使用的功能)更危险!

模块导入与重载——使用笔记:导入与重载

因为一些原因,一旦人们发现如何使用 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">import</font> <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">reload</font> 来运行文件,许多人便倾向于只关注这个方法且忘记了其他总是运行当前版本代码的启动选项(如,点击图标,IDLE 目录选项,和系统命令行)。然而,这个方法会很快导致困惑 —— 你需要记住什么时候已经导入过,从而知道是否可以重载,当调用 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">reload</font> 时需要记住使用小括号,并且需要记住在一开始使用 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">reload</font> 来获取当前版本代码来运行。而且,重载不会传递 —— 重载一个模块只会重载那个模块,而不是它可能导入的任何模块 —— 所以有时必须重载多个文件。 因为这些复杂性(和其他我们将稍后探索的,包括在本章前面的注意中简短提及的 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">reload</font>/<font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">from</font> 问题),暂时避免通过导入和重载来启动的诱惑通常是个好主意。比如,在下一节中描述的 IDLE 运行 —> 运行模块 目录选项就提供了一个更简单且更不容易出错的运行文件的方式,而且它总是运行代码的当前版本。系统 shell 命令行提供了类似的好处。如果使用这些其他的技术,你不需要使用 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">reload</font> 另外,在本书的这个节点上如果用不寻常的方式使用模块可能会碰到麻烦。比如,如果想导入一个存储目录不在你当前工作目录的模块文件,将不得不直接跳到第 22 章并学习模块搜索路径。就目前而言,如果必须导入,尝试将所有文件放在当前工作目录来避免复杂性 [8]。 也就是说,导入和重载已经被证明是 Python 类中的流行测试技术,且你也可能倾向使用这个方法。然而,和通常一样,如果你发现自己遇到了无法解决的困难,请停止继续前进!
[8] 如果你太好奇而不能等待,简单的说,Python 在 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">sys.path</font>(在 <font style="color:rgb(133, 128, 128);background-color:rgb(249, 250, 250);">sys</font> 模块中的 Python 目录名字符串列表,它从 PYTHONPATH 环境变量初始化而来,再加上一组标准目录。如果你想导入一个不在你当前工作目录的目录,那个目录通常必须在 PYTHONPATH 设置中被列出。参见第 22 章和附录 A 获取更多细节)中列出的每个目录中搜索导入的模块。