目录
2019/08/15 学习整理
函数进阶
模块的四种形式
- 自定义模块:如果你自己写一个py文件,在文件内写入一堆函数,则它被称为自定义慕课,即使用python编写的.py文件
- 点三方模块:已被编译为共享库DLL的C或C++扩展
- 内置模块:使用C编写并链接到python解释器的内置模块
- 包:把一系列模块组织到一起的文件夹(注:文件夹下有一个__init__.py文件,该文件夹称之为包)
为什么要用模块?
- 用第三方或者内置的模块是一种拿来主义,可以极大地提升开发效率。
- 自定义模块,将我们自己程序中用到的公共功能,写入一个python文件,然后程序的各部分组件可以通过导入的方式来引用自定义模块的功能。
如何使用模块
一般我们使用import和from...import...导入模块。
import的使用:
# run.py
import spam # from the spam.py
import spam
import首次导入模块发生了3件事:
- 以模块为准创造一个模块的名称空间
- 执行模块对应的文件,将执行过程中产生的名字都丢到模块的名称空间
- 在当前执行文件中拿到一个模块名
模块的重复导入会直接饮用之前创造好的结果,不会重复执行模块的文件,即重复导入会发生:spam=spam=模块名称空间的内存地址
from 模块名 import 具体的功能:
# run.py
from spam import money
money = 10
print(money) # 10
from...import...首次导入模块发生了3件事:
- 以模块为准创造一个模块的名称空间
- 执行模块对应的文件,将执行过程中产生的名字都丢到模块的名称空间
- 在当前执行文件的名称空间中拿到一个名字,该名字直接指向模块中的某一个名字,意味着可以不用加任何前缀而直接使用
- 优点:不用加前缀,代码更加精简
- 缺点:容易与当前执行文件中名称空间中的名字冲突
import和from...import...的异同(掌握)
相同点:
- 两者都会执行模块对应的文件,两者都会产生模块的名称空间
- 两者调用功能时,需要跑到定义时寻找作用域关系,与调用位置无关
不同点
- import需要加前缀;from...import...不需要加前缀
循环导入问题
什么是循环导入
# m1.py
print('from m1.py')
from m2 import x
y = 'm1'
# m2.py
print('from m2.py')
from m1 import y
x = 'm2'
- 如果运行run.py,则会报错
ImportError: cannot import name 'y'
- 如果运行m1.py,则会报错
ImportError: cannot import name 'x'
- 如果运行m2.py,则会报错
ImportError: cannot import name 'y'
报错原因:
- 运行m2 -> 运行到from m1 import x
- 创建名称空间 m1
- m1 运行 -> 运行到 from m2 import y
- 创建名称空间 m2
- m2 被运行 -> 运行到from m1 import x 但是此时发现m2已被创建 寻找x x已存在 但为空 所以报错
解决方案(掌握)
我们可以使用函数定义阶段只识别语法的特性解决循环导入的问题,我们也可以从本质上解决循环导入的问题,但是最好的解决方法是不要出现循环导入。
方案一
# m1.py
print('from m1.py')
def func1():
from m2 import x
print(x)
y = 'm1'
# m2.py
print('from m2.py')
def func1():
from m1 import y
print(y)
x = 'm2'
方案二
# m1.py
print('from m1.py')
y = 'm1'
from m2 import x
# m2.py
print('from m2.py')
x = 'm2'
from m1 import y
模块的搜索路径
模块搜索路径的顺序(掌握)
模块其实就是一个文件,如果要执行文件,首先就需要找到模块的路径(某个文件夹)。如果模块的文件路径和执行文件不在同一个文件目录下,我们就需要指定模块的路径。
模块的搜索路径指的就是在导入模块时需要检索的文件夹。
导入模块时查找模块的顺序是:
- 先从内存中已经导入的模块中寻找
- 内置的模块
- 环境变量sys.path中找
Python文件的两种用途
python文件总共有两种用途,一种是执行文件;另一种是被当做模块导入。
编写好的一个python文件可以有两种用途:
- 脚本,一个文件就是整个程序,用来被执行
- 模块,文件中存放着一堆功能,用来被导入使用
# aaa.py
x = 1
def f1():
print('from f1')
def f2():
print('from f2')
f1()
f2()
# run.py
import aaa
如果直接运行run.py会直接运行aaa.py中的f1()
和f2()
,但是如果我们在aaa.py中加上if __name__ == '__main__':
这句话,则可以防止运行run.py时执行f1()
和f2()
。因为当aaa.py被直接执行,即当做执行文件的时候__name__ == '__main__'
; 在aaa.py被当做模块直接运行的时候__name__ == 'aaa'
。由此可以让aaa.py在不同的场景下有着不同的用法。
# aaa.py
x = 1
def f1():
print('from f1')
def f2():
print('from f2')
if __name__ == '__main__':
f1()
f2()
Python包
什么是包?
包是模块的一种形式,包的本质就是一个含有.py
的文件的文件夹。
为什么要有包?
模块的第一个版本只有10个功能,但是未来在扩展版本的时候,模块名和用法应该最好不要去修改,但是这只是对使用者友好,而由于版本扩展,文件越来越大,模块设计者对模块的管理、维护会越来越复杂,因此我们可以使用包来扩展模块的功能。
如何用包?
导入模块发生的三件事:
- 创建一个包的名称空间
- 执行py文件,将执行过程中产生的名字存放于名称空间中。
- 在当前执行文件中拿到一个名字aaa,aaa是指向包的名称空间的
导入包发生的三件事:
- 创建一个包的名称空间
- 由于包是一个文件夹,无法执行包,因此执行包下的.py文件,将执行过程中产生的名字存放于包名称空间中(即包名称空间中存放的名字都是来自于.py)
- 在当前执行文件中拿到一个名字aaa,aaa是指向包的名称空间的
导入包就是在导入包下的.py,并且可以使用以下两种方式导入:
- import ...
- from ... import...
Python具体模块
json 和 pickle模块
json:
Json序列化并不是python独有的,json序列化在java等语言中也会涉及到,因此使用json序列化能够达到跨平台传输数据的目的。
json数据类型和python数据类型对应关系表
Json类型 | Python类型 |
---|---|
{} | dict |
[] | list |
"string" | str |
520.13 | int或float |
true/false | True/False |
null | None |
json模块序列化和反序列化的一个过程如下图所示
import json
struct_data = {'name': 'json', 'age': 23, 'sex': 'male'}
print(struct_data, type(struct_data))
{'name': 'json', 'age': 23, 'sex': 'male'} <class 'dict'>
data = json.dumps(struct_data)
print(data, type(data))
{"name": "json", "age": 23, "sex": "male"} <class 'str'>
# 注意:无论数据是怎样创建的,只要满足json格式(如果是字典,则字典内元素都是双引号),就可以json.loads出来,不一定非要dumps的数据才能loads
data = json.loads(data)
print(data, type(data))
{'name': 'json', 'age': 23, 'sex': 'male'} <class 'dict'>
# 序列化
with open('Json序列化对象.json', 'w') as fw:
json.dump(struct_data, fw)
# 反序列化
with open('Json序列化对象.json') as fr:
data = json.load(fr)
print(data)
{'name': 'json', 'age': 23, 'sex': 'male'}
pickle:
Pickle序列化和所有其他编程语言特有的序列化问题一样,它只能用于Python,并且可能不同版本的Python彼此都不兼容,因此,只能用Pickle保存那些不重要的数据,即不能成功地反序列化也没关系。但是pickle的好处是可以存储Python中的所有的数据类型,包括对象,而json不可以。
pickle模块序列化和反序列化的过程如下图所示
import pickle
struct_data = {'name': 'json', 'age': 23, 'sex': 'male'}
print(struct_data, type(struct_data))
{'name': 'json', 'age': 23, 'sex': 'male'} <class 'dict'>
data = pickle.dumps(struct_data)
print(data, type(data))
b'\x80\x03}q\x00(X\x04\x00\x00\x00nameq\x01X\x04\x00\x00\x00jsonq\x02X\x03\x00\x00\x00ageq\x03K\x17X\x03\x00\x00\x00sexq\x04X\x04\x00\x00\x00maleq\x05u.' <class 'bytes'>
data = pickle.loads(data)
print(data, type(data))
{'name': 'json', 'age': 23, 'sex': 'male'} <class 'dict'>
# 序列化(注意:pickle模块需要使用二进制存储,即'wb'模式存储)
with open('Pickle序列化对象.pkl', 'wb') as fw:
pickle.dump(struct_data, fw)
# 反序列化
with open('Pickle序列化对象.pkl', 'rb') as fr:
pickle = pickle.load(fr)
print(data)
{'name': 'json', 'age': 23, 'sex': 'male'}
os 和 sys模块
os模块(掌握)
os模块负责程序与操作系统交互,多用于文件处理。
方法 | 详解 |
---|---|
os.getcwd() | 获取当前工作目录,即当前python脚本工作的目录路径 |
os.chdir("dirname") | 改变当前脚本工作目录;相当于shell下cd |
os.curdir | 返回当前目录: ('.') |
os.pardir | 获取当前目录的父目录字符串名:('..') |
os.makedirs('dirname1/dirname2') | 可生成多层递归目录 |
os.removedirs('dirname1') | 若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推 |
os.mkdir('dirname') | 生成单级目录;相当于shell中mkdir dirname |
os.rmdir('dirname') | 删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname |
os.listdir('dirname') | 列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印 |
os.remove() | 删除一个文件 |
os.rename("oldname","newname") | 重命名文件/目录 |
os.stat('path/filename') | 获取文件/目录信息 |
os.sep | 输出操作系统特定的路径分隔符,win下为"\",Linux下为"/" |
os.linesep | 输出当前平台使用的行终止符,win下为"\t\n",Linux下为"\n" |
os.pathsep | 输出用于分割文件路径的字符串 win下为;,Linux下为: |
os.name | 输出字符串指示当前使用平台。win->'nt'; Linux->'posix' |
os.system("bash command") | 运行shell命令,直接显示 |
os.environ | 获取系统环境变量 |
os.path.abspath(path) | 返回path规范化的绝对路径 |
os.path.split(path) | 将path分割成目录和文件名二元组返回 |
os.path.dirname(path) | 返回path的目录。其实就是os.path.split(path)的第一个元素 |
os.path.basename(path) | 返回path最后的文件名。如何path以/或\结尾,那么就会返回空值。即os.path.split(path)的第二个元素 |
os.path.exists(path) | 如果path存在,返回True;如果path不存在,返回False |
os.path.isabs(path) | 如果path是绝对路径,返回True |
os.path.isfile(path) | 如果path是一个存在的文件,返回True。否则返回False |
os.path.isdir(path) | 如果path是一个存在的目录,则返回True。否则返回False |
os.path.join(path1[, path2[, ...]]) | 将多个路径组合后返回,第一个绝对路径之前的参数将被忽略 |
os.path.getatime(path) | 返回path所指向的文件或者目录的最后存取时间 |
os.path.getmtime(path) | 返回path所指向的文件或者目录的最后修改时间 |
os.path.getsize(path) | 返回path的大小 |
sys模块(掌握)
sys模块负责程序与Python解释器进行交互,多用于处理环境变量。
方法 | 详解 |
---|---|
sys.argv | 命令行参数List,第一个元素是程序本身路径 |
sys.modules.keys() | 返回所有已经导入的模块列表 |
sys.exc_info() | 获取当前正在处理的异常类,exc_type、exc_value、exc_traceback当前处理的异常详细信息 |
sys.exit(n) | 退出程序,正常退出时exit(0) |
sys.hexversion | 获取Python解释程序的版本值,16进制格式如:0x020403F0 |
sys.version | 获取Python解释程序的版本信息 |
sys.maxint | 最大的Int值 |
sys.maxunicode | 最大的Unicode值 |
sys.modules | 返回系统导入的模块字段,key是模块名,value是模块 |
sys.path | 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值 |
sys.platform | 返回操作系统平台名称 |
sys.stdout | 标准输出 |
sys.stdin | 标准输入 |
sys.stderr | 错误输出 |
sys.exc_clear() | 用来清除当前线程所出现的当前的或最近的错误信息 |
sys.exec_prefix | 返回平台独立的python文件安装的位置 |
sys.byteorder | 本地字节规则的指示器,big-endian平台的值是'big',little-endian平台的值是'little' |
sys.copyright | 记录python版权相关的东西 |
sys.api_version | 解释器的C的API版本 |
random模块
random模块一般用于生成随机数。
import random
# 大于0且小于1之间的小数
print(random.random())
0.25435092120631386
# 大于等于1且小于等于3之间的整数
print(random.randint(1, 3))
2
# 大于等于1且小于3之间的整数
print(random.randrange(1, 3))
1
# 大于1小于3的小数,如1.927109612082716
print(random.uniform(1, 3))
2.718804989532962
# 列表内的任意一个元素,即1或者‘23’或者[4,5]
print(random.choice([1, '23', [4, 5]]))
1
# random.sample([], n),列表元素任意n个元素的组合,示例n=2
print(random.sample([1, '23', [4, 5]], 2))
[[4, 5], '23']
lis = [1, 3, 5, 7, 9]
# 打乱l的顺序,相当于"洗牌"
random.shuffle(lis)
print(lis)
[1, 3, 9, 7, 5]