模块,包

本文详细介绍了Python中的模块和包的概念及使用。模块是功能集合,包括内置、第三方和自定义模块,使用模块能提高开发效率和减少代码冗余。模块分为.py文件、C/C++扩展、文件夹(包)。导入模块时,`import`和`from...import...`各有优缺点。包是模块的一种组织形式,通过`__init__.py`文件创建,用于提高程序结构性和可维护性。文章还讨论了模块的搜索路径、循环导入问题以及绝对导入和相对导入的使用场景。
摘要由CSDN通过智能技术生成

一.模块介绍

   1.什么是模块

       模块就是一系列功能的集合体

    2.模块的来源

      1).内置的模块

      2).第三方的模块

      3).自定义模块

    3.模块的类别

      1).使用python编写的.py文件

      2).已被编译为共享库或DLL的C或C++扩展

      3).把一系列模块组织到一起的文件夹(注:文件夹下有一个__init__.py文件,该文件夹称之为包

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

    4.为何要用模块

        1).使用内置的或者第三方模块的好处是:拿来主义,可以极大提升开发效率

        2).使用自定义模块的好处是:可以减少代码冗余(抽取我们自己程序中要公用的一些功能定义成模块,然后程序的各部分组件 都去模块中调用共享的功能

    5.如何用模块

      大前提:一定要区分开谁是执行文件,谁是被导入模块

二.使用模块之import

     1.import的使用 

       文件名是spam.py,模块名则是spam

       首次导入import spam 模块发生3件事

      1).会产生一个模块的名称空间

      2).执行文件spam.py,将执行过程中产生的名字都放到模块的名称空间中

      3).在当前执行文件的名称空间中拿到一个模块名,该名字指向模块的名称空间

         之后导入import spam,都是直接引用第一次导入的成果,不会重新执行文件

         在执行文件中访问模块名称空间中名字的语法:模块名.名字

        print(spam.x)#指明道姓地跟spam要名字x,肯定不会与当前执行文件中的名字冲突

   总结:import导入模块,在使用时必须加上前缀:模块名.

   优点:指明道姓的向某一个名称空间要名字,肯定不会与当前名称空间中的名字冲突

   缺点:但凡应用模块中的名字都需要加前缀,不够简洁

2.被导入的模块有独立的名称空间

 每个模块都是一个独立的名称空间,定义在这个模块中的函数,把这个模块的名称空间当做全局名称空间,这样我们在编写自己的模块时,就不用担心我们定义在自己模块中全局变量会被导入时,与使用者的全局变量冲突

# spam.py:
# --------------------------------------
# print("from the spam.py")
# money = 1000
# def read1():
#     print("spam 模块:", money)
#
# def read2():
#     print("spam 模块")
#     read1()
#
# def change():
#     global money
#     money = 0
#
# -----------------------------------------

import spam
# money=10
# print(spam.money)

"""
from the spam.py
1000
spam 模块: 1000

"""

# def read1():
#     print("------------")
# spam.read1()

"""
from the spam.py
spam 模块: 1000
"""

# import spam
#
# money=1
#
# spam.change()
#
# print(money)

"""
from the spam.py
1

"""

   3.为模块名起别名

     为已经导入的模块起别名的方式对编写可扩展的代码很有用

     import spam as sm

     print(sm.money)

#mysql.py
def sqlparse():
    print('from mysql sqlparse')
#oracle.py
def sqlparse():
    print('from oracle sqlparse')

#test.py
db_type=input('>>: ')
if db_type == 'mysql':
    import mysql as db
elif db_type == 'oracle':
    import oracle as db

db.sqlparse()

   4.在一行导入多个模块(不推荐)

    import sys,os,re

三.使用模块之from....import....

   1.from...import...的使用

       from spam import read1,read2 

       首次导入模块发生3件事

       1).创建一个模块的名称空间

       2).执行文件spam.py,将执行过程中产生的名字都放到模块的名称空间中

       3).在当前执行文件中直接拿到一个名字,该名字与模块名称空间中的名字相对应

   2.from....import与import的对比

      #唯一的区别就是:使用from...import...则是将spam中的名字直接导入到当前的名称空间中,所以在当前名称空间中,直接使用名字就可以了,无需加          前缀:spam.

     总结from...import...:

     优点:使用时,无需再加前缀,更简洁

     缺点:容易与当前名称空间中的名字冲突(覆盖)

  *代表从被导入模块中拿到所有名字(不推荐使用)

   #大部分情况下我们的python程序不应该使用这种导入方式,因为*你不知道你导入什么名字,很有可能会覆盖掉你之前已经定义的名字。而且可读性极其的差,在交互式环境中导入时没有问题

    from spam import *  # 在spam.py中新增一行 __all__=[“money”,"real”]

