Python自学笔记-第五章模块(上)

Python自学笔记-第五章模块(上)

当然不是所有的程序都有相关的.py文件,比如sys模块就是内置于Python中的,而且有些内置模块是由C语言编写的。

主要有如下作用:

  • 代码重用:我们知道当一段代码需要用到两次的时候,我们就需要写一个函数了这是一个道理。
  • 避免变量名的冲突:每个模块都将变量名封装进了自己包含的软件包,这可以避免变量名的冲突。除非使用精确导入。

模块导入的语法1:

import module1
import module1,module2,...moduleN
import module1 as alias1

模块导入的语法2:

from module1 import object1,object,...objectN

1.1.Import方式

解释器执行到import语句, 如果在搜索路径中找到了指定的模块, 就会加载它。如果模块是被第一次导入, 它将被加载并执行

1.1.1.示例

本节通过使用PyCharm新建个临时项目,演示导入自定义模块的用法和注意事项

  1. 工程目录,其中src是即为包(后续讲解)

  2. 两个Py文件内容如下
    test.py

#!/usr/bin/env python3 
#-*- coding:utf-8 -*-
import src.hello

hello.py

#!/usr/bin/env python3 
#-*- coding:utf-8 -*-
print("Hello World")
def print_hello():
    print("print_hello")
  1. 运行结果,运行时第一次加载并打印信息。

1.1.2.Import as语句

有时候导入的模块或是模块属性名称已经在程序中使用了, 或者不想使用导入的名字。可能是太长不便输入什么的等。 一个普遍的解决方案是把模块赋值给一个变量。

test.py

#!/usr/bin/env python3 
#-*- coding:utf-8 -*-
import src.hello as hello
#src.hello.print_hello()
hello.print_hello()

1.2.from-import方式

导入指定的模块属性。 也就是把指定名称导入到当前作用域。
test.py

#!/usr/bin/env python3 
#-*- coding:utf-8 -*-
from src.hello import print_hello
print_hello()

但是要特别注意使用from时,会将hello.py中变量,函数都导入到test.py命名空间中,容易造成命名空间的污染。使用时要特别注意。

1.2.1.from…import* 语句

把一个模块的所有内容全都导入到当前的命名空间也是可行的,只需使用如下声明:

from modname import *

例如我们想一次性引入 math 模块中所有的东西,语句如下:

from math import *

1.3.模块导入过程总结

  1. 找到这个需导入的模块;
  2. 判断这个模块是否被导入过;
    • 如果没有被导入过:创建一个属于这个模块的命名空间;如果用户没有定义变量来引用这个模块的内存地址的话,那么就使用模块的名称来引用这个模块的内存地址;如果用户使用as来指定变量接受这个内存地址的话,那么就将内存地址赋值给这个变量;且下文在调用时只能使用这个变量进行调用不能再使用模块名进行调用了。然后执行这个模块中的代码;
    • 如果该模块已经被导入过:那么解释器不会重新执行模块内的语句,后续的import语句仅仅是对已经加载到内存中的模块的对象增加一次引用;

1.4."main方法"

类似于Java程序,如果某个模块是程序的入口,而非导入时,则需要添加个‘main方法’,从而控制程序的运行。
hello.py举例:

1.4.1.未添加"main方法"

#!/usr/bin/env python3 
# -*- coding:utf-8 -*-

print("Hello World")

def print_hello():
    print("print_hello")

def print_hello1():
    print("print_hello1")

此时,在Pycharm右键运行的结果

只有print会执行,函数均为执行。

1.4.2.添加"main"方法

hello.py修改为:

print("Hello World")

def print_hello():
    print("print_hello")

def print_hello1():
    print("print_hello1")

if __name__ == "__main__":
    print_hello1()
    print_hello()

此时结果:

1.4.3.__main__

  1. __name__这个系统变量显示了当前模块执行过程中的名称,如果当前程序运行在这个模块中,__name__的名称就是__main__。如果不是,则为这个模块的名称。
  2. __main__一般作为函数的入口,类似于Java语言,尤其在大型工程中,常常有if __name__ == "__main__":来表明整个工程开始运行的入口。

1.5.模块的搜索路径

当导入一个模块时,Python解析器对模块位置的搜索路径都保存在 system 模块的 sys.path 变量中。意为着,我们开发的py文件必须放置在这个列表中的某个路径下。

import sys
print(sys.path)

