模块与包

简介:

在我们之前学习的编程范式中有 面向过程式编程、函数式编程、面向对象编程………………  函数式编程是为了将相同功能的代码归类到一块,方便调用,而类也是解决代码冗余,功能区分开来。  不可能将一个APP的功能全部都写在一个文件里。 那这样的话如果下次要修改某个功能,全部代码都要跟着改动。  而模块与函数、类也大至相同,都是为了将代码功能区分开来,方便调用 ,解决代码的冗余。比如  需要一个删除的功能,则直接调用‘删除’功能的模块代码。也就实现了删除功能。 而不用再去重复的造轮子。 下面就进入模块的学习。

 

模块与包:

模块分为三类:1、内置模块(也就是当python程序执行时候,就已经加载了。python自带)

                       2、第三方模块:自己下载的一些第三方包

                       3、自定义: 自己定义的模块,可以被import 或from …… import …

先说自定义模块:

新建一个cover.py文件

#!/usr/bin/env python
#!-*- coding:utf-8 –*-
print(' cover file')
name='tony'
age='22'
def check_name():
    print('cover file ,name is ',name)
def check_age():
    print('cover file age is',age)
def modify_age():
    global age
    age=17

再新建 一个call_cover.py 的文件:  这个文件用来调用 cover 模块

#!/usr/bin/env python
#!-*- coding:utf-8 -*-
import cover      #调用cover模块,注意调用是不需要.py的 ,
这里的import cover  
第一件事:是在创建名称空间,这个名称空间是用来存放cover中定义的名字  
第二件事:基于刚刚创建的名称空间,执行cover.py ,在 import cover 的时候整个文件都已经被执行了一遍。 而此时也会生一个相应的pyc后缀的文件。—>也就是已经保存在内存里了,只是  再次引用 
第三件事:在本文创建的名字不影响cover 的调用,因为cover.名字的操作只操作cover 的名称空间---> 简言之: 创建名字cover 来引用该命名空间
 
print(cover.name)  # 再打印出cover  下的name 这个名字  ,name对应的名字是'tony'
每个模块都是一个独立的名称空间,定义在这个模块中的函数、变量,把这个模块的名称空间当做全局名称空间,这样我们在编写自己的模块时,就不用担心我们定义在自己模块中全局变量在被导入时,与使用者的全局变量冲突。
 
如:
######call_cover.py 文件
from cover import check_name  #这里导入了cover 文件 下的check_name方法,这种调用方式  from …  import  …   就不用cover.名字的方式调用了
def check_name(): #自己定义了一个check_name方法
    print('call_cover name is',name)
check_name() #这里的调用 依旧是 cover的内容, 验证了 上面注红的那句。
 
 
 
 

这样就完成了一个模块的导入

 

查看当前已经加载的模块

import sys
print(sys.modules)    #可以查看到当前已经加载的模块,sys.modules 是一个字典,内部包含了‘模块名与模块对象的映射’,该字典决定了导入模块时,是否需要重新导入。

 

为模块起别名:

import cover as cov    #如果模块名字过长,就可以采取这种方式,优雅的使用  module.名字 的调用 
print(cov.name)

还有另一种情况用到 模块起别名

当指定文件格式的时候:

if file_format=='xml':      不同格式,导入不同的模块名
     import xmlread as reader  
 elif file_format=='csv':
     import csvread as reader

 

import   模块名  与  from …..   import   模块名:

二者有相应的应用场景, 对比 import  cover ,会将尖文件的名称空间‘cover’ 带到当前名称空间中,使用时必须是 cover. 的方式,而from .. import .. 也会创建新的名称空间,但是将cover 中的名字直接导入到当前的名称空间中,在当前名称空间直接使用名字或者方法就可以调用。

from cover import name
print(name)    #当前名称空间,  如果在这里修改了name,那值肯定会发生改变,因为会再次将name 绑定到另一个新的值,

模块重名:

cover.py

print(' cover file')
name='tony'
age='22'
def check_name():
    print('cover file ,name is ',name)
