1、模块的介绍与使用模块import

1.1、模块的介绍

1.1.1、什么是模块?


模块就是一组功能的集合体,我们的程序可以导入模块来复用模块里的功能。

 在python中,模块的使用方式都是一样的,但其实细说的话,模块可以分为四个通用类别:
    (1)使用python编写的.py文件
    (2)已被编译为共享库或DLL的C或C++扩展
    (3)把一系列模块组织到一起的文件夹(注:文件夹下有一个__init__.py文件,该文件夹称之为包)
    (4)使用C编写并链接到python解释器的内置模块

 模块有三种来源:
    (1)自带的模块 内置模块 && 标准库
    (2)第三方模块  pip3 install requests
    (3)自定义的模块        

    常见的场景:一个模块就是一个包含了一组功能的python文件,比如spam.py,模块名为spam,可以通过import spam使用。


1.1.2、为何要使用模块


1、从文件级别组织程序,更方便管理
    随着程序的发展,功能越来越多,为了方便管理,我们通常将程序分成一个个的文件,这样做程序的结构更清晰,方便管理。这时我们不仅仅可以把这些文件当做脚本去执行,还可以把他们当做模块来导入到其他的模块中,实现了功能的重复利用

  2、拿来主义,提升开发效率
    同样的原理,我们也可以下载别人写好的模块然后导入到自己的项目中使用,这种拿来主义,可以极大地提升我们的开发效率

 如果你退出python解释器然后重新进入,那么你之前定义的函数或者变量都将丢失,因此我们通常将程序写到文件中以便永久保存下来,需要时就通过python test.py方式去执行,此时test.py被称为脚本script。


1.1.3、实例


以spam.py为例来介绍模块的使用:文件名spam.py,模块名spam

    #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


1.2、使用模块之import

1.2.1、import的使用


模块可以包含可执行的语句和函数的定义,这些语句的目的是初始化模块,它们只在模块名第一次遇到导入import语句时才执行(import语句是可以在程序中的任意位置使用的,且针对同一个模块很import多次,为了防止你重复导入,python的优化手段是:第一次导入后就将模块名加载到内存了,后续的import语句仅是对已经加载到内存中的模块对象增加了一次引用,不会重新执行模块内的语句),如下 

 test.py
    import spam #只在第一次导入时才执行spam.py内代码,此处的显式效果是只打印一次'from the spam.py',当然其他的顶级代码也都被执行了,只不过没有显示效果.
    import spam
    import spam
    import spam

#result:
    from the spam.py


1.2.2、在第一次导入模块时会做三件事,重复导入会直接引用内存中已经加载好的结果


执行文件run.py自定义模块spam.py

(1)执行run.py文件,在被导入的spam.py模块所在文件中执行;

(2)产生spam.py的名称空间(run.py的名称空间),将spam.py运行过程中产生的名字都丢到新产生的模块spam的名称空间中;

(3)在当前文件中产生一个名字spam,该名字指向spam.py的名称空间

#总的来说:
(1)会触发模块文件的运行,产生一个模块的名称空间,将运行模块文件过程中产生的名字都丢到模块的名称空间
(2)在当前名称空间中产生一个名字spam,该名字指向模块的名称空间


python import怎么使用form python from import_sql


1.2.3、被导入模块有独立的名称空间


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


测试一:money与spam.money不冲突


test.py
    import spam
    def read1():
        print('========')
    spam.read1()

执行结果:
    from the spam.py
    spam->read1->money 1000


测试二:read1与spam.read1不冲突


test.py
    import spam
    def read1():
        print('========')
    spam.read1()

执行结果:
    from the spam.py
    spam->read1->money 1000


测试三:执行spam.change()操作的全局变量money仍然是spam中的


test.py
    import spam
    money=1
    spam.change()
    print(money)
    #print(spam.money)  输出的结果就是0

执行结果:
    from the spam.py
    1


1.2.4、为模块名起别名

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


import spam as sm
print(sm.money)


有两中sql模块mysql和oracle,根据用户的输入,选择不同的sql功能


(1)mysql.py
    def sqlparse():
        print('from mysql sqlparse')

(2)oracle.py
    def sqlparse():
        print('from oracle sqlparse')

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

    db.sqlparse()


1.2.5、在一行导入多个模块


import sys,os,re


2、使用模块之from...import...

2.1、from...import...的使用


from spam import read1,read2


2.2、from...import 与import的对比**


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

from...import...的方式有好处也有坏处
    好处:使用起来方便了
    坏处:容易与当前执行文件中的名字冲突


以spam.py为例来介绍模块的使用:文件名spam.py,模块名spam


#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


验证一:当前位置直接使用read1和read2就好了,执行时,仍然以spam.py文件全局名称空间


测试一:导入的函数read1,执行时仍然回到spam.py中寻找全局变量money
    #test.py
    from spam import read1
    money=200
    read1()

    执行结果:
    from the spam.py
    spam->read1->money 1000

测试二:导入的函数read2,执行时需要调用read1(),仍然回到spam.py中找read1()
    #test.py
    from spam import read2
    def read1():
        print('==========')
    read2()

    执行结果:
    from the spam.py
    spam->read2 calling read
    spam->read1->money 1000


验证二:如果当前有重名read1或者read2,那么会有覆盖效果。


测试三:导入的函数read1,被当前位置定义的read1覆盖掉了
    #test.py
    from spam import read1
    def read1():
        print('==========')
    read1()
    '''
    执行结果:
    from the spam.py
    ==========


验证三:导入的方法在执行时,始终是以源文件为准的


from spam import money,read1
    money=100 #将当前位置的名字money绑定到了100
    print(money) #打印当前的名字
    read1() #读取spam.py中的名字money,仍然为1000

    '''
    from the spam.py
    100
    spam->read1->money 1000


2.3、支持as,一行导入多个名字


from spam import read1 as read
    from spam import read1,read2,money


2.4、from...import *


from spam import * 把spam中所有的不是以下划线(_)开头的名字都导入到当前位置
    大部分情况下我们的python程序不应该使用这种导入方式,因为*你不知道你导入什么名字,很有可能会覆盖掉你之前已经定义的名字。而且可读性极其的差,在交互式环境中导入时没有问题。

    from spam import * #将模块spam中所有的名字都导入到当前名称空间
    print(money)
    print(read1)
    print(read2)
    print(change)

    执行结果:
    from the spam.py
    1000
    <function read1 at 0x1012e8158>
    <function read2 at 0x1012e81e0>
    <function change at 0x1012e8268>  

可以使用__all__来控制*(用来发布新版本),在spam.py中新增一行     __all__=['money','read1'] #这样在另外一个文件中用from spam import *就这能导入列表中规定的两个名字


2.5、模块循环导入问题


模块循环/嵌套导入抛出异常的根本原因是由于在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()


小测试(使用import调用的时候,必须要跟上被调用的值)


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

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

run.py
    import m1