Python 进阶_模块 & 包

简介: 目录目录模块的搜索路径和路径搜索搜索路径命名空间和变量作用域的比较变量名的查找覆盖导入模块import 语句from-import 语句扩展的 import 语句 as自动载入模块模块导入的特性模块内建函数__import__...

目录

模块的搜索路径和路径搜索

搜索路径

默认的模块搜索路径在 Python 解析器编译安装时被指定, 我们可以通过 sys 模块来查看和修改它:

In [4]: sys.path
Out[4]: 
['',
 '/usr/bin',
 '/usr/lib/python2.7',
 '/usr/lib/python2.7/plat-x86_64-linux-gnu',
 '/usr/lib/python2.7/lib-tk',
 '/usr/lib/python2.7/lib-old',
 '/usr/lib/python2.7/lib-dynload',
 '/usr/local/lib/python2.7/dist-packages',
 '/opt/stack/keystone',
 '/opt/stack/glance',
 '/opt/stack/cinder',
 '/opt/stack/nova',
 '/opt/stack/horizon',
 '/opt/stack/tempest',
 '/opt/stack/oslo.vmware',
 '/usr/lib/python2.7/dist-packages',
 '/usr/lib/python2.7/dist-packages/IPython/extensions']

存在于搜索路径列表的路径下的模块就可以直接被 import 了.
我们还能够通过 sys.models 可以找到当前导入了那些模块和它来自萨满路径和地方.
NOTE: 这个路径每个人都不尽相同, 在安装 Openstack 项目时, 每一个项目的根目录都会加入到该目录列表中

命名空间和变量作用域的比较

命名空间: 是变量名(标识符)到实际对象的映射, 是一个字典. Python 的命名空间类型有 内建命名空间/全局命名空间/嵌套命名空间/局部命名空间.
Python 解析器启动时, 首先会加载 内建命名空间 , 该空间的变量名映射被包含在 __builtin__ 模块中, 我们可以通过 __builtin__.__dict__ 来查看其包含的变量字典, 主要包含了 ERROR/内建函数 等类型.

注意: __builtin__ 模块和 __builtins__ 模块的名称非常相似, 在标准的 Python 环境中 __builtins__ 含有 __builtin__ 内的所有变量名映射, 却别在于 __builtins__ 是可以被修改的, 用于创建一个 “沙盒” 环境, 但少有人用, 所以两者并不相等.

作用域: 变量名有效的区域定义. 指定了一个变量名在一个指定的区域内有效.

两者的区别: 作用域和命名空间非常相似, 一个作用域的创建伴随着新的命名空间的生成, 其一般是具有对应的关系. 所以往往命名空间中含有的变量名在对应的作用域中都是有效的. 但本质的区别是: 命名空间是纯粹意义上的变量名和对象的映射关系, 而作用域还指出了在代码中的那个物理位置(模块内/类内/函数类)可以访问到命名空间的变量名, 从而得到其映射的对象.

无限制的命名空间: Python 的一个特性在于我们可以动态的为一个函数对象添加变量, 这种变量也称之为实例属性. 所以 Python 的命名空间是无限制的.

In [9]: def func():
   ...:     print "This is a func"
   ...:     

In [10]: func.__dict__
Out[10]: {}

In [13]: func.name = 'JMilkFan'

In [14]: func.__dict__
Out[14]: {'name': 'JMilkFan'}

变量名的查找/覆盖

当我们 call 一个变量名时, Python 解析器的查找顺序如下:
1. 先从局部命名空间开始找, 如果找到了, 则引用变量名映射的对象. 若没有找到, 则进行下一步
2. 到嵌套命名空间找, 如果没有找到, 则进行下一步
3. 到全局命名空间找, 如果没有找到, 则进行下一步
4. 到内置命名空间找, 如果没有找到, 则进入下一步

从这个变量名查找算法可以看出, 当我们在代码物理位置定义了一个变量时, 这个变量就会添加到命名空间中. 而且这个命名空间为变量名的查找提供了清单. 更重要的一点是这个查找算法还有效的覆盖了外层作用域的同名变量. 局部作用域 覆盖 嵌套作用域 覆盖 全局作用域 覆盖 内置作用域.

