Python貌似有点火热,上手还是比较简单的,自己找了个教程也偷偷的学习一下,扒了一下网上的图片和数据,感觉并不是很难呀(不过之前换电脑,代码丢了,有点可惜,不过网上教程一抓一大把,随便看看也能扒一些基础的图片了),所以就有了这么一篇基础的学习笔记,自己记录一下,方便自己以后要用的话很快的能够捡起来。
编译型语言
通过编译器,统一编译,一次性执行,最终生成可执行文件(相对执行更快)
代表语言:C语言
解释型语言
逐行解释每一行代码,逐行编译,逐行执行(跨平台运行能力更强),在不同的操作系统上安装不同的解释器,相同源代码使用不同的解释器进行工作
代表语言:Python
优缺点
优点
简单,易学
可读性强
开发速度快
面向对象
免费,开源
可扩展性
具有丰富的库
缺点
运行速度相对较慢
国内市场较小
中文资料匮乏
设计哲学
优雅(代码工整)
明确
简单
用一种方法,最好是只有一种方法来做一件事情
Python特点
完全面向对象的语言(一切皆对象)
拥有一个强大的标准库
Python社区提供了大量的第三方模块
Python3.0没有考虑向下的兼容
Python解释器
CPython -- 官方版本的C语言实现
Jython -- 可以运行在Java平台
IronPython -- .NET和Mono平台
PyPy -- Python实现的,支持JIT即时编译
Python变量
1.不用事先声明变量,赋值过程中就包含了变量的声明和定义的过程
2.用"=" 赋值,左边是变量名,右边是变量值
3.使用前必须先被初始化(先被赋值)
4.可以通过del语句删除不再使用的变量
标识符
区分大小写
第一个字符必须是字母或者下划线
不能使用关键字
以双划线开头或者结尾的名称通常有特殊意义,不建议使用
模块和包名 -> 全小写,尽量简单
函数名 -> 全小写,多个单词用下划线隔开
类名 -> 首字母大小,采用驼峰命名法
常量名 -> 全大写,多个单词使用下划线隔开
链式赋值
使用同一个对象赋值给多个变量 x = y = 123
系列解包赋值
系列数据赋值给对应相同个数的变量(个数必须保持一致)
a,b,c=4,5,6
常量
Python不支持常量,没有语法规则限制改变一个常量的值。只能约定常量的命名规则,以及在程序的逻辑上不对常量的值做出修改
数字
// 整数除法 7//2 -> 3
** 幂 2**3 -> 8
divmod()函数同事得到商和余数 :
divmod(13,3) ->(4,1) 返回元组
整数
int_var = 1
0b或者0B -> 二进制
0o或者0O -> 八进制
0x或者0X -> 十六进制
自动转型,整数和浮点数相加 自动转化成浮点数
Python3中,int可以存储任意大小的整数,long 被取消
使用int()实现类型转换
1.浮点数直接舍去小数部分。 int(9.9) -> 9
2.布尔值True转为1,False转为0。 int(True) -> 1
3.字符串符合整数格式(浮点数据格式不行)则转换成对应整数,否则报错
时间的表示
1970年1月1日0点开始,以毫秒计算
time.time()获得当前时刻,返回的是以秒为单位精度的浮点值
浮点数
float_var = 1.0
round(value)可以返回四舍五入的值,但不会改变原有值,产生新的值
布尔型
True False
字符串型
字符串
字符串是不可变的 没有字符类型,单个字符也是字符串
Python3直接使用Unicode,可以表示世界上任何书名语言的字符
ord('苏') -> 获取单个字符的Unicode编码。chr(65) 获取Unicode编码表示的字符
Python允许空字符串的存在,不包含任何字符且长度为0
字符串切片slice操作
str = "I Love Python"
print(str[2:5]) // 输出第三个到第五个之间的字符,不包括第六个
print(str[:5]) // 输出0-5之间的字符串 (不包括第六个)
print(str[2:]) // 输出第3-最后一个的字符串(包括最后一个)
print(str * 2) // 输出字符串两次
print(str + " And Java") // 拼接之后输出
print(str[0:5:2]) // 最后一个代表步长step
print(str[-6]) // 倒数6个 Python
print(str[::-1]) // 步长为负,从右到左反向提取
字符串搜索
str.isalnum() // 所有字符全是字母或者数字
str.count("Love") // 指定字符出现的次数
str.find("L") // 返回首次出现的下标
str.rfind("o") // 反向搜索,返回首次出现的下标
去除字符串信息
str.strip("*") // 去除首尾指定信息
str.lstrip("*") // 去除字符串首部左边指定信息
str.rstrip("*") // 去除字符串尾部右边指定信息
大小写转换
str.capitalize() // 产生新的字符串,首字母大写
str.title() // 产生新的字符串,每个单词首字母大写
str.upper() // 产生新的字符串 ,所有字符串转换成大写
str.lower() // 产生新的字符串 ,所有字符串转换成小写
str.swapcase() // 产生新的字符串 ,所有字母大小写转换
排版格式
aa = "str"
print(aa.center(10, "*")) ->***str****
print(aa.ljust(10, "*")) ->str*******
print(aa.rjust(10, "*")) ->*******str
format格式化
aa = "名字是:{0},年龄是:{1}。{0}是个好小伙"
print(aa.format("Steven", 18))
b = "名字是:{name},年龄是:{age}。{name}是个好小伙"
print(b.format(name="Steven", age=18)) // 名字是:Steven,年龄是:18。Steven是个好小伙
填充和对齐
填充和对齐一起使用
^、分别是居中、左对齐、右对齐。后面带宽度
:号后面带填充的字符,只能是一个字符,不指定的话默认是空格填充
a = "我的电话号码是{0:*^11}"
print(a.format(888)) // 我的电话号码是****888****
数字格式化
浮点数通过f,整数通过d进行需要的格式化
数字 格式 输出 描述
3.1415926 {:.2f} 3.14 保留小数点后两位
3.1415926 {:+.2f} 3.14 带符号保留小数点后两位
2.71828 {:.0f} 3 不带小数
5 {:0>2d} 05 数字补零 (填充左边, 宽度为 2)
5 {:x<4d} 5xxx 数字补 x (填充右边, 宽度为 4)
10 {:x<4d} 10xx 数字补 x (填充右边, 宽度为 4)
1000000 {:,} 1,000,000 以逗号分隔的数字格式
0.25 {:.2%} 25.00% 百分比格式
1000000000 {:.2e} 1.00E+09 指数记法
13 {:10d} 13 右对齐 (默认, 宽度为 10)
13 {:<10d} 13 左对齐 (宽度为 10)
13 {:^10d} 13 中间对齐 (宽度为 10)
字符串拼接
+两边都是字符串,则拼接
+两边都是数字,则加法运算
+两边类型不同,则抛异常
不换行打印
print("字符串",end = "*") 打印时不会换行,换行符替换成了*符号,使用*连接。*可以是任意字符串
str()实现数字转型字符串
replace()会生成一个新的字符串对象
join字符串拼接
"".join(列表) ->使用将列表中的字符串连接起来
可变字符串 io.StringIo(s)
修改过程中不会产生新的字符串对象
列表
类似于Java中的数组,是一个有序可变的集合容器,支持内置的基础数据结构甚至是列表,列表是可以嵌套的。不同的数据结构可以放在同一个列表中,没有统一类型的限制
list_a = ["str", 1, 1.0]
元组
不可变的列表,在赋值之后就不能二次更改了
tuple_a = ("str", 1, ["a", "b", 1], 2.0)
输出结果: ('str', 1, ['a', 'b', 1], 2.0)
字典(用的比较多)
类似于Java中的map,key-value键值对的形式 无序的容器
dict_a = {
"name": "Steven",
"age": 27,
1: "1800000000"
}
print(dict_a) // {'name': 'Steven', 'age': 27, 1: '1800000000'}
print(dict_a["name"]) // Steven
print(dict_a[1]) // 1800000000
print("name" in dict_a) 判断是否包含改键值// True
print(dict_a.keys()) // dict_keys(['name', 'age', 1])
print(dict_a.values()) // dict_values(['Steven', 27, '1800000000'])
print(dict_a.items()) // dict_items([('name', 'Steven'), ('age', 27), (1, '1800000000')])
逻辑运算符
and 与关系 类似于Java中的&& x and y ->x 为true,则返回y的值。x为false,则直接返回false
or 或关系 类似于Java中的|| x or y ->x 为true,则直接返回true。x为false,则返回y的值
not 非 类似于Java中的!
同一运算符is(is not)
用于比较两个对象的存储单元,实际比较的是对象的地址
整数缓存问题
Python仅仅在命令行中执行时对较小的整数对象进行缓存,范围为[-5,256]之内的整数缓存起来。而在Pycharm或者保存为文件执行时,缓存范围是[-5,任意正整数]
if语句
if (a == b):
print("a == b")
else:
print("a != b")
turtle图形绘制库
\行连接符 续行符
变量位于:堆内存
对象位于:栈内存
序列
序列是一种数据存储方式,用来存储一系列的数据。在内存中,序列就是一块用来存放多个值的连续的内存空间。
常用的序列结构:字符串、列表、元组、字典、集合
列表:用于存储任意数目、任意类型的数据集合。
列表对象的常用方法汇总如下:
方法 要点 描述
list.append(x) 增加元素 将元素 x 增加到列表 list 尾部
list.extend(aList) 增加元素 将列表 alist 所有元素加到列表 list 尾部
list.insert(index,x) 增加元素 在列表 list 指定位置 index 处插入元素 x
list.remove(x) 删除元素 在列表 list 中删除首次出现的指定元素 x
list.pop([index]) 删除元素 删除并返回列表 list 指定位置 index 处的元素,默认是最后一个元素
list.clear() 删除所有元素 删除列表所有元素,并不是删除列表对象
list.index(x) 访问元素 返回第一个 x 的索引位置,若不存在 x 元素抛出异常
list.count(x) 计数 返回指定元素 x 在列表 list 中出现的次数
len(list) 列表长度 返回列表中包含元素的个数
list.reverse() 翻转列表 所有元素原地翻转
list.sort() 排序 所有元素原地排序
list.copy() 浅拷贝 返回列表对象的浅拷贝
Python的列表大小可变,根据需要随时增加或缩小
列表的创建
基本语法[ ]创建
a = [10,20,'gaoqi','sxt']
a = [] #创建一个空的列表对象
list()创建
使用 list()可以将任何可迭代的数据转化成列表。
a = list() #创建一个空的列表对象
a = list(range(10))
a - > [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
a = list("gaoqi,sxt")
a - > ['g', 'a', 'o', 'q', 'i', ',', 's', 'x', 't']
range()创建整数列表
range()可以帮助我们非常方便的创建整数列表,这在开发中及其有用。语法格式为:
range([start,] end [,step])
start 参数:可选,表示起始数字。默认是 0
end 参数:必选,表示结尾数字。
step 参数:可选,表示步长,默认为 1
python3 中 range()返回的是一个 range 对象,而不是列表。我们需要通过 list()方法将其转换成列表对象
a = [x*2 for x in range(100) if x%9==0] #通过 if 过滤元素
[0, 18, 36, 54, 72, 90, 108, 126, 144, 162, 180, 198]
列表元素的增加和删除
当列表增加和删除元素时,列表会自动进行内存管理,大大减少了程序员的负担。但这个特点涉及列表元素的大量移动,效率较低。除非必要,我们一般只在列表的尾部添加元素或删除元素,这会大大提高列表的操作效率。
append()方法
原地修改列表对象,是真正的列表尾部添加新的元素,速度最快,推荐使用。
a = [20,40]
a.append(80)
+运算符操作
并不是真正的尾部添加元素,而是创建新的列表对象;将原列表的元素和新列表的元素依次复制到新的列表对象中。这样,会涉及大量的复制操作,对于操作大量元素不建议使用。
a = a+[50]
extend()方法
将目标列表的所有元素添加到本列表的尾部,属于原地操作,不创建新的列表对象。
a.extend([50,60])
insert()插入元素
使用 insert()方法可以将指定的元素插入到列表对象的任意制定位置。这样会让插入位置后
面所有的元素进行移动,会影响处理速度。涉及大量元素时,尽量避免使用。
类似发生这种移动的函数还有:remove()、pop()、del(),它们在删除非尾部元素时也会发生操作位置后面元素的移动。
a.insert(2,100)
列表元素的删除
本质上是列表的拷贝,将后面的赋值拷贝到前面的值中
del 删除
del a[1]
pop()方法
pop()删除并返回指定位置元素,如果未指定位置则默认操作列表最后一个元素。
a = [10, 20, 30, 40]
a.pop() -> 返回 40
a.pop(1) -> 返回 20
remove()方法
删除首次出现的指定元素,若不存在该元素抛出异常。
a = [10,20,30,40,50,20,30,20,30]
a.remove(20)
列表元素访问和计数
通过索引直接访问元素
我们可以通过索引直接访问元素。索引的区间在[0, 列表长度-1]这个范围。超过这个范围则会抛出异常。
index()获得指定元素在列表中首次出现的索引
index()可以获取指定元素首次出现的索引位置。语法是:index(value,[start,[end]])。其中,
start 和 end 指定了搜索的范围。
count()获得指定元素在列表中出现的次数
len()返回列表长度
成员资格判断
判断列表中是否存在指定的元素,我们可以使用 count()方法,返回 0 则表示不存在,返回大于 0 则表示存在。但是,一般我们会使用更加简洁的 in 关键字来判断,直接返回 True或 False。
切片操作
切片是 Python 序列及其重要的操作,适用于列表、元组、字符串等等
切片 slice 操作可以让我们快速提取子列表或修改。标准格式为:
[起始偏移量 start:终止偏移量 end[:步长 step]]
操作和说明 示例 结果
[:] 提取整个列表 [10,20,30][:] [10,20,30]
[start:]从 start 索引开始到结尾 [10,20,30][1:] [20,30]
[:end]从头开始直到 end-1 [10,20,30][:2] [10,20]
[start:end]从start到 end-1 [10,20,30,40][1:3] [20,30]
[start:end:step] 从 start 提取到 end-1,步长是 step [10,20,30,40,50,60,70][1:6:2] [20, 40, 60]
其他操作(三个量为负数)的情况:
示例 说明 结果
[10,20,30,40,50,60,70][-3:] 倒数三个 [50,60,70]
10,20,30,40,50,60,70][-5:-3] 倒数第五个到倒数第三个(包头不包尾) [30,40]
[10,20,30,40,50,60,70][::-1] 步长为负,从右到左反向提取 [70, 60, 50, 40, 30, 20, 10]
切片操作时,起始偏移量和终止偏移量不在[0,字符串长度-1]这个范围,也不会报错。起始
偏移量小于 0 则会当做 0,终止偏移量大于“长度-1”会被当成”长度-1”
列表的遍历
for obj in listObj:
print(obj)
列表排序
修改原列表,不建新列表的排序
a.sort() #默认是升序排列
a.sort(reverse=True) #降序排列
random.shuffle(a) #打乱顺序
建新列表的排序
通过内置函数 sorted()进行排序,这个方法返回新列表,不对原列表做修改。
a = sorted(a) #默认升序
c = sorted(a,reverse=True) #降序
reversed()返回迭代器
内置函数 reversed()也支持进行逆序排列,与列表对象 reverse()方法不同的是,内置函数
reversed()不对原列表做任何修改,只是返回一个逆序排列的迭代器对象。
迭代器只能使用一次 第二次会返回空
a = [20,10,30,40]
c = reversed(a)
max 和 min
用于返回列表中最大和最小值。
max(a)
min(a)
sum
对数值型列表的所有元素进行求和操作,对非数值型列表运算则会报错。
sum(a)
多维列表
二维列表
一维列表可以帮助我们存储一维、线性的数据。
二维列表可以帮助我们存储二维、表格的数据。
a = [
["高小一",18,30000,"北京"],
["高小二",19,20000,"上海"],
["高小一",20,10000,"深圳"],
]
print(a[1][0],a[1][1],a[1][2])
元组 tuple
列表属于可变序列,可以任意修改列表中的元素。元组属于不可变序列,不能修改元组中的元素。因此,元组没有增加元素、修改元素、删除元素相关的方法。
元组支持如下操作:
1. 索引访问
2. 切片操作
3. 连接操作
4. 成员关系操作
5. 比较运算操作
6. 计数:元组长度 len()、最大值 max()、最小值 min()、求和 sum()等。
通过()创建元组。小括号可以省略。
a = (10,20,30) 或者 a = 10,20,30
如果元组只有一个元素,则必须后面加逗号。这是因为解释器会把(1)解释为整数 1,(1,)解释为元组。
通过 tuple()创建元组
tuple(可迭代的对象)
b = tuple() #创建一个空元组对象
b = tuple("abc")
b = tuple(range(3))
b = tuple([2,3,4])
tuple()可以接收列表、字符串、其他序列类型、迭代器等生成元组。
list()可以接收元组、字符串、其他序列类型、迭代器等生成列表。
元组的元素访问和计数
元组的元素不能修改
元组的元素访问和列表一样,只不过返回的仍然是元组对象。
a = (20,10,30,9,8)
a[1] -> 10
a[1:3] -> (10, 30)
a[:4] -> (20, 10, 30, 9)
列表关于排序的方法 list.sort()是修改原列表对象,元组没有该方法。如果要对元组排序,只能使用内置函数 sorted(tupleObj),并生成新的列表对象。
a = (20,10,30,9,8)
sorted(a) -> [8, 9, 10, 20, 30]
zip
zip(列表 1,列表 2,...)将多个列表对应位置的元素组合成为元组,并返回这个 zip 对象
a = [10,20,30]
b = [40,50,60]
c = [70,80,90]
d = zip(a,b,c)
list(d) -> [(10, 40, 70), (20, 50, 80), (30, 60, 90)]
生成器推导式创建元组
从形式上看,生成器推导式与列表推导式类似,只是生成器推导式使用小括号。列表推
导式直接生成列表对象,生成器推导式生成的不是列表也不是元组,而是一个生成器对象。
我们可以通过生成器对象,转化成列表或者元组。也可以使用生成器对象的__next__()
方法进行遍历,或者直接作为迭代器对象来使用。不管什么方式使用,元素访问结束后,如果需要重新访问其中的元素,必须重新创建该生成器对象。
s = (x * 2 for x in range(5))
print(tuple(s)) -> (0, 2, 4, 6, 8) #只能访问一次元素。第二次就为空了。需要再生成一次
元组总结
元组的核心特点是:不可变序列。
元组的访问和处理速度比列表快。
与整数和字符串一样,元组可以作为字典的键,列表则永远不能作为字典的键使用。
字典
字典是“键值对”的无序可变序列,字典中的每个元素都是一个“键值对”,包含:“键
对象”和“值对象”。可以通过“键对象”实现快速获取、删除、更新对应的“值对象”。
键”是任意的不可变数据,比如:整数、浮点数、字符串、元组。但是:列表、
字典、集合这些可变对象,不能作为“键”。并且“键”不可重复。
字典的创建
我们可以通过{}、dict()来创建字典对象。
a = {'name':'Steven','age':18,'job':'programmer'}
b = dict(name='Steven',age=18,job='programmer')
a = dict([("name","Steven"),("age",18)])
a = dict({("name", "Steven"), ("age", 18)})
a = dict({'name': 'Steven', 'age': 18, 'job': 'programmer'})
c = {} #空的字典对象
d = dict() #空的字典对象
通过 zip()创建字典对象
k = ['name','age','job']
v = ['Steven',18,'programmer']
d = dict(zip(k,v))
print(d) -> {'name': 'Steven', 'age': 18, 'job':'programmer'}
通过 fromkeys 创建值为空的字典
a = dict.fromkeys(['name','age','job'])
print(a) -> {'name': None, 'age': None, 'job': None} # none 是一个值,表示没有赋值
字典元素的访问
a = {'name':'Steven','age':18,'job':'programmer'}
通过 [键] 获得“值”。若键不存在,则抛出异常。
a['name'] -> 'Steven'
通过 get()方法获得“值”。推荐使用。优点是:指定键不存在,返回 None;也可以设定指定键不存在时默认返回的对象。推荐使用 get()获取“值对象”。
a.get('name') -> 'Steven'
a.get('sex','一个男人') -> '一个男人'
列出所有的键值对
a.items() -> dict_items([('name', 'Steven'), ('age', 18), ('job', 'programmer')])
列出所有的键,列出所有的值
a.keys() -> dict_keys(['name', 'age', 'job'])
a.values() -> dict_values(['Steven', 18, 'programmer'])
len() 键值对的个数
len(a)
a.__len__()
检测一个“键”是否在字典中
print("name" in a) -> True
字典元素添加、修改、删除
给字典新增“键值对”。如果“键”已经存在,则覆盖旧的键值对;如果“键”不存在,
则新增“键值对”。
a['address']='世界大厦1101'
a['age']=16
print(a) -> {'name': 'Steven', 'age': 16, 'job': 'programmer', 'address': '世界大厦1101'}
使用 update()将新字典中所有键值对全部添加到旧字典对象上。如果 key 有重复,则直接覆盖
b = {'name': 'StevenSon', 'money': 1000, 'sex': '男的'}
a.update(b)
print(a) -> {'name': 'StevenSon', 'age': 16, 'job': 'programmer', 'money': 1000, 'sex': '男的'}
字典中元素的删除,可以使用 del()方法;或者 clear()删除所有键值对;pop()删除指定键值对,并返回对应的“值对象”;
del(a["name"])
print(a) -> {'age': 18, 'job': 'programmer'}
a_value = a.pop("age")
print(a) -> {'job': 'programmer'}
print(a_value) -> 18
popitem() :随机删除和返回该键值对。若想一个接一个地移除并处理项,这个方法就非常有效(因为不用首先获取键的列表)。
print(a.popitem()) -> ('job', 'programmer')
print(a) -> {'name': 'Steven', 'age': 18}
序列解包
序列解包可以用于元组、列表、字典。序列解包可以让我们方便的对多个变量赋值。
x,y,z=(20,30,10)
(a,b,c)=(9,8,10)
[a,b,c]=[10,20,30]
序列解包用于字典时,默认是对“键”进行操作; 如果需要对键值对操作,则需要使用
items();如果需要对“值”进行操作,则需要使用 values();
s = {'name':'gaoqi','age':18,'job':'teacher'}
name,age,job=s #默认对键进行操作
name,age,job=s.items() #对键值对进行操作
name,age,job=s.values() #对值进行操作
字典核心底层原理(重要)
字典对象的核心是散列表。散列表是一个稀疏数组(总是有空白元素的数组),数组的
每个单元叫做 bucket。每个 bucket 有两部分:一个是键对象的引用,一个是值对象的引用。
由于,所有 bucket 结构和大小一致,我们可以通过偏移量来读取指定 bucket。
字典底层原理.png
字典底层原理2.png
根据键查找“键值对”的底层过程
字典底层取值.png
用法总结:
键必须可散列
(1) 数字、字符串、元组,都是可散列的。
(2) 自定义对象需要支持下面三点:
1 支持 hash()函数
2 支持通过__eq__()方法检测相等性。
3 若 a==b 为真,则 hash(a)==hash(b)也为真。
字典在内存中开销巨大,典型的空间换时间。
键查询速度很快
往字典里面添加新建可能导致扩容,导致散列表中键的次序变化。因此,不要在遍历字典的同时进行字典的修改。 会报RuntimeError: dictionary changed size during iteration错误
集合
集合是无序可变,元素不能重复。实际上,集合底层是字典实现,集合的所有元素都是字典中的“键对象”,因此是不能重复的且唯一的。
集合创建和删除
使用{}创建集合对象,并使用 add()方法添加元素
a = {3, 5, 7}
a.add(9)
使用 set(),将列表、元组等可迭代对象转成集合。如果原来数据存在重复数据,则只保留一个。
b = set(a)
remove()删除指定元素;clear()清空整个集合
a.remove(3)
集合相关操作
Python 对集合提供了并集、交集、差集等运算
a = {1, 3, 'sxt'}
b = {'he', 'it', 'sxt'}
a|b 或者 a.union(b) #并集 -> {1, 3, 'sxt', 'he', 'it'}
a&b 或者 a.intersection(b) #交集 ->{'sxt'}
a-b 或者 a.difference(b) #差集 ->{1, 3}
控制语句
选择结构
单分支选择结构
if 语句单分支结构的语法形式如下:
if 条件表达式:
语句/语句块
num = input("输入一个数字:")
if int(num)<10:
print(num)
条件表达式详解
在选择和循环结构中,条件表达式的值为 False 的情况如下:
False、0、0.0、空值 None、空序列对象(空列表、空元祖、空集合、空字典、空字符串)、空 range 对象、空迭代对象。其他情况均为True
条件表达式中,不能有赋值操作符“=”
双分支选择结构
双分支结构的语法格式如下:
if 条件表达式 :
语句 1/语句块 1
else:
语句 2/语句块 2
三元条件运算符
Python 提供了三元运算符,用来在某些简单双分支赋值情况。三元条件运算符语法格式如下:
条件为真时的值 if (条件表达式) else 条件为假时的值
多分支选择结构
多分支选择结构的语法格式如下:
if 条件表达式 1 :
语句 1/语句块 1
elif 条件表达式 2:
语句 2/语句块 2
.
.
.
elif 条件表达式 n :
语句 n/语句块 n
[else:
语句 n+1/语句块 n+1
]
【注】计算机行业,描述语法格式时,使用中括号[ ]通常表示可选,非必选。
选择结构嵌套
选择结构可以嵌套,使用时一定要注意控制好不同级别代码块的缩进量,因为缩进量决定了代码的从属关系。
循环结构
循环结构用来重复执行一条或多条语句。表达这样的逻辑:如果符合条件,则反复执行循环体里的语句。在每次执行完后都会判断一次条件是否为 True,如果为 True 则重复执行循环体里的语句。
循环体里面的语句至少应该包含改变条件表达式的语句,以使循环趋于结束;否则,就会变成一个死循环。
while 循环
while 循环的语法格式如下:
while 条件表达式:
循环体语句
for 循环和可迭代对象遍历
for 循环通常用于可迭代对象的遍历。for 循环的语法格式如下:
for 变量 in 可迭代对象:
循环体语句
可迭代对象
Python 包含以下几种可迭代对象:
序列。包含:字符串、列表、元组
字典
迭代器对象(iterator)
生成器函数(generator)
文件对象
range 对象
range 对象是一个迭代器对象,用来产生指定范围的数字序列。格式为:
range(start, end [,step])
生成的数值序列从 start 开始到 end 结束(不包含 end)。若没有填写 start,则默认从 0开始。step 是可选的步长,默认为 1。
for i in range(10) 产生序列:0 1 2 3 4 5 6 7 8 9
for i in range(3,10) 产生序列:3 4 5 6 7 8 9
for i in range(3,10,2) 产生序列:3 5 7 9
break 语句
break 语句可用于 while 和 for 循环,用来结束整个循环。当有嵌套循环时,break 语句只能跳出最近一层的循环。
continue 语句
continue 语句用于结束本次循环,继续下一次。多个循环嵌套时,continue 也是应用于最
近的一层循环。
else 语句
while、for 循环可以附带一个 else 语句(可选)。如果 for、while 语句没有被 break 语句
结束,则会执行 else 子句,否则不执行。语法格式如下:
while 条件表达式:
循环体
else:
语句块
或者:
for 变量 in 可迭代对象:
循环体
else:
语句块
循环代码优化
编写循环时,遵守下面三个原则可以大大提高运行效率,避免不必要的低效计算:
尽量减少循环内部不必要的计算
嵌套循环中,尽量减少内层循环的计算,尽可能向外提。
局部变量查询较快,尽量使用局部变量
其他优化手段
连接多个字符串,使用 join()而不使用+
列表进行元素插入和删除,尽量在列表尾部操作
使用 zip()并行迭代
我们可以通过 zip()函数对多个序列进行并行迭代,zip()函数在最短序列“用完”时就会停止。
names = ("Steven","高老二","高老三","高老四")
ages = (18,16,20,25)
jobs = ("老师","程序员","公务员")
for name,age,job in zip(names,ages,jobs):
print("{0}--{1}--{2}".format(name,age,job)
执行结果:
Steven--18--老师
高老二--16--程序员
高老三--20--公务员
推导式创建序列
推导式是从一个或者多个迭代器快速创建序列的一种方法。它可以将循环和条件判断结合,
从而避免冗长的代码。推导式是典型的 Python 风格,会使用它代表你已经超过 Python 初
学者的水平。
列表推导式
列表推导式生成列表对象,语法如下:
[表达式 for item in 可迭代对象 ]
或者:
{表达式 for item in 可迭代对象 if 条件判断
[x*2 for x in range(1,5)] -> [2, 4, 6, 8]
[x*2 for x in range(1,20) if x5==0 ] -> [10, 20, 30]
[a for a in "abcdefg"] -> ['a', 'b', 'c', 'd', 'e', 'f', 'g']
cells = [(row,col) for row in range(1,10) for col in range(1,10)] #可以使用两个循环
字典推导式
字典的推导式生成字典对象,格式如下:
{key_expression : value_expression for 表达式 in 可迭代对象}
my_text = ' i love you, i love sxt,i love Steven'
char_count = {c:my_text.count(c) for c in my_text}
集合推导式
集合推导式生成集合,和列表推导式的语法格式类似:
{表达式 for item in 可迭代对象 }
或者:
{表达式 for item in 可迭代对象 if 条件判断}
生成器推导式(生成元组)
元组是没有推导式的,推导式推导出的是一个生成器,可以通过生成器生成元组。一个生成器只能运行一次。第一次迭代可以得到数据,第二次迭代发现数据已经没有了。
tunple((x for x in range(4))) -> (0, 1, 2, 3)
函数用法和底层分析
Python 函数的分类
Python 中函数分为如下几类:
内置函数
我们前面使用的 str()、list()、len()等这些都是内置函数,我们可以拿来直接使用。
标准库函数
我们可以通过 import 语句导入库,然后使用其中定义的函数
第三方库函数
Python 社区也提供了很多高质量的库。下载安装这些库后,也是通过 import 语句导
入,然后可以使用这些第三方库的函数
用户自定义函数
函数的定义和调用
核心要点
Python 中,定义函数的语法如下:
def 函数名 ([参数列表]) :
'''文档字符串'''
函数体/若干语句
Python 执行 def 时,会创建一个函数对象,并绑定到函数名变量上。即函数也是一个对象
参数列表
(1) 圆括号内是形式参数列表,有多个参数则使用逗号隔开
(2) 形式参数不需要声明类型,也不需要指定函数返回值类型
(3) 无参数,也必须保留空的圆括号
(4) 实参列表必须与形参列表一一对应
return 返回值
(1) 如果函数体中包含 return 语句,则结束函数执行并返回值;
(2) 如果函数体中不包含 return 语句,则返回 None 值。
return 返回值要点:
如果函数体中包含 return 语句,则结束函数执行并返回值;
如果函数体中不包含 return 语句,则返回 None 值。
要返回多个返回值,使用列表、元组、字典、集合将多个值“存起来”即可。
文档字符串(函数的注释)
通过三个单引号或者三个双引号来实现,中间可以加入多行文字进行说明。
调用 help(函数名.__doc__)可以打印输出函数的文档字符串
变量的作用域( 全局变量和局部变量)
全局变量:
在函数和类定义之外声明的变量。作用域为定义的模块,从定义位置开始直到模块
结束。
全局变量降低了函数的通用性和可读性。应尽量避免全局变量的使用。
全局变量一般做常量使用。
函数内要改变全局变量的值,使用 global 声明一下
global a #如果要在函数内改变全局变量的值,增加 global 关键字声明
局部变量:
在函数体中(包含形式参数)声明的变量。
局部变量的引用比全局变量快,优先考虑使用。
如果局部变量和全局变量同名,则在函数内隐藏全局变量,只使用同名的局部变量
参数的传递
函数的参数传递本质上就是:从实参到形参的赋值操作。 Python 中“一切皆对象”,
所有的赋值操作都是“引用的赋值”。所以,Python 中参数的传递都是“引用传递”,不
是“值传递”。具体操作时分为两类:
对“可变对象”进行“写操作”,直接作用于原对象本身。
对“不可变对象”进行“写操作”,会产生一个新的“对象空间”,并用新的值填
充这块空间。(起到其他语言的“值传递”效果,但不是“值传递”)
可变对象:
字典、列表、集合、自定义的对象等
不可变对象:
数字、字符串、元组、function 等
传递可变对象的引用
传递参数是可变对象(例如:列表、字典、自定义的其他可变对象等),实际传递的还是对
象的引用。在函数体中不创建新的对象拷贝,而是可以直接修改所传递的对象
传递可变对象的引用
传递参数是可变对象,实际传递的还是对象的引用。在函数体中不创建新的对象拷贝,而是可以直接修改所传递的对象。
a = 100
def f1(n):
print("n:",id(n)) #传递进来的是 a 对象的地址
n = n+200 #由于 a 是不可变对象,因此创建新的对象 n
print("n:",id(n)) #n 已经变成了新的对象
print(n)
f1(a)
print("a:",id(a)
执行结果:
n: 1663816464
n: 46608592
300
a: 1663816464
浅拷贝和深拷贝
内置函数:copy(浅拷贝)、deepcopy(深拷贝)。
浅拷贝:不拷贝子对象的内容,只是拷贝子对象的引用。
深拷贝:会连子对象的内存也全部拷贝一份,对子对象的修改不会影响源对象
传递不可变对象包含的子对象是可变的情况
传递不可变对象时。不可变对象里面包含的子对象是可变的。则方法内修改了这个可变对象,源对象也发生了变化
参数的几种类型
位置参数
函数调用时,实参默认按位置顺序传递,需要个数和形参匹配。按位置传递的参数,称为:“位置参数”。
默认值参数
我们可以为某些参数设置默认值,这样这些参数在传递时就是可选的。称为“默认值参数”。默认值参数放到位置参数后面。
命名参数
我们也可以按照形参的名称传递参数,称为“命名参数”,也称“关键字参数”。参数传递顺序可变
可变参数
可变参数指的是“可变数量的参数”。分两种情况:
*param(一个星号),将多个参数收集到一个“元组”对象中。
**param(两个星号),将多个参数收集到一个“字典”对象中。
def f1(a,b,*c):
print(a,b,c)
f1(8,9,19,20)
def f2(a,b,**c):
print(a,b,c)
f2(8,9,name='gaoqi',age=18)
def f3(a,b,*c,**d):
print(a,b,c,d)
f3(8,9,20,30,name='gaoqi',age=18
强制命名参数
在带星号的“可变参数”后面增加新的参数,必须在调用的时候“强制命名参数”
lambda 表达式和匿名函数
lambda 表达式可以用来声明匿名函数。lambda 函数是一种简单的、在同一行中定义函数
的方法。lambda 函数实际生成了一个函数对象。
lambda 表达式只允许包含一个表达式,不能包含复杂语句,该表达式的计算结果就是函数
的返回值
lambda 表达式的基本语法如下:
lambda arg1,arg2,arg3... :
arg1/arg2/arg3 为函数的参数。相当于函数体。运算结果是:表达式的运算结果。
eval()函数
功能:将字符串 str 当成有效的表达式来求值并返回计算结果。
nonlocal 关键字
nonlocal 用来声明外层的局部变量。
global 用来声明全局变量
LEGB 规则
Python 在查找“名称”时,是按照 LEGB 规则查找的:
Local-->Enclosed-->Global-->Built in
Local 指的就是函数或者类的方法内部
Enclosed 指的是嵌套函数(一个函数包裹另一个函数,闭包)
Global 指的是模块中的全局变量
Built in 指的是 Python 为自己保留的特殊名称。
对象
类
类的定义
定义类的语法格式:
class 类名:
类体
类名必须符合“标识符”的规则;一般规定,首字母大写,多个单词使用“驼峰原则”。
类体中我们可以定义属性和方法。
属性用来描述数据,方法(即函数)用来描述这些数据相关的操作。
__init__构造方法和__new__方法
类是抽象的,也称之为“对象的模板”。
__init__()的要点如下:
名称固定,必须为:__init__()
第一个参数固定,必须为:self。 self 指的就是刚刚创建好的实例对象。
构造函数通常用来初始化实例对象的实例属性
通过“类名(参数列表)”来调用构造函数
__init__()方法:初始化创建好的对象,初始化指的是:“给实例属性赋值”
__new__()方法: 用于创建对象,但我们一般无需重定义该方法。
如果我们不定义__init__()方法,系统会提供一个默认的__init__()方法。如果我们定义了带参
的__init__()方法,系统不创建默认的__init__()方法。
注:
Python 中的 self 相当于 C++中的 self 指针,JAVA 和 C#中的 this 关键字。
Python 中,self 必须为构造函数的第一个参数,名字可以任意修改。但一般遵守惯例,都叫做 self。
实例属性和实例方法
实例属性
实例属性是从属于实例对象的属性,也称为“实例变量”。
使用要点:
实例属性一般在\__init__()方法中通过如下代码定义:
self.实例属性名 = 初始值
在本类的其他实例方法中,也是通过 self 进行访问:
self.实例属性名
创建实例对象后,通过实例对象访问:
obj01 = 类名() #创建对象,调用__init__()初始化属性
obj01.实例属性名 = 值 #可以给已有属性赋值,也可以新加属性
实例方法
实例方法是从属于实例对象的方法。实例方法的定义格式如下:
def 方法名(self [, 形参列表]):
函数体
方法的调用格式如下:
对象.方法名([实参列表])
要点:
定义实例方法时,第一个参数必须为 self。和前面一样,self 指当前的实例对象。
调用实例方法时,不需要也不能给 self 传参。self 由解释器自动传参。
函数和方法的区别
都是用来完成一个功能的语句块,本质一样。
方法调用时,通过对象来调用。方法从属于特定实例对象,普通函数没有这个特点。
直观上看,方法定义时需要传递 self,函数不需要。
其他操作:
dir(obj)可以获得对象的所有属性、方法
obj._dict_ 对象的属性字典
pass 空语句
isinstance(对象,类型) 判断“对象”是不是“指定类型”
类对象、类属性、类方法、静态方法
类对象
类对象的类型就是type
pass 为空语句。就是表示什么都不做,只是作为一个占位符存在。当你写代码时,
遇到暂时不知道往方法或者类中加入什么时,可以先用 pass 占位,后期再补上。
类属性
类属性是从属于“类对象”的属性,也称为“类变量”。由于,类属性从属于类对象,可以
被所有实例对象共享。
类属性的定义方式:
class 类名:
类变量名= 初始值
class Student:
company = "SXT" #类属性
count = 0 #类属性
def __init_(self,name,score):
self.name = name #实例属性
self.score = scor
类方法
类方法是从属于“类对象”的方法。类方法通过装饰器@classmethod 来定义,格式如下:
@classmethod
def 类方法名(cls [,形参列表]) :
函数体
要点如下:
@classmethod 必须位于方法上面一行
第一个 cls 必须有;cls 指的就是“类对象”本身;
调用类方法格式:“类名.类方法名(参数列表)”。 参数列表中,不需要也不能给 cls 传
值。
类方法中访问实例属性和实例方法会导致错误
子类继承父类方法时,传入 cls 是子类对象,而非父类对象
静态方法
Python 中允许定义与“类对象”无关的方法,称为“静态方法”。
“静态方法”和在模块中定义普通函数没有区别,只不过“静态方法”放到了“类的名字空
间里面”,需要通过“类调用”。
静态方法通过装饰器@staticmethod 来定义,格式如下:
@staticmethod
def 静态方法名([形参列表]) :
函数体
要点如下:
@staticmethod 必须位于方法上面一行
调用静态方法格式:“类名.静态方法名(参数列表)”。
静态方法中访问实例属性和实例方法会导致错误
__del__方法(析构函数)和垃圾回收机制
__del__方法称为“析构方法”,用于实现对象被销毁时所需的操作。
__del__方法称为“析构方法”,用于实现对象被销毁时所需的操作。
可以通过 del 语句删除对象,从而保证调用__del__方法。系统会自动提供__del__方法,一般不需要自定义析构方法。
__call__方法和可调用对象
定义了__call__方法的对象,称为“可调用对象”,即该对象可以像函数一样被调用。
class SalaryAccount:
def __call__(self, salary):
yearSalary = salary*12
daySalary = salary//30
hourSalary = daySalary//8
return dict(monthSalary=salary,yearSalary=yearSalary,daySalary=daySalary,hourSalary=hourSalary)
s = SalaryAccount()
print(s(5000)
{'monthSalary': 5000, 'yearSalary': 60000, 'daySalary': 166, 'hourSalary': 20}
方法没有重载
Python 中,方法的的参数没有声明类型(调用时确定参数的类型),参数的数量也可以由
可变参数控制。
不要使用重名的方法!Python 中方法没有重载
方法的动态性
Python 是动态语言,我们可以动态的为类添加新的方法,或者动态的修改类的已有的方法。
Python 一切皆对象
私有属性和私有方法(实现封装)
Python 对于类的成员没有严格的访问控制限制,这与其他面向对象语言有区别。关于私有
属性和私有方法,有如下要点:
通常我们约定,两个下划线开头的属性是私有的(private)。其他为公共的(public)。
类内部可以访问私有属性(方法)
类外部不能直接访问私有属性(方法)
类外部可以通过“_类名__私有属性(方法)名”访问私有属性(方法)p._Employee__age
@property 装饰器
@property 可以将一个方法的调用方式变成“属性调用”。
class Employee
def __init__(self,name,salary):
self.name = name
self.__salary = salary
@property #相当于 salary 属性的 getter 方法
def salary(self):
print("月薪为{0},年薪为{1}".format(self.__salary,(12*self.__salary)))
return self.__salary;
@salary.setter
def salary(self,salary): #相当于 salary 属性的 setter 方法
if(0
self.__salary = salary
else:
print("薪水录入错误!只能在 0-1000000 之间"
面向对象
继承
语法格式
Python 支持多重继承,一个子类可以继承多个父类。继承的语法格式如下:
class 子类类名(父类 1[,父类 2,...]):
类体
默认父类是 object 类
class Person:
def __init__(self,name,age):
self.name = name
self.__age = age
class Student(Person):
def __init__(self,name,age,score):
self.score = score
Person.__init__(self,name,age) #构造函数中包含调用父类构造函数。根据需要,不是必须。
子类并不会自动调用父类的\_\_init\_\_(),我们必须显式的调用
类成员的继承和重写
成员继承:子类继承了父类除构造方法之外的所有成员。
方法重写:子类可以重新定义父类中的方法,这样就会覆盖父类的方法,也称为“重写”
重写__str__()方法
object 有一个__str__()方法,用于返回一个对于“对象的描述”,对应于内置函数 str()
经常用于 print()方法,帮助我们查看对象的信息。__str__()可以重写。
多重继承
Python 支持多重继承,一个子类可以有多个“直接父类”。这样,就具备了“多个父类”的特点。但是由于这样会被“类的整体层次”搞的异常复杂,尽量避免使用。
MRO()
Python 支持多继承,如果父类中有相同名字的方法,在子类没有指定父类名时,解释器将
“从左向右”按顺序搜索。
MRO(Method Resolution Order):方法解析顺序。 我们可以通过 mro()方法获得
“类的层次结构”,方法解析顺序也是按照这个“类的层次结构”寻找的。
super()获得父类定义
在子类中,如果想要获得父类的方法时,我们可以通过 super()来做。
super()代表父类的定义,不是父类对象。
多态
多态(polymorphism)是指同一个方法调用由于对象不同可能会产生不同的行为。
多态是方法的多态,属性没有多态。
多态的存在有 2 个必要条件:继承、方法重写。
特殊方法和运算符重载
Python 的运算符实际上是通过调用对象的特殊方法实现的。
常见的特殊方法统计如下:
方法 说明 例子
__init__ 构造方法 对象创建:p = Person()
__del__ 析构方法 对象回收
__repr__,__str__ 打印,转换 print(a)
__call__ 函数调用 a()
__getattr__ 点号运算 a.xxx
__setattr__ 属性赋值 a.xxx = value
__getitem__ 索引运算 a[key]
__setitem__ 索引赋值 a[key]=value
__len__ 长度 len(a)
运算符 特殊方法 说明
运算符+ __add__ 加法
运算符- __sub__ 减法
>,>=,!= __gt__,__ge__,__ne__ 比较运算符
|,^,& __or__,__xor__,__and__ 或、异或、与
<> __lshift__,__rshift__ 左移、右移
*,/,%,// __mul__,__truediv__,__mod__,__floordiv__ 乘、浮除、模运算
(取余)、整数除
** __pow__ 指数运算
每个运算符实际上都对应了相应的方法
特殊属性
Python 对象中包含了很多双下划线开始和结束的属性,这些是特殊属性,有特殊用法
特殊方法 含义
obj.__dict__ 对象的属性字典
obj.__class__ 对象所属的类
class.__bases__ 类的基类元组(多继承)
class.__base__ 类的基类
class.__mro__ 类层次结构
class.__subclasses__() 子类列表
对象的浅拷贝和深拷贝
变量的赋值操作
只是形成两个变量,实际还是指向同一个对象。
浅拷贝
Python 拷贝一般都是浅拷贝。拷贝时,对象包含的子对象内容不拷贝。因此,源对象
和拷贝对象会引用同一个子对象。
深拷贝
使用 copy 模块的 deepcopy 函数,递归拷贝对象中包含的子对象。源对象和拷贝对象
所有的子对象也不同
组合
is-a”关系,我们可以使用“继承”。从而实现子类拥有的父类的方法和属性。
“has-a”关系,我们可以使用“组合”,也能实现一个类拥有另一个类的方法和属性。
设计模式_工厂模式实现
工厂模式实现了创建者和调用者的分离,使用专门的工厂类将选择实现类、创建对象进行统一的管理和控制。
class CarFactory:
def createCar(self,brand):
if brand == "奔驰":
return Benz()
elif brand == "宝马":
return BMW()
elif brand == '比亚迪':
return BYD()
else:
return "未知品牌,无法创建"
class Benz:
pass
class BMW:
pass
class BYD:
pass
factory = CarFactory()
c1 = factory.createCar("奔驰")
c2 = factory.createCar("宝马")
设计模式_单例模式实现
单例模式(Singleton Pattern)的核心作用是确保一个类只有一个实例,并且提供一个访问该实例的全局访问点。
class MySingleton:
__obj = None
__init_flag = True
def __new__(cls, *args, **kwargs):
if cls.__obj == None:
cls.__obj = object.__new__(cls) # 创建父类对象
return cls.__obj
def __init__(self,name):
if MySingleton.__init_flag: # 保证初始化只执行一次
print( "init....")
self.name = name
MySingleton.__init_flag = False
a = MySingleton( "aa")
print(a)
b = MySingleton( "bb")
print(b)
异常
在执行程序的时候遇到错误,会停止程序的执行,并提示错误信息,这就是异常
捕获异常
语法格式:
try:
# 尝试执行的代码
except:
# 出现错误的处理
捕获特殊错误类型
程序运行出错的第一个单词,就是错误类型
try:
# 尝试执行的代码
pass
except 错误类型1:
# 出现错误类型1的处理
pass
except (错误类型2, 错误类型3):
# 出现错误类型2或者错误类型3的处理
pass
捕获未知错误
except Exception as result: # result变量接收错误信息
print("未知错误 %s" % result)
异常捕获完整语法
try:
pass
except ValueError:
pass
except Exception as result:
pass
else:
# 没有异常执行的代码
pass
finally:
# 无论是否出现异常,都会执行。最后执行
pass
异常的传递
当函数/方法出现错误时,会把异常传递给函数/方法的调用处,直到主程序
当异常抛出到主程序时,仍然没有进行异常捕获,程序就会终止
利用异常的传递性,可以在主程序中捕获异常
抛出raise异常
创建一个Exception的对象
使用raise关键字抛出异常对象
# 创建异常对象
ex = Exception("异常描述信息")
# 主动抛出异常
raise ex # raise + 异常对象
模块
模块的导入
import导入
模块导入关键字import,导入模块应该单行导入一个模块
模块名.的方式使用模块中提供的类,全局变量,函数
使用关键字as指定模块的别名,别名命名方法使用大驼峰命名法(单个单词首字母大写)
import 模块名 as 模块名
from...import导入
从一个模块中,导入部分工具
from 模块名 import 工具名
通过from导入之后,不需要通过模块名.的方式来使用。可以直接使用工具名进行使用
注意:如果两个模块,存在同名的函数,那么后导入的函数,会覆盖掉之前导入的函数。
一旦发现有同名的函数,可以使用as指定别名
模块的搜索顺序
搜索当前目录指定模块名的文件,如果有就直接导入
如果没有,再搜索系统目录
__name__属性
文件被导入时,能够直接执行的代码不需要被执行!因此需要使用__name__属性来做判断。
__name__是Python的一个内置属性,记录着一个字符串.
如果是被其他文件导入的,__name__就是模块名
如果是当前执行的程序__name__就是__main__
Python文件的标准代码格式:
# 导入模块
# 定义全局变量
# 定义类
# 定义函数
def main():
pass
# 根据__name__属性判断是否需要执行代码
if __name__="__main__"
main()
包
包是一个包含多个模块的特殊目录
目录下有一个特殊文件:__init__.py
包的命名方式和变量名一样,小写字母+下划线
使用import 包名 可以一次性导入包中的所有模块
__init__.py
要在外界使用包中的模块,需要在__init__.py文件中指定对外界提供的模块列表
from . import 模块列表
发布模块
将自己开发的模块,分享给其他人
制作发布压缩包
制作压缩包步骤.png
安装删除模块
安装删除模块.png
pip安装删除模块
pip安装删除模块.png
文件
文件操作步骤
打开文件 open()
读写文件 read() write()
关闭文件 close()
read()函数运行之后会将指针移动到文件末尾,再次读的话是读取不到内容的
文件复制
小文件
小文件直接复制
file = open("move.json", encoding="utf-8")
text = file.read()
copyFile = open("copyMove.json", mode="w", encoding="utf-8")
copyFile.write(text)
file.close()
copyFile.close()
大文件
大文件需要逐行复制
file = open("move.json", encoding="utf-8")
copyFile = open("copyMove.json", mode="w", encoding="utf-8")
while True:
text = file.readline() # 逐行读取
if not text:
break
copyFile.write(text)
file.close()
copyFile.close()
文件操作
文件操作需要导入os模块
rename() 重命名
remove() 删除文件
# 文件重命名
os.rename("copyMove.json", "reNameCopyMove.json")
# 删除文件
os.remove("reNameCopyMove.json")
文本编码格式
Python2.x 默认是ASCII编码格式
Python3.x 默认是UTF-8编码格式 (UTF-8是Unicode的一种)
Python2.x 中第一行增加一行代码注释就可以处理Python文件中的中文导致的错误
# *-* coding:utf8 *-* 官方推荐
# coding = utf8
eval函数
eval()函数将字符串当成有效的表达式,并返回计算结果
print(eval("1+1")) # 输出2
eval函数不要滥用,不要直接转换input结果
网络编程
IP地址
目的:用来标记网络上的一台设备(唯一)
dest_ip 目标IP
src_ip 源IP
ip地址的分类
IP地址由四组数字组成 (0-255)
IP地址分类.png
端口
dest_port 目标端口
src_port 源端口
用来标识PC的应用进程
知名端口
知名端口0-1023 动态端口从1024-65535
80端口http服务 21端口分配给FTP服务
socket
import socket
# 创建一个socket UDP
udp_socket = socket.socketpair(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定端口号
udp_socket.bind("端口号")
# 使用套接字收发数据 数据和对方的IP地址以及端口号
udp_socket.sendto("数据", ("192.158.33.23", 8080))
udp_socket.close()
UDP收发收据
同一个套接字可以收发数据
import socket
# UDP聊天器,并保存聊天记录
def main():
# 创建一个socket
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
file = open("chatRecord.txt", mode="a", encoding="utf-8")
while True:
print("UDP聊天器")
print("输入0结束程序")
print("输入1发送数据")
print("输入2接收数据")
input_data = int(input())
if 0 == input_data:
file.close()
break
elif 1 == input_data:
file.write(str(send_data(udp_socket)) + "\n")
elif 2 == input_data:
file.write(str(receive_data(udp_socket)) + "\n")
else:
print("输入错误")
udp_socket.close()
# socket 接收数据
def receive_data(udp_socket):
receive_data = udp_socket.recvfrom(1024)
print("发送者:%s\n发送内容:%s" % (str(receive_data[1]), receive_data[0].decode("gbk")))
return "发送者:%s\n发送内容:%s" % (str(receive_data[1]), receive_data[0].decode("gbk"))
# 使用套接字发送数据
def send_data(udp_socket):
dest_ip = input("请输入对方的ip地址:")
dest_port = int(input("请输入对方的端口号:"))
send_data = input("请输入要聊天的内容:")
udp_socket.sendto(send_data.encode("gbk"), (dest_ip, dest_port))
return str(send_data.encode("utf-8"), encoding="utf-8")
if __name__ == '__main__':
main()
TCP
TCP客户端收发数据
import socket
def main():
# 创建套接字
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_socket.bind(("", 7788))
# 连接服务器
tcp_socket.connect(("192.168.28.90", 8080))
send_data = input("请输入要发送的内容:")
# 发送数据
tcp_socket.send(send_data.encode("gbk"))
# 接收数据
receive_data = tcp_socket.recvfrom(1024)
print("发送者:%s\n发送内容:%s" % (receive_data[1], receive_data[0].decode("gbk")))
# 关闭套接字
tcp_socket.close()
if __name__ == '__main__':
main()
TCP服务端收发数据
import socket
# 创建socket
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定端口信息
tcp_server_socket.bind(("", 7000))
tcp_server_socket.listen(128)
new_client_socket, client_address = tcp_server_socket.accept()
print(client_address)
receive_data = new_client_socket.recv(1024)
print(receive_data.decode("gbk"))
new_client_socket.send("收到信息啦...".encode("gbk"))
new_client_socket.close()
tcp_server_socket.close()
多任务
多线程
线程执行的顺序不确定
调用线程的start()方法时才会创建线程,以及运行线程
import time
import threading
def sing():
for i in range(5):
print("------唱歌--------")
time.sleep(1)
def dance():
for i in range(5):
print("=========跳舞========")
time.sleep(1)
def main():
sing_thread = threading.Thread(target=sing) # 注意这里是要用函数名,不能用函数名()->这个是调用函数
dance_thread = threading.Thread(target=dance)
sing_thread.start()
dance_thread.start()
if __name__ == '__main__':
main()
多线程共享全局变量
全局变量的的修改:
在一个函数中对全局变量进行修改的时候,到底是否需要使用global进行说明,要看是否对全局变量的指向进行了修改
如果修改了指向,即全局变量指向了一个新的地方,那么必须使用global
如果仅仅是修改了指向空间中的数据,此时不用使用global
多线程共享全局变量会导致资源竞争问题
互斥锁
lock = threading.Lock() #创建锁
lock.acquire() # 上锁
lock.release() # 释放锁
GIL
GIL全局解释器锁.png