day21 python模块和包 及绝对导入和相对导入
一.模块
1.如何自己写一个模块
可以是py文件
也可以是dll文件(底层是c,但可以直接用)
也可以是zip文件(可以直接使用)
import my_module
二.模块的导入: import
1.模块的导入
import my_module #导入模块时, 会执行模块里面的代码
print(my_module.name)
print(my_module.my_name())
2.模块的导入: 命名空间
def my_name(): #导入的模块和当前文件在不同的命名空间(而且导入能用被导入的内存空间, 而被导入的不能反向用导入的内存空间中的变量)
return 'in practice s my_name'
print(my_name())
3.import导入模块时发生了什么?
1.找到这个模块
2.判断这个模块是否被导入过了
3.1.如果没有被导入过
创建一个属于这个模块的命名空间
让模块的名字指向这个命令空间
执行这个模块的代码
3.2.如果被导入过
就不再重复导入
4.如何判断模块是否被导入过了
import sys
print(sys.modules)
5.1.为模块起别名 as: 别名可以用
import my_module as m #之前的原名字就不能用了(让my_module去找这个文件, 让m指向内存空间)
print(m.my_name())
print(my_module.name)
5.2.别名, 原名都能用: 分别导入两次
import my_module #这样导入新别名和原来的名字都可以用
import my_module as m
print(m.my_name())
print(my_module.name)
5.3.别名用法: 当你导入的多个模块可能做相同事情的时候
def func(dic,flags = 'json'):
if flags == 'json':
import json as que
if flags == 'pickle':
import pickle as que
return que.dump(dic)
6.一行导入多个模块
import os, sys, time #逗号分隔, 但是不建议这样写: 若同时需导入内置模块, 第三方, 自定义.应该是这个顺序比较好
三.模块的导入: from import
1.from 模块 import 变量名
from my_module import name
print(name)
2.from 模块名 import 变量名: 的时候发生了什么
(import了哪个变量就只能用哪个变量)
(但是还是会执行模块中的所有代码)
1.找到要被导入的模块
2.判断这个模块是否被导入过
3.1.如果没被导入过
创建一个属于这个模块的命名空间
先执行这个文件中的代码
然后找到你要导入的变量
再给你要导入的变量(导入文件的内存中)创建一个引用,指向要导入的变量(被导入的文件的内存)
3.2.如果已经被导入过
不再创建模块的内存空间, 也不在执行模块中的代码
直接把你要导入的变量(导入文件的内存中)创建一个引用,指向要导入的变量(被导入的文件的内存)
3.命名空间
name = 'change module name'
print(name)
4.为导入的名字起别名
from my_module import name as alias1, my_name as alias2
5.一行导入多个模块
from my_module import name, my_name
6.*和__all__的关系
from my_module import * #说明你导入了模块中的所有的东西, 都可以直接用
print(name)
print(my_name()) #报错: 若你用*这样的方式导入时, 会找到被导入模块中变量 __all__ = ['name'] , 只能导入__all__中写的变量
from my_module import my_name #__all__只能约束 * 的时候, 这样导入时不影响
print(my_name())
四.模块引用中的情况
1.模块的循环引用
一个纯的函数组成的多个模块之间, 不能形成循环引用: 设计程序时候要避免
2.模块的加载与修改
import time
import my_module #这个时候所有代码都执行完了
import importlib
time.sleep(20) #如果这个时间修改了文件中的内容, 那么你只是修改了文件, 而不是修改了内存
importlib.reload(my_module) #这个是重新import了一遍, 代码重新执行一遍(正常人不用这个)
print(my_module.my_name())
3.把模块当成脚本执行
直接运行这个文件, 叫脚本: __name__ == '__main__'
被导入执行, 叫模块: __name__ == '模块名'
import my_module #内置的模块被导入时, 执行代码,并没有输出; 而我们自己的模块被导入时, 有打印东西, 不专业
import calculate
print(calculate.main('23+98*33'))
#当成脚本时: 能自主完成交互
#当成模块时: 需要被调用才能交互
#在被导入的模块中 __name__ == '__main__' 才执行顶层函数
4.模块的搜索路径
当和被当做脚本执行的文件同目录下的模块: 可以直接被导入
除此之外其他路径下的模块: 在被导入式需要自己修改sys.path列表
import sys
print(sys.path)
path = r'C:\Users\THINKPAD\PycharmProjects\s15\day01\bajie'
sys.path.append(path)
import a
五.包
1.什么是包
就是文件夹中有一个 __init__.py 文件
是几个模块的集合
2.从包中导入模块
凡是导入带点的, 点的左边必须是一个包
import
import glance.api.policy
import glance.api.policy as policy #用的时候名字长, 搞个别名
glance.api.policy.get()
policy.get()
from import
from glance.api import policy #导入模块 #from import时, import后面的东西不能有点
from glance.api.policy import get #直接导入函数
policy.get()
get()
3.直接导入一个包: 然后通过这个包可以用包里面的所有模块,需要设计__init__.py文件
3.1.1.绝对导入
import glance #导入一个包, 不意味着这个包下的文件能用
#导入一个包,发生了什么: 执行了包下面的 __init__.py文件
glance.api.policy.get() #没有执行导入api, 那么我在__init__.py文件里导入api, 不就行了吗?
#不行, 因为import glance时候, glance执行__init__.py里面的 import api的时候, 用的还是执行glance时的sys.path
#这个时候如果sys.path没有api目录的路径, 那么就找不到api这个文件
如何解决:
在glance的__init__.py 文件里from glance import api
在api的__init__.py 文件中from glance.api import policy
'''
glance\
|
|--api\
| |
| |--policy.py
| |--versions.py
| |--__init__.py: from glance.api import policy
|
|--cmd\
| |
| |--manager.py
| |--__init__.py: from glance.cmd import manager
|
|--db\
| |
| |--model.py
| |--__init__.py: from glance.db import model
|
|--__init__.py: from glance import api,cmd,db
'''
import glance
glance.api.policy.get()
#那么通过这个方法, 就可以通过直接导入一个包的方法, 用这个包里面的所有东西
#绝对导入: 我认识这个路径, 那么我就可以从这个路径导入所有的文件
#缺点: 如果当前导入包的文件和被导入包的位置关系发生了变化,那么所有的__init__.py都要调整
3.1.2:绝对导入:pycharm帮你做的事: 当你的文件要导入的包, 和你的文件不是平级的, 怎么办?
比如以上绝对导入在改位置时,这时pycharm会自动给你在__init__.py文件中改绝对的路径
3.2.1.相对导入
import glance #glance的__init__.py: from . import api #api的__init__.py: from . import policy
'''
glance\
|
|--api\
| |
| |--policy.py
| |--versions.py
| |--__init__.py: from . import policy, versions
|
|--cmd\
| |
| |--manager.py
| |--__init__.py: from . import manager; from ..api import policy #想在manager中使用policy模块中的方法的时候
|
|--db\
| |
| |--model.py
| |--__init__.py: from . import model
|
|--__init__.py: from . import api,cmd,db
'''
glance.api.policy.get()
3.2.2.相对导入: 如果改变了glance和导入文件的位置, 被放到了和导入文件同级的包upname里面
from upname import glance #只要找到glance, 就可以直接使用, __init__.py中的相对导入不用改
glance.api.policy.get()
3.2.3.相对导入: 注意事项
from . import api #文件中导入用的是相对 . 时, 文件本身不能被直接执行
#必须放在一个包中, 被导入的调用才能正常使用
4.总结:
如果只是从包中导入模块, 那么我们不需要做任何多余的操作, 直接导入就好了
如果我们希望导入包的时候,顺便把模块也导入进来, 那么需要用绝对导入,和相对导入
六.项目开发规范
'''
software\ #项目名
|
|--bin\ #脚本目录
| |
| |--start.py -- 只放一个启动脚本
|
|--conf\ #配置文件目录
| |
| |--config.ini -- 比如你代码里的用到的文件名等变量,可以单独写到这个文件中
| |--my_log_settings.py -- 以后所有代码用到变量的值改动的时候, 都只需要在conf中修改, 不用改代码了
| |--settings.py -- 方便运维同学使用和维护
|
|--core\ #核心代码目录
| |
| |--core.py -- 如auth.py等代码
|
|--db\ #数据库目录
| |
| |--bajie_json -- 用户信息啥的
| |--wukong_json
|
|--lib\ #库文件目录
| |
| |--read_ini.py -- 别人开发好的功能, (不是内置的, 也不是第三方, 可能是你自己写的比较完善的,还和当前项目的相关性不大, 是个通用模块)
|
|--log\ #日志文件目录
|
|--all.log -- 记录日志
'''