["D:Pythonpython3.6.5python36.zip",
"D:Pythonpython3.6.5DLLs",
"D:Pythonpython3.6.5lib",
"D:Pythonpython3.6.5",
"",
"D:Pythonpython3.6.5libsite-packages",
"D:Pythonpython3.6.5libsite-packageswin32", "D:Pythonpython3.6.5libsite-packageswin32lib", "D:Pythonpython3.6.5libsite-packagesPythonwin", "D:Pythonpython3.6.5libsite-packagesIPythonextensions",
"C:UsersAdministrator.ipython"]

或者可以直接改动 sys.path 变量,将代码路径,添加至 sys.path 变量中。(此种方法一般不使)

1.5.1.搜索路径

首先,简述下Python的自动搜索路径是这样的:

  1. 程序的根目录
  2. PYTHONPATH环境变量设置的目录
  3. 标准库的目录
  4. 任何能够找到的.pth文件的内容
  5. 第三方扩展的site-package目录 最终,这五个部分的拼接成为了sys.path,其中第一和第三、第五部分是自动定义的。
  • 根目录(自动)
    Python首先在根目录搜索要导入的文件。这个根目录的入口依赖于你怎么运行代码;当你运行一个程序时,这个入口就是程序运行入口(top-level script file)文件所在的目录;当你用交互式窗口期运行代码时,这个入口就是你所在的工作目录。

  • PYTHONPATH 目录(可配置的)
    接下来,python会搜索PYTHONPATH环境变量里列出的所有目录,因为这个搜索在标准库之前,所以要小心不要覆盖一些标准库的同名模块。

  • 标准库目录(自动)
    这个没什么好说的,pyton会自动搜寻标准库模块所在的目录。

  • .pth文件列出的目录(可配置的)
    这是用的比较少的一个python特性。它允许用户以每行一个的方式列出搜索路径,它和PYTHONPATH环境变量的不同是会在标准库路径之后搜索;而且它是针对这个python安装的,而不是针对用户的(环境变量会随着用户的不同而不同)。
    可以通过以下代码找到.pth文件可以放置的位置:

import site
site.getsitepackages()

["D:Pythonpython3.6.5",
"D:Pythonpython3.6.5libsite-packages"]

  • Lib/site-package目录(自动)
    最后,Python会在搜索路径上自动加上site-packages目录,这一般是第三方扩展安装的地方,一般是由distutils工具发布的。

所以总结,标准的实现方法是在PYTHONPATH环境变量中设置模块的目录。

2.包

简单的说,包就是一个目录,其中包含一组模块和一个__init__.py文件。

这里src就是指一个包:

注意:

  • 必须保证顶级模块名(这里是src)不与标准库中的任何顶级名相同,以避免名称冲突。(在Liunx可以通过将模块名称的首字母大写实现,)

2.1.dir

dir函数,可以查看模块包含的内容,它会将对象的所有特性(以及模块的所有函数,类,变量等)列出。

import copy
dir(copy)

["Error","__all__","__builtins__","__cached__","__doc__","__file__","__loader__","__name__","__package__", "__spec__", "_copy_dispatch",_copy_immutable", "_deepcopy_atomic","_deepcopy_dict","_deepcopy_dispatch","_deepcopy_list", "_deepcopy_method","_deepcopy_tuple","_keep_alive","_reconstruct","copy", "deepcopy","dispatch_table","error"]

2.2.__all__变量

__all__主要控制from xxx import * 的行为。用来定义模块中对于from XXX import * 时要对外导出的符号,即要暴露的接口,但它只对import * 起作用,对from XXX import XXX不起作用。

如果一个模块 spam 没有定义__all__,执行 from spam import * 的时候会将 spam 中非下划线开头的成员都导入当前命名空间中,这样当然就有可能弄脏当前命名空间。如果显式声明了 __all_后,import * 就只会导入 __all__ 列出的成员。如果 __all__ 定义有误,列出的成员不存在,还会明确地抛出异常,而不是默默忽略。

hello.py

__all__ = ["print_hello"]

def print_hello():
    print("print_hello")

def print_hello1():
    print("print_hello1")

test.py

from src.hello import *
print_hello()
print_hello1()

这是print_hello1() 提示错误:

修改为import hello 方式,则__all__无效:

from src import hello
hello.print_hello()
hello.print_hello1()


更多文章,请关注: