1. 模块
1.1 什么是模块? # 模块是已经写好的一组功能的集合 # 别人写好的函数 变量 方法 放在一个文件里 (这个文件可以被我们直接使用)这个文件就是一个模块 # 模块可以是:py文件 dll文件 zip文件 1.2 如何自己写一个模块 # 创建一个py文件,给它起一个符合变量名命名规则的名字,这个名字就是模块名
2. 模块的导入
2.1 import 模块名 假设有在同级目录下有一个my_module.py文件 my_module.py: ------------------------------ print(12345) name = 'alex' def read1(): print('in read1 func') def read2(): print("in read2 func", name) print(54321) ------------------------------ # 在导入模块的过程中发生了什么? import my_module 结果: 12345 54321 总结:导入一个模块就是执行一个模块 # 怎么使用my_module模块中的名字 print(my_module.name) # alex print(my_module.read1) # <function read1 at 0x0000017D5B7E9510> my_module.read1() # in read1 func # improt的命名空间,模块和当前文件在不同的命名空间中 name = 'egon' def read1(): print("main read1") print(name) # egon print(my_module.name) # alex # 模块只能用自己命名空间的变量,不能用当前空间的,因为没有执行关系 name = 'egon' def read1(): print("main read1") print(name) # egon print(my_module.name) # alex my_module.read2() # in read2 func alex 总结:每个模块都是一个独立的名称空间,定义在这个模块中的函数,把这个模块的名称空间当做全局名称空间,这样我们在编写自己的模块时,就不用担心我们定义在自己模块中全局变量会在被导入时,与使用者的全局变量冲突 --------------------------------------- # 模块是否可以被重复导入? import my_module import my_module 结果: 13245 54321 总结:第一次导入后就将模块名加载到内存,后续的import语句仅是对已经加载到内存中的模块对象增加了一次引用,不会重新执行模块内的语句。 # 怎么判断这个模块已经被导入过了??? import sys print(sys.modules) # 模块导入的过程中发生了什么? # 找到这个模块 # 判断这个模块是否被导入过了 # 如果没有导入过 # 创建一个属于这个模块的命名空间 # 让模块的名字 指向 这个空间 # 执行这个模块中的代码 ------------------------------------ # 给模块起别名,起了别名之后,使用这个模块就都使用别名引用变量了 import my_module as m # 12345 54321 m.read1() # in read1 func # json pickle # dumps loads def func(dic, t = 'json'): if t == 'json': import json return json.dumps(dic) elif t == 'pickle': import pickle return pickle.dumps(dic) # 直接用别名,简化代码 def func(dic, t = 'json'): if t == 'json': import json as aaa elif t == 'pickle': import pickle as aaa return aaa.dumps(dic) ----------------------------------- # 导入多个模块/分别起别名 import os,time import os as o,time as t # 规范建议 模块应该一个一个的导入: 内置模块,第三方模块,自定义模块 # 所以导入模块是有顺序的: # 内置模块 # 扩展(第三方模块) # 自定义模块 # 大家都符合这个规范,是为了大家用彼此模块很方便 # import os # import django # import my_module
2.2 模块的导入from import # 如何使用 from import? # 需要从一个文件中使用哪个名字,就把这个名字导入进来 # from my_module import name # from import的过程中仍然会执行了这个被导入的文件 ------------------------------------------------------------- # import谁就只能用谁 from my_module import read1 # 第一次导入,创建命名空间,执行这个文件,找到要导入的变量,创建引用指向要导入的变量. read1() # in read1 func 这里只导入了read1,所以只能用read # 想要使用read2,还要再导入read2 from my_module import read2 # 上面已经导入过,所以直接找到要导入的变量,创建引用指向要导入的变量. read2() # in read2 func alex -------------------------------------------------------------------- # 覆盖问题 from my_module import read1 # 第一次被导入,创建命名空间,执行,找到变量,创建引用指向要导入的变量 def read1(): # 这里重新定义了read1,所以导入进来的模块中的read1倍覆盖 print("in my read1") read1() # in my read1 from my_module import read2 # 已经被导入过,直接找到变量,创建引用指向要导入的变量 read2() # in read2 func alex -------------------------------------------------------------- # 一行导入多个名字? from my_module import read1, read2 # 12345 54321 read1() # in read1 func read2() # in read2 func alex ------------------------------------------------------------------- # 给导入的名字起别名 from my_module import read1 as r1, read2 as r2 read1(): print("in my read1") r1() # in read1 func r2() # in read2 func alex read1() # in my read1 ------------------------------------------------------------------ # from my_module import * 在导入的过程中 内存的引用变化 from my_module import * # 导入my_module.py文件中的所有成员 name = 'egon' # name被覆盖 print(name) # egon read1() read2() # 用的是自己空间的alex ---------------------------------------------------------------- # * 和 __all__ __all__能够约束*导入变量的内容 修改:在my_module.py文件的上面加上一行__all__ = ['name'] from my_module import * # 12345 54321 print(name) # alex read1() # 报错 read2() # 报错 # __all__ 只能约束*
3. 模块中引用的情况
3.1 模块的循环引用 ☆☆☆ # 模块之间不允许循环引用 # 比如以下这些情况:
模块a和模块b的引用构成了一个循环
3.2 模块的加载与修改 ☆ ------------------------------------------ import time # 导入时间模块 import my_module # 导入my_module.py文件,此时read1函数里是打印111 time.sleep(10) # 程序在这里停止10s,假设我在这里修改了打印为222 my_module.read1() # 那么这里打印的还是111,因为导入的时候已经存在于内存当中了,你修改的是文件里面的,你修改不了内存 总结: 已经被导入的模块发生了修改,是不会被感知到的 ---------------------------------------------------- import time # 导入时间模块 import importlib import my_module # 导入my_module.py文件,此时read1函数里是打印111 time.sleep(10) # # 程序在这里停止10s,假设我在这里修改read1函数打印的为222 importlib.reload(my_module) # 重新导入模块my_module,此时内容已经被修改 my_module.read1() # 所以此时read1打印的是222 总结: 要想修改的模块被正在运行中的程序感知到,重启这个程序(或者重新加载模块)
3.3 把模块当初脚本执行 ☆☆☆☆☆ # 执行一个py文件的方式: # 在cmd里执行/在pycharm执行: 直接执行这个文件 - 以脚本的形式运行这个文件 # 导入这个文件 # 都是py文件 # 直接运行这个文件 这个文件就是一个脚本 # 导入这个文件 这个文件就是一个模块 有calculate.py文件,里面是一个计算器 ----------------------------------------------- def main(exp): exp = exp.replace(' ', '') while 1: ret = re.search('([^()]+)', exp) if ret: inner_backet = ret.group() res = str(cal(inner_backet)) exp = exp.replace(inner_backet, res) exp = format_exp(exp) else: break return cal(exp) s = input(">>>") print(main(s)) ------------------------------------------------- 假设我直接执行这个文件,此时它是脚本,它可以独立的提供一个功能 但是假设我从同级目录的文件导入它,此时它是一个模块,能够被导入者调用这个功能,会先执行交互,但是我们不需要这个交互 # 当一个py文件 # 当做一个脚本的时候: 能够独立的提供一个功能,能自主完成交互 # 当成一个模块的时候: 能够被导入者调用这个功能,不能自主交互 # 一个文件中的__name__变量 # 当这个文件被当做脚本执行的时候 __name__ == '__main__' # 当这个文件被当前模块导入的时候 __name__ == '模块的名字' 这时我们可以用__name__ 来完成脚本和模块的需求 将 if __name__ == '__main__': s = input(">>>") print(main(s)) 放在calculate.py文件的最下面, 当我们直接执行这个文件时,如果__name__等于'__main__',就可以执行交互,else:当它被当做模块导入时,__name__等于'模块的名字' 此时calculate.py文件为 ------------------------------------------ def main(exp): exp = exp.replace(' ', '') while 1: ret = re.search('([^()]+)', exp) if ret: inner_backet = ret.group() res = str(cal(inner_backet)) exp = exp.replace(inner_backet, res) exp = format_exp(exp) else: break return cal(exp) if __name__ == '__main__' s = input(">>>") print(main(s))
3.4 模块搜索路径 ☆☆☆☆☆ # 和被当做脚本执行的文件 同目录下的文件 可以直接被导入 因为被当做脚本直接执行这个文件,模块搜索路径中有这个文件的所在的目录,所有这个文件同级的模块可以被直接导入,因为他们的目录都一样 # 除此之外其他路径下的模块 在被导入的时候需要自己修改sys.path列表
4. 包
4.1 什么是包? # 包: 文件夹中有一个__init__.py文件 # 包: 是几个模块的集合 4.2 包的导入语句 # import 从包当中导入模块 # import glance2.api.policy # glance2.api.policy.get() # import glance2.api.policy as policy # policy.get() # from import 导入模块,或者导入模块中的方法 # from glance2.api import policy # policy.get() # from glance2.api.policy import get # get() # 关于包相关的导入语句也分为import和from ... import ...两种,但 是无论哪种,无论在什么位置, # 在导入时都必须遵循一个原则:凡是在导入时带点的,点的左边都必须是一个包,否则非法。 # 需要注意的是from后import导入的模块,必须是明确的一个不能带点, # 否则会有语法错误,如:from a import b.c是错误语法
4.3 绝对导入/相对导入 (1)直接导入包 # 导入一个包 # 不意味着这个包下面的所有的内容都是可以被使用的 # 导入一个包到底发生了什么? # 相当于执行了这个包下面的__init__.py文件 (2)绝对导入 # 绝对导入: # 优点: # 在执行一个python脚本的时候,这个脚本以及和这个脚本同级的模块只能用绝对导入 # 缺点: # 所有的导入都要从一个根目录下往后解释文件夹之间的关系 # 是是如果当前导入包的文件和被导入的包的位置发生了变化,那么所有init文件都要做相应的调整 (3)相对导入 # 相对导入 # 优点: # 不需要去反复的修改路径 # 只要一个包中的所有文件夹和文件的相对位置不发生改变 # 也不需要去关心当前这个包和被执行文件之间的层级关系 # 缺点: # 含有相对导入的py文件不能被直接执行 # 必须放在包中被导入的调用才能正常执行 # 如果只是从包中导入模块的话,那么我们不需要做任何多余的操作 # 直接导入就行了 # 如果我们希望导入包的时候,能够顺便把模块也导进来 # 需要设计init文件 # 绝对目录的导入相对目录的导入各有千秋