def check_age():
    print('cover file age is',age)
def modify_age():
    global age
    age=17
def look_all():
    print('cover file name is',name)

 

call_cover.py

from cover import check_age,look_all      #   cover  下有个方法或名字叫 check_age ,
print(check_age)  #通过打印出来知道这是一个函数   ,既然是函数,加括号就能运行。
check_age()   这里就已经运行cover   的check_age()方法

那如果这个时候有重名的的名字的话呢??
check_age='abc'     
print(check_age)     #这个时候的结果然会被替换掉。 因为使用的是from 到当前的名称空间了。


这个时候再调用其它的函数:
look_all()   #这个函数调用的是cover 的name  ,与当前的名称空间无关,不受影响

 

from cover import *

这种情况不推荐使用,因为在自己不知道的情况下导入,很大的可能把之前定义的内容覆盖掉。因为  *  代表了所有,你不知道的都导入进去了。

 

name='liang'
from cover import *     
print(name)

既然不能用,还说它干嘛???

可以在 cover 文件中规定 被 import * 的内容

#!/usr/bin/env python
#!-*- coding:utf-8 -*-
__all__=['check_name','check_age']   #就是它
print(' cover file')

name='tony'
age='22'
def check_name():
    print('cover file ,name is ',name)
def check_age():
    print('cover file age is',age)
def modify_age():
    global age
    age=17
def look_all():
    print('cover file name is',name)

 

call_cover.py

from cover import *      #这里导入的就只是 'money','read1'  这两个模块了
check_age() 
check_name()
print(name)    #这个就会查找不到。因为导入的时候 已经限制,且 全局 名称空间也没有创建

 

把模块当做脚本运行:  

在文件最后加如下代码 ,

if __name__=="__main__":
    print('文件被当做脚本去执行')
    read1()
print(__name__)

 

 

模拟搜索路径:

python 解释器在启动时会自动加载一些模块,可以使用sys.modules 查看

在第一次导入某个模块时 如(cover )  ,会先检查该模块是否已经被加载到内存中(当前执行文件的名称空间对应的内存),如果有则直接引用。

如果没有--->按照顺序查找 ------  1、 –>  先从内存找,sys.modules  , 内存找不到 -->2、查找同名的内建模块 ,内建模块没有  --> 3、查找sys.path   -->4、查看不到报错

 

sys.path

sys.path是python的搜索模块的路径集,是一个list

import sys
print(sys.path)

可以在python环境下使用 sys.path.append(path) 添加相关的路径,但在退出python环境后自己添加的路径就会自动消失!

sys.path.append('/one/two/three')   # 添加至列表最后面
sys.path.insert(0,'/two/one/three')   #插入到最前,最先搜索路径

 

python 编译

python 编译文件
python -m compileall /module_directory #递归编译
python -O -m compileall /module_directory -1  #则编译一层
命令行里使用compile() 函数时,自动使用python -O -m compileall
详见:https://docs.python.org/3/library/compileall.html#module-compileall

 

image

理解 ‘包’ :

‘包’ 是一堆代码文件构成的集合,每个包都有一个__init__.py  的文件,更高级别的模块

1、无论是import 形式还是from ….. import  形式,凡是在导入语句中(而不是在使用时)遇到带点的,都是关于‘包’才有的导入语法

  • 包的本质就是一个包含__init__.py文件的目录.
  • 包 A和包B下有同名模块也不会冲突,如A.a与B.b 来自两个命名空间
glance/                   #Top-level package

├── __init__.py      #Initialize the glance package

├── api                  #Subpackage for api

│   ├── __init__.py

│   ├── policy.py

│   └── versions.py

├── cmd                #Subpackage for cmd

│   ├── __init__.py

│   └── manage.py

└── db                  #Subpackage for db

    ├── __init__.py

    └── models.py

 

 

文件内容创建:

#文件内容

#policy.py
def get():
    print('from policy.py')

#versions.py
def create_resource(conf):
    print('from version.py: ',conf)

