Python 模块

Python 模块

image

1. 模块简介

1.1 什么是模块

编写较长程序时,建议用文本编辑器代替解释器,执行文件中的输入内容,这就是编写 脚本 。随着程序越来越长,为了方便维护,最好把脚本拆分成多个文件。编写脚本还一个好处,不同程序调用同一个函数时,不用每次把函数复制到各个程序。为实现这些需求,Python 把各种定义存入一个文件,在脚本或解释器的交互式实例中使用。这个文件就是 模块

模块即是一系列功能的结合体。

1.2 为什么使用模块

提高了代码的可维护性,不用重复造轮子提升开发效率.

1.3 模块的各类

  1. 内置模块(使用python解释器直接可以导入)
  2. 第三方模块(使用pip安装)
  3. 自定义

1.4 模块的表现形式

  • 使用python编写的代码(.py文件),就是平时写的一个python文件

  • 已被编译为共享库或DLL的C或C++扩展

  • 包好一组模块的包(文件夹)

    包其实就是多个py文件(模块)的集合

    包里面通常会含有一个__init__.py文件(在python3中这个文件可以没有)

  • 使用C编写并链接到python解释器的内置模块

2. import句式

导入模块使用关键字importpy文件名,不要加.py.

示例:

# 导入内置模块
>>> import time
>>> time.time()  # 直接使用
1637651203.9467623
#导入自定义
# 代码文件:foo.py 
name = 'Hans'

def hello(name):
    print("Hello, %s" % name)