3.为模块名起别名

  from spam import read1 as r1

  r1()

四.模块循环导入问题

    模块循环/嵌套导入抛出异常的根本原因是由于在python中模块被导入一次之后,就不会重新导入,只会在第一次导入时执行模块内代码

    在我们的项目中应该尽量避免出现循环/嵌套导入,如果出现多个模块都需要共享的数据,可以将共享的数据集中存放到某一个地方

    在程序出现了循环/嵌套导入后的异常分析,解决方法如下

# m1.py
print('正在导入m1')
from m2 import y
x='m1'

# m2.py
print('正在导入m2')
from m1 import x
y='m2'

#run.py
import m1

# 测试一
# 执行run.py会抛出异常
"""
正在导入m1
正在导入m2
Traceback (most recent call last):
  File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/aa.py", line 1, in <module>
    import m1
  File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m1.py", line 2, in <module>
    from m2 import y
  File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m2.py", line 2, in <module>
    from m1 import x
ImportError: cannot import name 'x'

"""


# 测试一结果分析
# 先执行run.py--->执行import m1,开始导入m1并运行其内部代码--->打印内容"正在导入m1"
# --->执行from m2 import y 开始导入m2并运行其内部代码--->打印内容“正在导入m2”--->执行from m1 import x,由于m1已经被导入过了,所以不会重新导入,所以直接去m1中拿x,然而x此时并没有存在于m1中,所以报错
#
#
# 测试二:执行文件不等于导入文件,比如执行m1.py不等于导入了m1
# 直接执行m1.py抛出异常
"""
正在导入m1
正在导入m2
正在导入m1
Traceback (most recent call last):
  File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m1.py", line 2, in <module>
    from m2 import y
  File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m2.py", line 2, in <module>
    from m1 import x
  File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m1.py", line 2, in <module>
    from m2 import y
ImportError: cannot import name 'y'


"""

# #测试二分析
# 执行m1.py,打印“正在导入m1”,执行from m2 import y ,导入m2进而执行m2.py内部代码--->打印"正在导入m2",执行from m1 import x,此时m1是第一次被导入,执行m1.py并不等于导入了m1,于是开始导入m1并执行其内部代码--->打印"正在导入m1",执行from m1 import y,由于m1已经被导入过了,所以无需继续导入而直接问m2要y,然而y此时并没有存在于m2中所以报错

# # 解决方法:
# 方法一:导入语句放到最后
#m1.py
print('正在导入m1')
x='m1'
from m2 import y

#m2.py
print('正在导入m2')
y='m2'
from m1 import x

# 方法二:导入语句放到函数中
#m1.py
print('正在导入m1')
def f1():
    from m2 import y
    print(x,y)
x = 'm1'
f1()

#m2.py
print('正在导入m2')
def f2():
     from m1 import x
     print(x,y)
y = 'm2'

# #run.py
import m1
m1.f1()

  五.py文件区分两种用途:模块与脚本

      编写好的一个python文件可以有两种用途:

      一:脚本.一个文件就是整个程序,用来被执行

      二:模块,文件中存放着一堆功能,用来被导入使用

      python为我们内置了全局变量__name__,

      当文件被当做脚本执行时:__name__等于"__main__"

      当文件被当做模块导入时:__name__等于模块名

    作用:用来控制.py文件在不同的应用场景下执行不同的逻辑

     if __name__=="__main__":

六.模块搜索路径

     模块的查找顺序是:内存中已经加载的模块-->内置模块-->sys.path路径中包含的模块

     注意:环境变量是以当前执行文件为准的

     强调:所有被导入的模块参照环境变量sys.path都是以执行文件为准的