#manage.py
def main():
    print('from manage.py')

#models.py
def register_models(engine):
    print('from models.py: ',engine)

 

在与包glance 同级别的文件中创建一个call_all.py 文件:

这个文件中调用glance下的模块

#!/usr/bin/env python
#!-*- coding:utf-8 -*-
import glance.db.models
glance.db.models.register_models('nosql')
from glance.db import models
models.register_models('mysql')

 

__init__.py 文件:

__init__.py文件,只要是第一次导入包或者是包的任何其他部分,都会执行包下的__init__.py文件,(我们在每个包的文件内 都打印一行内容进行验证,),这个文件可以为空,但是也可以存放一些初始化包的代码。

__init__.py
print('db-init')   #import 就会执行

如下:都会触发 __init__.py

#!/usr/bin/env python
#!-*- coding:utf-8 -*-
import glance.db.models

 

现在在当前glance文件下,想用glance  的police 模块

import glance.api.policy
glance.api.policy.get()

这样用需要加  glance.api.policy 前缀   ,

 

现在想用不要前缀 调用 get()

from glance.api.policy import get
get()

在导入的时候带点的---------->必须满足   from 包名 import 模块名  或者是from 包名.模块名 import  方法名,或者是import  后面一定不能是有点的形式,必须是 一个单独的。

点的左边必须是包    点的左边必须是包  点的左边必须是包

 

包下 有__init__.py就是一个包,那__init__.py有什么用。

这里只导入了api,但是只要一  import glance 下的__init__.py  就触发,跟着还有api 也触发了__init__.py

导入包也就做了这么一件事。请看打印内容。都打印相应的内容

image

 

那现在能不能调用到  glance.api.policy

导入包仅仅是做了一件事,就是执行了__init__.py 其它的什么事也没做,如果这个时候再来个导入其它的模块,是调用不到的

image

 

而下面这个导入方法,则指定了要导入谁,所以能调用到policy下的get() 方法

from glance.api import policy
policy.get()

 

image

 

导入包就只是在执行__init__.py文件,你没有给什么文件就是没有文件,* 号还是代表__init__.py里面的所有

from glance.api import *

 

现在在api的 __init__.py的文件下 添加如下内容:

__all__=[‘x’,’y’,’’policy,’versions’]

x=1

y=2

image

 

现在再来导入:

现在就能拿到policy   versions

image

同样,也可以policy.get()

但是不是能直接 get(),因为拿到的名字是policy  这个模块 ,对于模块来说*代表的意思。

image

 

 

现在看问题
import glance.api ,为何说没有 ,而且提示信息跑到   api 的__init__.py  去了

image

 

x却能找到:

image

现在的问题是api 文件下__init__.py没有policy的这个文件:

可以在__init__.py 文件里面添加内容:,但这个不是我们想要的。

image

 

那既然可以定义,不如直接 import policy 吧。

image

再到call_all.py文件调用 ,还是不行,错误出在__init__.py  文件里

image

 

现在是谁在调用 policy.py   ,是call_all.py 在调用policy.py ,而不是__init__.py 在调用 policy .py文件

在call_all.py里只要一 import   glance.api   就会触发api的__init__.py ,这个时候内存没有 policy ,内建也没有,sys.path 以call_all.py的为准。

都没有,所以报错。    

再分析:在call_all.py     import glance.api  就会触发api的__init__.py(它导入import policy ),   但是当前 sys.path 路径仍然是 call_all  的路径,它的路径指定没有policy--->报错。 如何解决 ---->>绝对导入,  和相对导入。  * 是对于__all___的,对于 单纯的import 没有意义

 

绝对导入:

在glance 下 api  下 __init__.py 添加    from glance.api import policy     , 那call_all.py 就能找到路径并找到policy ,相应的也找到get()方法了,但是如果直接在glance下 api 下__init__.py    那就会报错,因为 from glance.api import policy  是基于调用者 的,而不是被调用者的,如果是被调用者也就是__init__.py 自己 from glance.api import policy    没有这个目录,所以是调用不到的。除非使用   import  policy  是在自己的这一层目录的,但是这样就没有任何意义了, 包的意义就是被调用的,如果这样import 自已目录下的文件,就没有了调用的意义。