# 导入    
>>> import foo
>>> foo.
foo.hello(  foo.name    
>>> foo.name
'Hans'
>>> foo.hello(foo.name)
Hello, Hans
>>> foo.hello("Jack")  
Hello, Jack
          
# 同一个模块多次导入
# 代码文件:boo.py
print("hello")

# 导入        
>>> import boo  # 第一次导入,会执行里面的代码。
hello
>>> import boo		# 第二次导入,不会执行
>>> import boo		# 第二次导入,不会执行
>>> import boo		# 第二次导入,不会执行
# 多次导入相同模块 只会执行一次

模块首次导入发生了什么?(以导入boo.py中导入foo.py为例)

# foo.py
name = 'Hans'

def hello(name):
    print("Hello, %s" % name)

# boo.py 
import foo
print("hello world")

foo.name
foo.hello(foo.name)
foo.hello("Jack")

# 执行结果:
hello world
Hello, Hans
Hello, Jack
  1. 运行导入文件(boo.py)产生该文件的全局名称空间
  2. 运行foo.py
  3. 产生foo.py全局名称空间 运行foo.py文件内代码 将产生的名字全部存档于foo.py名称空间
  4. 在导入文件名称空间产生一个foo的名字指向foo.py全局名称空间

image

import方法导入模块后就可以使用模块中的所有的变量名或函数名,而且绝对不会冲突,因为调用的时候已经指定了要使用哪个包中的那个变量或函数

3. from…import…句式

from...import...句式为从哪个包或模块中导入哪个模块或功能。

示例:

# foo.py代码:
name = 'Hans'

def hello(name):
    print("Hello, %s" % name)

def hi():
    print("Hi, world")
    
# boo.py代码:    
from foo import hi  #在boo中只使用foo的hi功能
print("hello world")

hi()

# 执行结果:
hello world
Hi, world

# 代码 boo.py 
from foo import hi
from foo import hi
from foo import hi
执行结果:
from foo
# from...import...多次导入也只会导入一次

使用from...import...导入:

  1. 先产生执行文件的全局名称空间
  2. 执行模块文件 产生模块的全局名称空间
  3. 将模块中执行之后产生的名字全部存档于模块名称空间中
  4. 在执行文件中有一个hi执行模块名称空间中hi指向的值

导入

# foo.py 代码
print("from foo")
name = 'Hans'

def hello(name):
    print("Hello, %s" % name)

def hi():
    print("Hi, world")

# boo.py 代码
    
from foo import hi
print("hello world")

def hi():
    print("from boo hi")

hi()
# 执行结果:
from foo
hello world
from boo hi   # 发现执行hi()的结果为boo.py中的函数不是从foo.py中导入进来的hi

from...import...指定的导入某个名字

在使用的时候直接写名字即可 但是当前名称空间有相同名字的时候,就会产生冲突 使用的就变成了当前名称空间

4. 导入方式的扩展

4.1 使用别名

# import导入
>>> import foo as f  # 把foo定义别名为f,这时只能调用f,如果再调用foo就会报错,说foo没有定义
>>> f.name
'Hans'

# from ... import ...导入
>>> from foo import hi as h		# 把hi定义为别名为h,调用的时候直接使用h即可,同理hi也不能使用
>>> h()
Hi, world

4.2 连续导入

# import导入
>>> import sys
>>> import os
# 上面的导入方式可以写成下面:
>>> import sys, os  # 这种方式和上面的方式功能是一样的

# from ... import ...导入
>>> from foo import hello
>>> from foo import hi
# 上面的导入方式可以写成下面:
>>> from foo import hello, hi  # 这种方式和上面的方式功能是一样的

import使用连续导入多个模块时,如果多个模块功能相似或者属于同一个系列时推荐使用。

如果功能不同并且不属于一个系列 那么推荐分行导入

4.3 通用导入

如果使用from ... import ...方式导入一个模块里全部功能时,最基本的方法是依次导入
>>> from foo import hello, hi, name 
# 或
>>> from foo import hello
>>> from foo import hi
>>> from foo import name 

#可以使用* 号把一个模块里的全部功能都导入
>>> from foo import * 


# 如果一个模块里有三个功能,在使用from ... import ... 想让人用其中两个可以使用__all__
# 代码:
print("from foo")
name = 'Hans'

def hello(name):
    print("from foo. Hello, %s" % name)

def hi():
    print("from foo Hi")

def play():
    print("from foo play")

__all__ = ['hi', 'play']  # 在被导入的模块文件中可以使用__all__指定可以被导入使用的名字

# 执行:
>>> from foo import *
from foo
>>> hi()
from foo Hi
>>> play()
from foo play
>>> hello("Hans")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'hello' is not define

4.4 判断py文件是作为模块文件还是执行文件

可以使用函数自带的__name__方法

# foo.py 代码 
print("from foo")
name = 'Hans'

def hello(name):
    print("from foo. Hello, %s" % name)

def hi():
    print("from foo Hi")

def play():
    print("from foo play")

print("__name__: %s" % __name__)

# 执行如果:
from foo
__name__: __main__
# 如果foo.py是直接执行时。__name__为 __main__  

# 如果foo.py 当成模块在别的文件里导入时:
# importTest.py  代码
import foo

#执行结果:
from foo
__name__: foo   
# 如果foo.py文件是被当做模块导入则返回模块名

# 
# 一个py文件当成模块被导入时,它会直接执行py文件里的全部代码,可以利用__name__来判断它是否被当成模块导入,如果是则不执行
# 代码 foo.py
def hello(name):
    print("from foo. Hello, %s" % name)

def hi():
    print("from foo Hi")

def play():
    print("from foo play")

if __name__ == '__main__':
    print("from foo")
    name = 'Hans'
# 代码 importTest.py 
import foo
# 执行结果:

#之前导入的时候会直接打印: from foo

5. 模块导入的顺序

模块导入的顺序:

  1. 先从内存中查找
  2. 再去内置模块中查找
  3. 最后去sys.path系统路径查找(自定义模块)

如果都没有查找到则报错

# 1.在内存中查找
# foo.py 代码:

def hello(name):
    print("from foo. Hello, %s" % name)

def hi():
    print("from foo Hi")

def play():
    print("from foo play")

if __name__ == '__main__':
    print("from foo")
    name = 'Hans'
# importTest.py 代码:

from foo import hello
import time 
print("Hello")
time.sleep(10)
hello("time")

# 执行结果:
Hello
				#在time.sleep(10)的时候把foo.py删除,这时foo.py已经加载到内存中,所以下面依然执行
from foo. Hello, time

#如果再执行则会报错

# 2.再去内置模块中查找
# 可以自己定义一个和内置模块同名的模块,看看导入的是谁

# 自己编写:time.py 代码:
print("time...")

# boo.py 代码: 
import time
print(time)

# 执行结果:   
<module 'time' (built-in)>
# 发现time为内置的模块,所以在给py文件命名的时候不要与内置模块名冲突

# sys.path系统路径查找
>>> import sys
>>> sys.path  
['', '/usr/lib64/python36.zip', '/usr/lib64/python3.6', '/usr/lib64/python3.6/lib-dynload', '/usr/local/lib64/python3.6/site-packages', '/usr/local/lib/python3.6/site-packages', '/usr/local/lib/python3.6/site-packages/cloud_init-17.1-py3.6.egg', '/usr/lib/python3.6/site-packages', '/usr/lib64/python3.6/site-packages']

# ''为当前目录,然后依次查找

当某个自定义模块查找不到的时候解决方案:

  1. 自己手动将该模块所在的路径添加到sys.path中

    # 查看当前目录
    [root@hans_tencent_centos82 tmp]# pwd
    /tmp
    [root@hans_tencent_centos82 tmp]# python3
    >>> import foo
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ModuleNotFoundError: No module named 'foo'
    # 提示没有foo模块。
    # 查找foo.py模块在哪
    [root@hans_tencent_centos82 module]# pwd
    /tmp/module
    [root@hans_tencent_centos82 module]# ls -lrt foo.py 
    -rw-r--r-- 1 root root 202 Nov 23 16:54 foo.py
    # foo.py在/tmp/module目录下。
    # /tmp/module加入到sys.path
    >>> import sys
    >>> sys.path.append('/tmp/module')
    >>> import foo
    >>> sys.path
    ['', '/usr/lib64/python36.zip', '/usr/lib64/python3.6', '/usr/lib64/python3.6/lib-dynload', '/usr/local/lib64/python3.6/site-packages', '/usr/local/lib/python3.6/site-packages', '/usr/local/lib/python3.6/site-packages/cloud_init-17.1-py3.6.egg', '/usr/lib/python3.6/site-packages', '/usr/lib64/python3.6/site-packages', '/tmp/module']
    # 可以看到 '/tmp/module'添加到sys.path
    
  2. 使用from...import...句式

    from 文件夹名称.文件夹名称 import 模块名
    from 文件夹名称.模块名称 import 名字

    # from 文件夹名称.文件夹名称 import 模块名
    
    #  foo.py在/tmp/module目录下。
    # 当前目录为/tmp
    # 使用from...import...
    # 执行结果:
    >>> from module import foo
    >>> foo.hi()
    from foo Hi
    
    #当前在/tmp下,而foo.py在/tmp/module/test/下
    [root@hans_tencent_centos82 tmp]# ls -lrt /tmp/module/test/foo.py 
    -rw-r--r-- 1 root root 202 Nov 23 16:54 /tmp/module/test/foo.py
    >>> from module.test import foo
    >>> foo.play()
    from foo play
    
    # from 文件夹名称.模块名称 import 名字
    
    #只导入foo模块中的一个功能:
    >>> from module.test.foo import play
    >>> play()
    from foo play
    

    6. 循环导入

    不允许出现循环导入

    真要出现了,一般解决方法(就是明知道有循环导入了还是让它运行,一错再错方法):

    1. 调换顺序
      将彼此导入的句式放在代码的最后
    2. 函数形式
      将导入的句式放入函数体代码 等待所有的名字加载完毕之后再调用
Tags: