自定义模块

自定义模块

模块的定义与分类

定义 : 模块,就是一些列常用功能的集合体。

模块的作用(优点):

  • 程序的结构更清晰,方便管理,实现了功能的重复利用
  • 拿来主义,提升开发效率
  • 避免重复造轮子

script (脚本)

  • 将程序写到文件中以便永久保存下来,需要时就通过python test.py方式去执行,此时test.py被称为脚本script,脚本就是一个python文件

模块的分类

第一类 : 内置模块 (标准库)

  • 由python解释器提供,例 time 模块 os 模块,标准库模块约(200多个,每个模块又有很多功能)

第二类 : 第三方模块 (第三方库)

  • python大神写的非常好用的模块,必须通过 pip install 指令安装的模块,比如BeautfulSoup, Django,等等。大概有6000多个

第三类 : 自定义模块

  • 自己在项目中定义的一些模块

import 直接引用

import 使用

print('from the tbjx.py')   # 整体是 tbjx 模块
name = '太白金星'

def read1():
    print('tbjx模块:',name)

def read2():
    print('tbjx模块')
    read1()

def change():
    global name
    name = 'barry'

模块包含可执行的语句和函数的定义,这些语句的目的是初始化模块,它们只在模块名第一次遇到导入import语句时才执行(import语句是可以在程序中的任意位置使用的,且针对同一个模块import很多次,为了防止你重复导入,python的优化手段是:第一次导入后就将模块名加载到内存了,后续的import语句仅是对已经加载到内存中的模块对象增加了一次引用,不会重新执行模块内的语句),如下 import tbjx #只在第一次导入时才执行tbjx.py内代码,此处的显式效果是只打印一次'from the tbjx.py',当然其他的顶级代码也都被执行了,只不过没有显示效果.

第一次导入模块执行三件事

  • 将模块文件加载到内存.(在执行文件中执行一次此模块)
  • 在内存中创建 一个 以 导入模块 命名的名称空间.
  • 通过 导入模块 名称空间的 模块 名字 + . 等方式引用此模块的名字(变量,函数名,类名等等).
  • ps:重复导入会直接引用内存中已经加载好的结果

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

  • 每个模块都是一个独立的名称空间,定义在这个模块中的函数,把这个模块的名称空间当做全局名称空间,这样我们在编写自己的模块时,就不用担心我们定义在自己模块中全局变量会在被导入时,与使用者的全局变量冲突。
  • 坑 : 通过 导入模块 名称空间的 模块 名字 + . 的方式引用 此模块的名字时,一定一定是从 此模块 中寻找.
  • 通过 import 引用模块 他有自己的 独立名称空间 , 与当前 执行文件 没有关系.

为模块起别名

  • 别名其实就是一个外号
import tbjx as t
t.read1()

别名 应用 :

  • 好处可以将很长的模块名改成很短,方便使用
  • 有利于代码的扩展和优化 , 便于统一维护

导入多个模块

开发过程中,免不了会在一个文件中,导入多个模块,推荐写法是一个一个导入 , 即分开多行导入

多行导入优点:

  • 易于阅读 / 易于编辑 / 易于搜索 / 易于维护
import os,sys,json   # 这样写可以但是不推荐

# 推荐写法

import os
import sys
import json

导入模块顺序

内置模块 → 第三方模块 → 自定义模块

  • 模块的导入遵循PEP8规范:所有的模块导入都应该尽量放在这个文件的 开头

from ... import ...

from ... import ... 使用

# from ..(模块名). import . (模块里的内容).. 的使用示例。
from tbjx import name, read1  #相当于从tbjx模块的全局空间中将name,read1变量与值的对应关系复制到当前执行文件的全局名称空间中.
print(name)
read1()
'''
执行结果:
from the tbjx.py
太白金星
tbjx模块: 太白金星
'''

from...import... 与import对比

唯一的区别就是

  • 使用from...import...则是将 导入模块 中的 变量名字 直接导入到 当前的 名称空间 中,所以在当前名称空间中,直接使用名字就可以了、无需加前缀:模块名 .
  • 好处:使用起来方便了
  • 坏处:容易与当前执行文件中的名字冲突 / 如果当前空间有变量名与导入模块的变量相同 , 后者将把前者覆盖 即↓
  • 执行文件有与模块同名的变量或者函数名,会有 覆盖效果 由下往上覆盖 ↑

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

一行导入多个 / 起别名

from tbjx import read1,read2,name

from ... import * (别单独用) 与 __all__ 结合使用

__all__

  • 将模块中所有的变量名字复制过来,
  • 容易覆盖
  • __all__ 结合使用 __all__ 写在 模块文件
__all__=['money','read1'] #这样在另外一个文件中用from spam import *就这能导入列表中规定的两个名字

py文件的两种功能

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

脚本( 承载文件 ) / 执行文件

__name__ (在模块中调试数据)

  • 一个文件就是整个程序,用来被执行
  • 直接打印 __name__ 返回值为 ' __main__ '

模块 / 被执行文件

  • 文件中存放着一堆功能,用来被导入使用
  • 直接打印 __name__ 返回值为 模块名

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

  • 当文件当做脚本执行时: __name__ 等于' __main__ '
  • 当文件当做模块被执行时: __name__ 等于模块名

作用:

  • 用来控制.py文件在不同的应用场景下执行不同的逻辑(或者是在模块文件中测试代码

模块循环导入问题

#创建一个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()

模块的搜索路径

内存中已经加载的模块 > 内置模块 > sys.path路径中包含的模块

import sys
# print(sys.path)
sys.path.append(r'D:s15')
# import tbjx
import tbjx1
tbjx1.read1()

需要特别注意的是:我们自定义的模块名不应该与系统内置模块重名。虽然每次都说,但是仍然会有人不停的犯错

原文地址:https://www.cnblogs.com/fanxss/p/11051640.html