缺点:如果包名更名,就找不到模块了,所以有了相对导入

相对导入:  .  和  . .

相对导入是 __init__.py 相对的。

现在要找一个main 的一个方法,基于glance下的api 下的 __init__.py , 那这个时候的路径就如下:

image

call_all.py文件调用 :

image

 

再调用同一级目录的create_resource() 方法

注意:还是基于glance下的api文件的__init__.py 文件,所以文件路径是   当前目录的versions 导入 create_resource ,这样就可以调用它下面的方法了

image

调用 :

image

 

 

 

用import 导入内置的或者第三方的模块,但是要绝对避免使用import来导入自定义包的子模块,应该使用from ….import …的绝对路径或者相对路径导入,且包的相对导入只能用 from 的形式。

比如现在glance/api/version.py 中导入glance/api/policy.py    , call_all.py 调用:

 

这是version.py  文件   加入了from . import policy   与policy.py  同级

image

这时,call_all.py 调用 versions.py

image

这样就完成  api 里调用 versions 功能,versions里调用 policy功能。

 

错误用法:

现在glance/api/version.py 中导入glance/api/policy.py , call_all.py 调用:

image

没毛病。。。。在versions.py文件里执行也没有问题   

那就call_all.py  继续调用吧。  ,调用方法和之前没有变,但是报错了、、、、、

 

image

 

因为在versions.py  image     , 它的import policy 在它自己的文件中执行没有任何问题,但是包的本质意义就是让其它的模块去调用,  这里用的是让call_all.py去调用 , 这样一调用就是基于  和  glance 同一级的  call_all 去调用  policy了, call_all 这一级压根就没有policy.py模块文件,就报错。  所以还是得用  相对路径 或者绝对路径 来调用   ,同时推荐使用    相对路径来调用。

 

 

包 直接   使用:

glance/api/version.py   

glance/api/policy.py

现在要  实现 glance.   任何功能 

glance.get()

glance.register_modles(‘mysql’)

分析: 要实现  glance. 任何功能     的调用,那 就是在执行它的__init__.py  所以,把注意力放在glance的__init__就好,然后再是glance. 那就是glance下的__init__要 导入其 同级目录的 功能

image

 

这时候调用的话,就可以直接调用了

image

 

 

现在有另一个问题,现在glance  如果在另外一个目录 里面。如图:

image

现在已经不与call_all.py 同一级目录了。   现在 在call_all.py再用 import glance 必然找不到。

image

 

现在就要用到sys 模块

将它的路径添加到 sys.path中,这样就正常运行了,但是不能每次都 这样去运行吧,每次都COPY目录手动加进去。 这就要用到‘路径处理’了

image‘’

 

 

路径处理:

方法一:

如果自己定义的包,想在文件任意位置 都能被 import ,永久生效,那就把文件放到 site-packages 文件 里,这样就可以在任意位置调用

print(sys.path)  # 找到site-packages 

 

如果只是用完就清除的话就用以下方法

方法二:

先找到本地的绝对路径

import os
print(os.path.abspath(__file__))   #这里打印的就是绝对路径
 

现在要拿到的是test 的路径,才能拿到glance

image

print(os.path.dirname(os.path.abspath(__file__)))   #这里拿到是 test 的上一级目录

 

 
将test 这一层的目录添加到 sys.path中
base_dir=os.path.dirname(os.path.abspath(__file__))
sys.path.append(base_dir)

 

call_all.py 调用

from test import glance
glance.get()

image

 

还 有问题,我还想  直接 import  glance

import os,sys
base_dir=os.path.dirname(os.path.abspath(__file__))
sys.path.append(r'%s\test'%base_dir)   #将目录拼接
import glance
glance.get()

image

转载于:https://www.cnblogs.com/tonycloud/articles/6798429.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值