7.16 模块

一。模块

  1.什么是模块?

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

  模块的三种来源:

    1.内置的,也就是python解释器自带的,可以直接导入

    2.第三方的,需要下载的

    3.自定义的,自己写的模块

  模块的四种表现形式:

    1.使用python编写的py文件,也就意味着py文件也可以称之为模块

    2.已被编译为共享库或DLL的C或C++扩展(了解即可)

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

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

  2.为什么要使用模块

    1.通过模块可以使用别人写的代码,可以提高开发效率。

    2.调用自己写的模块,可以使得代码层次分明,因为不可能将所有代码放到一个py里,需要调用模块连接。

    3.在使用模块时,需要区分哪个是执行文件,哪个是被导入文件。(******)

  带着这个问题,可以进入下面的学习。

二。导入模块

  导入模块的方法是:

import md

  此时被导入文件是名为md的py文件,而执行文件是名为run的py问件,md文件中,有money,read1,read2,等函数和变量:

money = 1000
def read1():
  print('md',money)
def read2():
  print('md模块')
  read1()
def change():
  global money
  money = 0

  在运行run.py文件时,首先会创建一个run.py的名称空间

  这是,导入md模块后会进行以下操作:

  1.执行md.py文件

  2.运行md.py文件中的代码将产生的名字与值存放到md.py名称空间中

  3.在执行文件中产生一个指向名称空间的名字(md)

  运行结束后名称空间如图:

  多次导入不会再执行模块文件,会用第一次导入的成果。

  在import md 执行后,就可以使用模块名.函数的方式,或参数调用模块中的数据,

  函数调用的是内存地址,所以加上()后就可以执行函数了。

  当run中存在一个money,使用模块名.名字的方法调用模块中的money输出显示的还是模块中的money,同理,在使用模块中的read1时,run中也有read1时,最后运行的是模块中的read1:

def read1():
    print('from run read1')
money = 9999
md.read1()
print(money)
print(md.money)
#输出结果>>>md 1000
#9999
#1000

  当run文件中有read1,md文件中也有read1.用run中执行md中的read2,read2中嵌套了read1,结果会输出什么呢:

#run
import md
money = 9999
def read1():
    print('from run read1')
md.read2()
#md
money = 1000
def read1():
  print('md',money)
def read2():
  print('md模块')
  read1()
def change():
  global money
  money = 0
#输出结果>>>md模块
#md 1000

  最终可以看到md.read2运行的还是模块中的read1,所以可以得出以下结论:

  1.只要你能拿到函数名 无论在哪都可以通过函数加括号来调用这个函数(会回到函数定义阶段 依次执行代码)

  2.函数在定义阶段 名字查找就已经固定死了 不会因为调用位置的变化而改变.

  3.指名道姓的访问模块中的名字 永远不会与执行文件中的名字冲突

  4.你如果想访问模块中名字 必须用模块名.名字的方式

  补充:

  1.只要当几个模块有相同部分或者属于用一个模块,可以使用下面的方法:

import os,time,md

  2当模块名字比较复杂的情况下 可以给该模块名取别名:

import longgggggggggggggggggg as long

三。from ... import ...

  使用from import取得模块里的值有如下几步:

  会先创建run1.py的名称空间

    首次导入md

    1.py模块1.运行md1.py

    2.将产生的名字存放到md1.py名称空间中

    3.直接拿到指向模块md1.py名称空间中某个值的名字

    多次导入不会执行 会沿用第一次导入的成果

# run
from md1 import money
money=10000
print(money)

#md
money=0
#输出结果>>>10000

  当运行文件中也有模块中的参数时,如果模块语句在下面在,则输出模块中的参数,如果模块语句在上面,则输出run中的。

  这种句式的缺点是:

    1.访问模块中的名字不需要加模块名前缀

    2.在访问模块中的名字可能会与当前执行文件中的名字冲突

  补充:

  使用from md1 import *,可以将md中的名字全部加载过来,这种方法不推荐使用,因为非常占内存,而且不知道模块中都有哪些名字可用。

  其实可以用__all__=[]来限制导入者能够拿到的名字个数,all在默认状态下是存放所有的名称,当里面有值后,用户执行from md1 import *只能取得all中的名称,(all中需要存放字符串类型的数据)

五。循环导入和解决思路

  当设计模块时,会出现以下情况

#run
import m1
#m1
print('正在导入m1')
from m2 import y  # 首次导入m2
x = 'm1'
#m2
print('正在导入m2')
from m1 import x  # 第二次导m1
y = 'm2'
#输出结果>>>
#正在导入m1
#正在导入m2
#报错

  为什么会报错内,这里画一下名称引用图:

  当程序执行到1处时,导入m1的模块,m1 已经被导入过一次,所以不会再执行语句,而再m1寻找x并不会找到,因为此时x还在2的下面,所以,会报错。

  解决方法

  1,将导包语句放在检索值的下面

  2.使用函数:

#run
import m1
m1.f1()
#m1
print('正在导入m1')
def f1():
  from m2 import y,f2
  print('m1.f1>>>y:',y)
  f2()
x = 'm1'
#m2
print('正在导入m2')
def f2():
  from m1 import x
  print('m2.f2>>>x:',x)
y = 'm2'
#输出结果>>>
#正在导入m1
#正在导入m2
#m1.f1>>>y: m2
#m2.f2>>>x: m1

  当使用函数进行循环时,会将函数内存地址保存到名称里,这时函数还没有运行,所以不存在找不到元素的问题,这是后调用函数,就可以获得所有变量名称和函数名称。

  当在实际的程序设计时应该避免模块的循环调用,如果出现了这个个问题,先考虑修改设计。