导入模块

import 语句

import module1[, module2, ..., moduleN]

Python 允许一行导入多个模块, 但那这并不符合 PEP8 的编程标准, 所以建议使用多行导入的方式.

导入模块类型的顺序(中间已单空行隔开):
1. Python 标准库模块
2. Python 第三方模块
3. Python 自定义模块

NOTE: 模块的导入遵循作用域的原则, 在顶格导入的模块, 那么模块的作用域是全局的. 在函数或类内导入的模块, 其作用域就是局部或嵌套作用域.

from-import 语句

如果你希望从一个模块中导入指定属性, 或希望从一个 package 中导入一个模块, 都可以使用 form-import 语句.

from-import 语句也支持一行导入多个模块或属性, EG.

from package import (module1, module2, module3, ..., moduleN)

from-import 语句的好处在于: 针对性的导入可以提高效率和节省空间

NOTE: 我们一般建议导入到模块即可, 非特殊情况不要导入包或模块内的属性

扩展的 import 语句 as

也称之为别名, 模块之间也会经常出现同名的情况, 如果出现同一个模块中导入了两个或多个同名模块的情况, 那么应该为每个相同的模块取一个别名来有效区分. 除此之外, 如果模块的全称过长, 我们也可以为其取一个短小的别名. EG.

import module1 as module_one

from package import module1 as module_two

import Tkinter as tk

自动载入模块

Python 解析器在标准模式下启动会自动的导入一个模块, 例如 __bulitin__. 我们可以通过 sys.modules 来查看已经被导入到当前环境下模块字典, key 为模块名, value 为模块路径.

import sys
sys.modules.keys()

模块导入的特性

  • 加载模块时执行文件
    在导入模块时, 其顶格的代码(全局变量/类定义/函数定义…)会被执行, 所以不建议写太多的顶格代码, 应尽量多的将顶格代码收入函数或类体内.

  • 导入和加载模块是不一样的
    一个模块可以被导入多次, 但正常情况下只能被加载一次. 当然除了手动加载之外. EG: 你需要在一个模块中导入 5 个模块, 其中之一为 sys 模块, 然后另外的 4 个模块也需要导入 sys 模块, 这种情况下, sys 模块只会被加载一次, 但却会被导入 5 次.

  • 导入到当前命名空间的变量
    通过 from-import 导入到当前命名空间的模块变量是不需要通过句点标识符来调用的, 直接就可以引用. 但是不建议通过 from-import 来直接导入模块变量, 这样容易污染当前的命名空间, 可能导致覆盖一个已经存在的具有相同名字的对象.

模块内建函数

__import__()

格式:

__import__(module_name[, globals[, locals[, fromlist]]])
# globals 是全局命名空间的字典 ⇒ globals()
# locals 是局部命名空间的字典 ⇒ locals()
# fromlist 是 from-import 语句导入的变量名的列表 ⇒ []

当我们执行 import 语句的时候, 实际上是调用了 __import__() 内置函数, EG:

import sys 
# 等效于
sys = __import__('sys')

globals() / locals() / reload()

  • globals(): 返回调用者的全局命名空间的字典
  • locals(): 返回调用者的局部命名空间的字典
    NOTE: 如果在全局命名空间调用上面两个内置函数, 那么两者返回的字典是相同的

  • reload(): 重新导入一个已经被导入了的模块
    NOTE: reload() 由两个使用限制

    1. 模块必须全部被导入, 不能重载通过 from-import 来导入模块属性的模块
    2. 该模块必须是已经被导入了的, 才能被重载
      模块的顶格代码在导入的时候会执行且仅执行一次, 再次导入的时候不会被执行, 如果希望再次执行的话, 可以使用 reload()

Package 包

模块是代码的组织形式, 包是模块的组织形式. 如果在一个目录中创建了 __init__.py 文件, 那么 Python 解析器会将该目录标识为一个包 Package.
NOTE: 我们一般在使用 import 语句的时候, 建议导入到模块级别, 不要导入模块属性, 也不要导入包.

__init__.py

__init__.py 文件是包的标识文件, 作为包的初始化模块, from-import 包下的模块会子包时, 需要用到该初始化模块, 所以现在如果一个包中没有该模块时, 会触发 ImportWarning 信息.