七.包

    1.什么是包?

       包是一种通过使用".模块名"来组织python模块名称空间的方式

       #具体的:包就是一个包含有__init__.py文件的文件夹,所以其实我们创建包的目的就是为了用文件夹将文件/模块组织起来

       #需要强调的是:1.在python3中,即使包下没有__init__.py文件,import包仍然不会报错,而在python2中,包下一定要有该文件,否则import包报错

                                   2.创建包的目的不是为了运行,而是被导入使用,记住,包只是模块的一种形式而已,包的本质就是一种模块

      2.为何要用包

         包的本质就是一个文件夹,那么文件夹唯一的功能就是将文件组织起来

         随着功能越来越多,我们无法将所有的功能都放到一个文件夹中,于是我们使用模块去组织功能,而随着模块越来越多,             我们就需要用文件夹将模块文件组织起来,以此来提高程序的结构性和可维护性

      3.绝对导入和相对导入

         我们的最顶级包glance是写给别人用的,然后在glance包含内部也会有彼此之间互相导入的需求,这时候就有绝对导入和星队导入两种方式:

         绝对导入:以glance作为起始

         相对导入:用.或者..的方式最为起始(只能在一个包中使用,不能用于不同的目录内)

        例如:我们在glance/api/version.py中想要导入glance/cmd/manage.py

在glance/api/version.py

#绝对导入
from glance.cmd import manage
manage.main()

#相对导入
from ..cmd import manage
manage.main()

   4.Python包的相对导入时出现错误的解决方法

        在练习python中package的相对导入时,即:

         from . import xxx 或者  from .. import xxx时会遇到这样的两个错误:

 

SystemError: Parent module '' not loaded, cannot perform relative import

 

ValueError: attempted relative import beyond top-level package

分析:其实这两个错误的原因归根结底是一样的,在涉及到相对导入时,package所对应的文件夹必须正确的被python解释器视作package,而不是普通文件夹,否则由于不被视作package,无法利用package之间的嵌套关系实现python中包的相对导入。

   文件夹被python解释器视作package需要满足两个条件:

   1.文件夹中必须有__init__.py文件,该文件可以为空,但必须存在该文件

   2.不能作为顶层模块来执行该文件夹中的py文件(既不能作为主函数的入口)

补充:在"from YY import XX"这样的代码中,无论是XX还是YY,只要被python解释器视作package,就会首先调用该package的__init__.py文件。如果都是package,则调用顺序是YY,XX

   另外,练习中"from . import XX" 和"from .. import XXX"中的.和.. 可以等同于linux里的当前目录和上一级目录

举个例子:

#目录结构:
#   testIm/
#
#   --__init__.py
#
#   --main.py : from Tom import tom
#
#   --Tom/
#
#     --__init__.py : print("I'm Tom's __init__!")
#
#     --tom.py : from . import tomBrother, from .. import Kate,print("I'm Tom!")
#
#     --tomBrother.py print(I'm Tom's Brother!)
#
#   --Kate/
#
#     --__init__.py : print("I'm Kate's __init__!")
#
#     --kate.py
#运行文件:main.py
#结果:I'm Tom's __init__!
# I'm Tom's Brother!
# Traceback (most recent call last):
# File "D:\PythonLearning\TestIm2\main.py", line 3, in <module>
# from cat import cat
# File "D:\PythonLearning\TestIm2\cat\cat.py", line 4, in <module>
# from .. import dog
# ValueError: attempted relative import beyond top-level package
# >>>

可以看到from . import tomBrother 顺利执行,首先执行了Tom文件夹下的__init__.py文件,后来执行了tomBrother.py文件,但是当执行到"from .. import Kate"时报错,这是因为我们是在TestIm文件夹下把main.py文件作为主函数的入口执行的,因此尽管TestIm文件夹中有__init__.py文件,但是该文件夹不能被python解释器视作package,即Tom package不存在上层package,自然会报错相对导入时超出了最高层级的package。

 修改方法:

 

# test/
#
#   --main.py : from testIm.Tom import tom
#
#   --testIm/
#
#     --__init__.py
#
#     --Tom/
#
#       --__init__.py : print("I'm Tom's __init__!")
#
#       --tom.py : from . import tomBrother, from .. import Kate,print("I'm Tom!")
#
#       --tomBrother.py print(I'm Tom's Brother!)
#
#     --Kate/
#
#     --__init__.py : print("I'm Kate's __init__!")
#
#     --kate.py
#运行文件:main.py
#结果:
# I'm top's __init__!
# I'm Tom's __init__!
# I'm Tom's Brother!!
# I'm Kate's __init__!
# I'm Tom!

    即主函数入口不在TestIm中,则TestIm和其同样包含__init__.py文件的子文件夹都被python解释器视作为package,形成相应的嵌套关系。可以正常使用from . import XXX 和 from .. import XXX。  

   

   

             

     

 

        

   

     

         

        

       

   

  

    

 

 

 

 

 

    

   

 

 

    

       

         

 

    

       

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值