六。__name__用法

  py文件分为执行文件和被导入文件。

  当被导入文件需要测试其代码可行性时 ,难免会运行函数,当带有运行语句的模块被导入时会出现意外的显示。

  为了区分执行文件和被导入文件,py里有__name__函数,

  当是执行文件时__name__取值为__main__,当作为模块被导入时,__name__的值是它所在文件的文件名(没有后缀)

  使用main+tab会补全这个函数

if __name__ == '__main__':
    index1()
    index2()

七。模块的查找顺序

  模块的查找顺序

    1.先从内存中找

    2.内置中找

    3.sys.path中找(类似于环境变量):

    注意:一定要分清楚谁是执行文件谁是被导入文件(******)

import time
import md
time.sleep(20)
md.f1()

  当程序运行时,文件存在与内存中,当运行到sleep时将md文件删除,程序依然可以成功,因为文件在内存中可以找到。

  当定义了一个名为time为名字的模块时,在执行函数中导入其模块,并执行time方法,得到的结果是内置对象中的time,所以查询顺序内置方法优先级较高。

  当1,2都找不到时会在sys.path中寻找,这是一个列表,里面放了文件的路劲,其中的路径就是模块可调用的文件,凡是在其下方的都可找到,于是当文件在大文件夹下时可以这样寻找:

from dir1.dir import md

  其意思是在主文件夹下的dir1文件夹下的dir文件夹寻找md模块。

  当调用sys模块后就可以查询当前文件的sys.path:

import sys
print(sys.path)
#输出结果>>>
#['D:\\python日常学习资料\\7_16day14\\代码\\day14\\07 模块的查找顺序', 'D:\\python日常学习资料\\7_16day14\\代码\\day14', 'D:\\python3.6\\python36.zip', 'D:\\python3.6\\DLLs', 'D:\\python3.6\\lib', 'D:\\python3.6', 'D:\\python3.6\\lib\\site-packages', 'D:\\python编译器\\PyCharm 2018.1.4\\helpers\\pycharm_matplotlib_backend']

  其中看到了大文件夹和执行文件所在文件夹,所以可以得出结论:

    1,凡是在大文件夹下的模块,都可以调用

    2,凡是在执行文件同文件夹的模块都可调用。

  如果执行文件和模块都不满足上述条件,也可以将模块所在文件夹路径添加至sys.path中。

import sys
sys.path.append(r'文件夹路径')
print(sys.path)

八。模块的绝对导入

  当执行文件导入模块时,模块中也有要导入的文件:

#run
import m1
m1.f1
#m1 import m2 def f1(): print('from m1 f1') m2.f2() #m2 def f2(): print('from m2 f2')

  m1.和m2在与run不同的文件夹dir中,当执行run时,会报错,因为当run导入了文件m1时,运行m1中的import m2会报错,这里的模块导入是基于run的路径为主,所以不会找到m2。

  所以得出结论:绝对导入必须依据执行文件所在的文件夹路径为准,而且绝对导入无论在执行文件中还是被导入文件都适用。

  还有第二种导入方式:相对导入

  相对导入

    .代表的当前路径

    ..代表的上一级路径

    ...代表的是上上一级路径

  值得注意的是相对导入不能再执行文件中。

  使用相对导入只能在被导入的模块中使用。

  使用相对导入 就不需要考虑执行文件到底是谁,只需要知道模块与模块之间路径关系。

九。软件开发目录规范

  1,当一个软件很大时,不会将代码放在一个文件夹里,需要分开放置。

  当软件启动时,会将启动文件放入两个位置:

    1,bin文件夹下

    2,直接放在项目根目录下

  2,conf 配置文件

  放置一些不需要变的变量,像ip 端口,数据库名称等,都是不经常变动的变量。

  3,lib 公共文件夹,

  放置一些所以模块都需要使用的文件。

  4,core,核心文件逻辑

  是软件的功能模块,处理数据,以atm为例子,判断用户的钱还有多少,如何转账等,都写在这个文件夹。

  5,log日志

  记录用户的行为,24小时的记录。

  6,readme

  对软件的介绍,通常是txt格式。

  7db数据库

  前期可以放一些字典等存储数据,后期是model,是对数据库的使用。

bin
    start.py
conf
    setting.py
lib
    common.py
core
    src.py
log
    talk.log
db
    userinfo.txt
readme

  当start文件写完后,需要调用其他模块的功能,但在bin文件夹下,只有start一个模块,所以需要导入其他文件夹下的包:

from core import src

  将core文件夹下的src模块导入到执行文件中,需要将core放入syspath中,还有其他文件,需要一一导入

  但这个方法效率太低,可以将项目名ATM放入sys.path中,这样所以文件夹都可以,看到:

sys.path.append(r'文件路径')

  虽然上述代码可行,但是只能在自己的电脑上可行,将软件下载到其他电脑上时如果文件名不一样,就找不到该地址,所以应该寻找变动的方法获取文件名。

  这里调用os模块:

os.path.dirname(__file__)

  其中,dirname的作用是将__file__文件的上一级文件名返回,file是将本文件名返回,所以完整的方法是:

import sys
import os

BASE_DIR=os.path.dirname(os.path.dirname(__file__))
sys.path.append(settings.BASE_DIR)

from core import src

if __name__=='__main__':
    src.run()

  小知识:当然,在prcharm中,会将大文件名自动添加到syspath中,但是在用户端不会,所以,需要在启动的时候加这一句话。

  在文件名路径中,不要随意手动拼接路径,因为在mac的文件拼接中是使用/拼接,如果使用反斜杠会大面积报错,所以可以使用os中的一个方法:

LOG_PATH=os.path.join(BASE_DIR,'log')

  使用join拼接出的文件路径可以在所以平台上识别。

 

posted on 2019-07-16 22:10  一只萌萌哒的提莫  阅读(132)  评论(0编辑  收藏  举报