import package

如果我们不需要使用初始化模块__init__.py时. 该模块的内容为空, 如果我们需要使用该模块时, import packageName == import packageName._init_.
而且需要注意的是: 如果我们执行 from package import * 时, 会将该包下的所有模块和属性导入, 实际上这种做法是不科学的. 所以在包的初始化模块文件中有一个特殊属性 __all__ , __all__ 属性由一个模块名字组成的列表组成, 这个列表中包含了所有在执行全导入时应该被导入的模块和属性的名字.

相关文章
|
18天前
|
开发者 Python
如何在Python中管理模块和包的依赖关系?
在实际开发中,通常会结合多种方法来管理模块和包的依赖关系,以确保项目的顺利进行和可维护性。同时,要及时更新和解决依赖冲突等问题,以保证代码的稳定性和可靠性
33 4
|
16天前
|
算法 数据安全/隐私保护 开发者
马特赛特旋转算法:Python的随机模块背后的力量
马特赛特旋转算法是Python `random`模块的核心,由松本真和西村拓士于1997年提出。它基于线性反馈移位寄存器,具有超长周期和高维均匀性,适用于模拟、密码学等领域。Python中通过设置种子值初始化状态数组,经状态更新和输出提取生成随机数,代码简单高效。
101 63
|
18天前
|
测试技术 Python
手动解决Python模块和包依赖冲突的具体步骤是什么?
需要注意的是,手动解决依赖冲突可能需要一定的时间和经验,并且需要谨慎操作,避免引入新的问题。在实际操作中,还可以结合使用其他方法,如虚拟环境等,来更好地管理和解决依赖冲突😉。
|
18天前
|
持续交付 Python
如何在Python中自动解决模块和包的依赖冲突?
完全自动解决所有依赖冲突可能并不总是可行,特别是在复杂的项目中。有时候仍然需要人工干预和判断。自动解决的方法主要是提供辅助和便捷,但不能完全替代人工的分析和决策😉。
|
24天前
|
JSON Linux 数据格式
Python模块:从入门到精通,只需一篇文章!
Python中的模块是将相关代码组织在一起的单元,便于重用和维护。模块可以是Python文件或C/C++扩展,Python标准库中包含大量模块,如os、sys、time等,用于执行各种任务。定义模块只需创建.py文件并编写代码,导入模块使用import语句。此外,Python还支持自定义模块和包,以及虚拟环境来管理项目依赖。
Python模块:从入门到精通,只需一篇文章!
|
10天前
|
Python 容器
[oeasy]python048_用变量赋值_连等赋值_解包赋值_unpack_assignment _
本文介绍了Python中变量赋值的不同方式,包括使用字面量和另一个变量进行赋值。通过`id()`函数展示了变量在内存中的唯一地址,并探讨了变量、模块、函数及类类型的地址特性。文章还讲解了连等赋值和解包赋值的概念,以及如何查看已声明的变量。最后总结了所有对象(如变量、模块、函数、类)都有其类型且在内存中有唯一的引用地址,构成了Python系统的基石。
20 5
|
18天前
|
Python
Python的模块和包
总之,模块和包是 Python 编程中非常重要的概念,掌握它们可以帮助我们更好地组织和管理代码,提高开发效率和代码质量
29 5
|
18天前
|
数据可视化 Python
如何在Python中解决模块和包的依赖冲突?
解决模块和包的依赖冲突需要综合运用多种方法,并且需要团队成员的共同努力和协作。通过合理的管理和解决冲突,可以提高项目的稳定性和可扩展性
|
22天前
|
JavaScript 前端开发 Python
python中的OS模块的基本使用
欢迎来到瑞雨溪的博客,一名热爱JavaScript与Vue的大一学生。博客分享前端技术及全栈开发经验,持续更新中,期待您的关注和支持!🎉🎉🎉
28 0
|
22天前
|
JavaScript 前端开发 Python
python中的platform模块的基本使用
欢迎来到瑞雨溪的博客,一名热爱JavaScript与Vue的大一学生。博客分享前端技术,助你成长。关注我,持续更新中!🎉🎉🎉
20 0