1. 字符串
1.1. 字符串格式化
字符串有三种格式化方式
,分别如下:
- 方式一:
%s 字符串占位,%d 占位整数,%f 占位小数
# 方式1
s = "我叫%s, 我住在%s, 我今年%d岁, 我喜欢%s" % (name, address, age, hobby)
s0 = "我叫%s" % name
print(s0)
- 方式二:
s1 = "我叫{}, 我住在{}, 我今年{}岁, 我喜欢{}".format(name, address, age, hobby)
print(s1)
- 方式三:
s2 = f"我叫{name},我叫{name},我叫{name},我叫{name},我今年{age}岁,我叫{name},我叫{name}" # 新的格式化方案:f-string
print(s2)
1.2. 字符串索引和切片
注意:同列表切片
-
基本概念
(1)索引:按照位置提取元素
(2)切片:从一个字符串中提取一部分内容 -
语法
(1)s[start:end] 从start到end进行切片. 但是取不到end [start, end)
(2)s[start:end:step] 从start切到end, 每step个元素出来一个元素
(3)通过步长的符号可以控制切片的方向,切片步长默认为1 -
实例
s = "我叫周杰伦,你呢? 你叫周润发吗?"
print(s[3:6]) # 从索引3位置进行切片, 切到6结束, 坑: 切片拿不到第二个位置的元素
#语法: s[start:end] 从start到end进行切片. 但是取不到end [start, end)
print(s[:5]) # 如果start是从开头进行切片, 可以省略
print(s[6:]) # 从start开始一直截取到末尾
print(s[:]) # : 如果左右两端有空白. 表示开头或者结尾
print(s[-3:-1]) # 目前还是只能从左往右切片
print(s[-1:-3]) # 没结果, 这里是坑!!!!
s = "我爱你"
#可以给切片添加步长来控制切片的方向
print(s[::-1]) # 负号表示从右往左, 前面讲的步长都是1 ,省略了。
#语法: s[start:end:step] 从start切到end, 每step个元素出来一个元素
s = "abcdefghijklmnopqrst"
print(s[2:11:3])
print(s[-1:-10:-3]) # 每三个为一组,步长正号取左边的,步长为负号取右边的。
2.set集合
2.1. 增删改查
Set集合:Set集合是无序的。
(1)set集合要求存储的数据必须是可以进行哈希计算的,根据计算出来的哈希值进行数据存储。
(2)不可哈希类型:list, dict ,因此set集合中不能出现这两种类型的元素。
# 创建set集合
s = set()
s = {1, 2, 3, "呵呵", (1, 2, 3)}
# 添加元素
s.add("zhangsan")
print(s)
# 删除元素
s.pop() # 随机删除一个元素
s.remove(2) # 删除指定数值的元素
print(s)
# 遍历
for item in s:
print(item)
2.2. 交集、并集、差集
# 交集, 并集, 差集
s1 = {"刘能", "赵四", "皮长山"}
s2 = {"刘科长", "冯乡长", "皮长山"}
print(s1 & s2) # 交集
print(s1.intersection(s2))
print(s1 | s2) # 并集
print(s1.union(s2))
print(s1 - s2) # 差集
print(s1.difference(s2))
set集合另一重要功能是去重
lst = ["周杰伦", "昆凌", "蔡依林", "侯佩岑", "周杰伦", "昆凌", "蔡依林", "侯佩岑", "周杰伦", "昆凌", "蔡依林", "侯佩岑"]
print(lst)
print(list(set(lst))) # 去除重复之后的数据是无序的.
3.字符编码
3.1. 字符编码发展史
1. ASCII编码(开天辟地)
(1) 在计算机中,所有的数据的存储和运算时都要使用二进制数表示,而具体用哪些二进制数字表示哪个符号,当然每个人都可以约定自己的一套(这就叫编码),而大家如果要想互相通信而不造成混乱,那么大家就必须使用相同的编码规则,于是美国有关的标准化组织就出台了ASCII编码(American Standard Code for Information Interchange,美国信息互换标准代码)
,统一规定了常用符号用哪些二进制数来表示。
(2 ) ASCII第一次以规范标准的类型发表是在1967年,最后一次更新则是在1986年。ASCII码,用一个字节表示,8位能够表示256个字符,可以用来表示所有的大小写字母,数字、特殊符号
,足够美国人用的了,事实上他们只用到了128个字符,还有1位就空着了。
ascii => 编排了128个文字符号(英文大小字母,数字,特殊符号). 只需要7个0和1就可以表示了. 01111111 => 1 byte => 8bit
这个ascii一直延续到今天 (预留的1位还能编码128个文字符号)
2. ANSI标准
其他国家逐渐普及计算机…,为了满足其他国家字符编码的需要,老美搞了一个ANSI标准:每个字符使用16bit编码(2byte) ,原来ASCII的位置不变,其它的位置让各个国家(或地区)自己去编码,于是:
ANSI => 一套标准, 每个字符 16bit, 2byte => 2^16 = 65536
00000000 01111111 : ascii 的位置不变,其它的位置让各个国家去自己编码
- 中国制定了
GB2312
编码,把中文编码进去 - 台湾指定了
big5
编码 - 日本制定了
Shift_JIS
编码 - 韩国制定了
Euc-kr
编码
(1) GB2312: 中国于1980年发布了GB2312,就是国标的拼音,这个编码用区位码(94个区,每区94个字符)的方式可以支持7000多个汉字,它所收录的汉字已经覆盖中国大陆99.75%的使用频率,基本可以满足汉字计算机的需要了。
(2) GBK1.0 : 7000多个汉字人类当然是不能满足,所以人们在GB2312的基础上开始扩展,所以1995年GBK1.0诞生了(国标扩展的拼音),可以支持2万多个汉字。
(3) GB18030: 2000年的时候GB18030来了,可以支持2万7千多个汉字。
存在问题:随着全球化的趋势,这套编码并不通用。
3. Unicode
Unicode编码也称万国码(满足各个国家编码的需要),但是它有一个缺点就是浪费存储空间比较大。
早期Unicode没有意识到这个问题. UCS-2标准 2个字节.
进行了扩充, UCS-4 4个字节
00000000 00000000 00000000 01111111 扩充成四个字节需要的存储空间变大了
Unicode(统一码、万国码、单一码)是一种在计算机上使用的字符编码。Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,规定虽有的字符和符号最少由 16 位来表示(2个字节),即:2 **16 = 65536(注:此处说的的是最少2个字节,可能更多)。
4. UTF编码
UTF是变长字符编码,是对Unicode编码的压缩和优化。
例如: UTF-8包含于Unicode,只是他不再使用最少使用1个字节,而是将所有的字符和符号进行分类:
- 英文: 8bit, 1byte
- 欧洲文字: 16bit, 2byte
- 中文: 24bit, 3byte
另外,
(1)UTF-16 最少使用2个字节。
(2) windows默认GBK, mac默认UTF-8。GBK和UTF-8不能直接转换。
(3)ASCII 一个字节,GBK两个字节, UTF-8变长字节。
3.2. 编码和解码
1. str.encode("编码") 进行编码
2. bytes.decode("编码") 进行解码
编码:
s = "周杰伦"
bs1 = s.encode("gbk") # b'xxxx' bytes类型
bs2 = s.encode("utf-8")
print(bs1) # b'\xd6\xdc\xbd\xdc\xc2\xd7' 一个\x是一个字节
print(bs2) # b'\xe5\x91\xa8\xe6\x9d\xb0\xe4\xbc\xa6'
如何把一个GBK字节转换UTF-8字节?
先把GBK字节转化为文字,再把文字转化为UTF-8字节。
bs = b'\xd6\xdc\xbd\xdc\xc2\xd7'
# 先变成文字符号(字符串)
s = bs.decode("gbk") # 解码
bs2 = s.encode("utf-8") # 重新编码
print(bs2) # b'\xe5\x91\xa8\xe6\x9d\xb0\xe4\xbc\xa6'
4. 文件读写
4.1. 文件基本操作
1. open
f = open('文件路径', mode='r', encoding='utf-8')
mode常用的模式:
- r:表示文件只能读取
- w:表示文件只能写入
- a:表示打开文件,在原有内容的基础上追加内容,在末尾写入
- w+:表示可以对文件进行读写双重操作
mode参数可以省略不填,默认为r模式mode参数还可以指定以什么样的编码方式读写文本,默认情况下open是以文本形式打开文件的,比如上面的四种mode模式。
当你需要以字节(二进制)形式读写文件时,只需要在mode参数中追加’b’即可:
- rb:以二进制格式打开一个文件,用于只读
- wb:以二进制格式打开一个文件,用于只写
- ab:以二进制格式打开一个文件,用于追加
- wb+:以二进制格式打开一个文件,用于读写
常用方法:
f.read() # 读取文件的全部内容
f.readlines() # 读取文件的全部内容,返回一个list列表
f.readline() # 一次读取一样,可以使用while循环判断结束
f.close() # 关闭, 是一个好的习惯。如果你没有显式地关闭文件,Python的垃圾回收器最终将销毁该对象并为你关闭打开的文件,但这个文件可能会保持打开状态一段时间。
注意:
(1)当你在默认模式下读取文本文件时(二进制文件不可以),文件中的换行符会转换为’\n’形式。
(2)也就是说,你读取的txt文本,其中换行符会以’\n’形式出现,写入txt文本时,文本中的’\n’会变成换行指令。
2.使用关键字with
好处:不需要手动去关闭一个文件
with open('文件路径', mode='r', encoding='utf-8') as f:
lines = f.readlines()
print(lines)
5. 函数
5.1. 函数的嵌套
- 函数内部可以嵌套还能输
- 函数可以作为返回值进行返回
- 函数可以作为参数进行互相传递
- 函数名实际上就是一个变量名,指向一个内存地址
- 函数的嵌套
def func1():
b = 20
def func2(): # 函数的嵌套, 局部变量
print("执行了func2")
# func2 = def():
func2() # 局部的东西. 一般都是在局部自己访问使用的
func1()
- 函数作为返回值
def func1():
b = 20
def func2(): # 函数的嵌套, 局部变量
print("执行了func2")
print(func2) # 打印函数
return func2
fn_a = func1()
print(fn_a) # 打印函数
fn_a() # 调用函数
# 输出结果
<function func1.<locals>.func2 at 0x000001F5FDE291F8>
<function func1.<locals>.func2 at 0x000001F5FDE291F8>
执行了func2
- 函数作为参数传递
# 代理模式
def func(an): # 此时an收到的是一个函数
print(an)
an() # 执行这个函数
def target():
print("我是target")
print(target)
func(target) # 实参可以是函数
# 输出结果:
<function target at 0x00000285CFF391F8>
<function target at 0x00000285CFF391F8>
我是target
5.2. 两个关键字: global 和 nonlocal
- global : 在局部. 引入全局变量
- nonlocal: 在局部, 引入外层的局部变量
- global用法
a = 10
def func():
# print(a)
# 此时我就想在函数内部修改全局的变量a
global a # 把外面的全局变量引入到局部
a = 20 # 创建一个局部变量. 并没有去改变全局变量中的a
func()
print(a)
# 输出
20
- nonlocal用法
def func():
a = 10
def func2():
nonlocal a # 向外找一层. 看看有没有该变量. 如果有就引入, 如果没有, 继续向外一层, 直到全局(不包括)
a = 20
func2()
print(a)
func()
# 输出
20
全局变量能够在函数里面发生更改,整个程序流程可能发生翻天覆地的变化,说明全局变量变得不安全了。
5.3. 闭包函数
闭包: 本质, 内层函数对外层函数的局部变量的使用. 此时内层函数被称为闭包函数
- 可以让一个变量常驻与内存
- 可以避免全局变量被修改(使用局部变量达到和全局变量相同的效果):只能执行内层函数才能修改变量。
def func():
a = 10
def inner():
nonlocal a
a += 1
return a
return inner
ret = func()
# inner => ret => 什么时候执行
r1 = ret()
print(r1)
r2 = ret()
print(r2)
# 输出
11
12
5.4. 装饰器
装饰器:
装饰器本质上是一个闭包
作用:
在不改变原有函数调用的情况下. 给函数增加新的功能.
直白: 可以在函数前后添加新功能, 但是不改原来的代码
- 基本雏形
def wrapper(fn): # wrapper: 装饰器, fn: 目标函数
def inner():
# 在目标函数执行之前...
fn()
# 在目标函数执行之后...
return inner # 千万别加()
实例:
def guanjia(game): # game也是一个局部变量
def innfer():
print("打开外挂...")
game()
print("关闭外挂...")
return innfer
@guanjia # 等价于 play_dnf = guanjia(play_dnf)
def play_dnf():
print("你好我是赛利亚,今天又是美好的一天")
@guanjia
def play_lol():
print("你好我是德玛西亚,今天又是美好的一天")
play_dnf()
print("=================================")
play_lol()
# 输出
打开外挂...
你好我是赛利亚,今天又是美好的一天
关闭外挂...
=================================
打开外挂...
你好我是德玛西亚,今天又是美好的一天
关闭外挂...
- 装饰器的参数问题
def wrapper(fn): # wrapper: 装饰器, fn: 目标函数
def inner(*args, **kwargs): # *, **表示接收所有参数, 打包成元组和字典
# 在目标函数执行之前...
fn(*args, **kwargs) # *, ** 表示把args元组和kwargs字典打散成 位置参数以及关键字参数传递进去
# 在目标函数执行之后...
return inner # 千万别加()
- 通用装饰器写法
添加了返回值
通用装饰器的写法:
def wrapper(fn): wrapper: 装饰器, fn: 目标函数
def inner(*args, **kwargs):
# 在目标函数执行之前.....
ret = fn(*args, **kwargs) # 这里是目标函数的执行, 这里是能够拿到从目标函数返回的返回值的.
# 在目标函数执行之后.....
return ret
return inner 千万别加()
@wrapper
def target():
pass
# 调用
target() # =>inner
-
一个函数被多个装饰器修饰
一个函数可以被多个装饰器装饰.
@wrapper1
@wrapper2
def target():
print(‘我是目标’)规则和规律: wrapper1 wrapper2 TARGET wrapper2 wrapper1
实例:
login_flag = False
def login_verify(fn):
def inner(*args, **kwargs):
global login_flag
if not login_flag: # ????
# 这里完成登录校验
print('还未完成用户登录操作')
while 1:
username = input(">>>")
password = input(">>>")
if username == "admin" and password == "123":
print("登录成功")
login_flag = True
break
else:
print("登录失败, 用户名或密码错误")
ret = fn(*args, **kwargs) # 后续程序的执行
return ret
return inner
@login_verify
def add():
print("添加员工信息")
@login_verify
def delete():
print("删除信息")
@login_verify
def upd():
print("修改信息")
@login_verify
def search():
print("查询员工信息")
add()
upd()
delete()
search()
# 输出
还未完成用户登录操作
>>>admin
>>>123
登录成功
添加员工信息
修改信息
删除信息
查询员工信息
5.5. 匿名函数
匿名函数:
lambda表达式
语法:
变量 = lambda 参数,参数2,参数3…: 返回值
fn = lambda a, b: a + b
ret = fn(12, 13)
print(ret)
5.6. 内置函数
- zip: 可以把多个可迭代内容进行合并
lst1 = ["赵本山", "范伟", '苏有朋']
lst2 = [40, 38, 42]
lst3 = ["卖拐", "耳朵大有福", "情深深雨蒙蒙"]
result = []
for i in range(len(lst1)):
first = lst1[i]
second = lst2[i]
third = lst3[i]
result.append((first, second, third))
print(result)
result = zip(lst1, lst2, lst3)
# for item in result:
# print(item)
lst = list(result)
print(lst)
# 输出
[('赵本山', 40, '卖拐'), ('范伟', 38, '耳朵大有福'), ('苏有朋', 42, '情深深雨蒙蒙')]
[('赵本山', 40, '卖拐'), ('范伟', 38, '耳朵大有福'), ('苏有朋', 42, '情深深雨蒙蒙')]
- sorted: 排序
sorted(lst, key=排序函数, reverse=True)
- sorted接受三个参数:可迭代对象,排序函数,是否降序
- 列表中的每一项传递给排序函数,sorted会根据排序函数的返回值进行排序
lst = ["秋", "张二嘎", "比克", "卡卡罗特", "超级宇宙无敌大帅B"]
# 按照名字的长度排序
s = sorted(lst, key=lambda x: len(x))
print(s)
lst = [
{"id": 1, "name": "周润发", "age": 18, "salary": 5200},
{"id": 2, "name": "周星驰", "age": 28, "salary": 511100},
{"id": 3, "name": "周海媚", "age": 78, "salary": 561230},
{"id": 4, "name": "周伯通", "age": 12, "salary": 532100},
{"id": 5, "name": "周大兴", "age": 35, "salary": 53210},
{"id": 6, "name": "周周有辣", "age": 47, "salary": 520},
{"id": 7, "name": "周扒皮", "age": 8, "salary": 12},
]
# 1.根据每个人的年龄排序
s = sorted(lst, key=lambda d: d['age'])
print(s)
# 2.根据工资进行排序. 从大到小
s = sorted(lst, key=lambda d: d['salary'], reverse=True)
print(s)
- filter: 筛选
filter(func, lst)
- filter接受两个参数,第一个参数是函数,第二参数是可迭代对象
- 保留函数返回值为True的元素
lst = ["张无忌", "张三丰", "张翠山", "灭绝小师太", "小狐仙"]
f = filter(lambda x: x.startswith("张"), lst)
print(list(f))
- map: 映射
lst = [1,2,3,4,5,6,7,8,9]
result = [item * item for item in lst]
print(result)
- locals 和globals
- locals被写在了全局作用域范围内. 此时看到的就是全局作用域中的内容
- locals放在局部作用域范围, 看到的就是局部作用域的内容
- globals无法放在全局作用范围内还是局部作用范围内看到的是全局作用域中的内容
a = 188
print(locals()) # 此时locals被写在了全局作用域范围内. 此时看到的就是全局作用域中的内容
def func():
a = 336
print(locals()) # 此时locals放在局部作用域范围, 看到的就是局部作用域的内容
func()
c = 12
print(globals()) # globals看到的是全局作用域中的内容
def func():
a = 336
print(globals()) # globals看到的是全局作用域中的内容
func()
6.高级特性
6.1. 迭代器
for 变量 in 可迭代:
pass
iterable: 可迭代的东西
iterator: 迭代器
str, list, tuple, dict, set, open()
可迭代的数据类型都会提供一个叫迭代器的东西. 这个迭代器可以帮我们把数据类型中的所有数据逐一的拿到
- 获取迭代器有两种方式
- 从迭代中拿数据有两种方式
# 1.获取迭代器
# it = iter("你叫什么名字啊")
it = "你叫什么名字啊".__iter__()
# 2.从迭代器中拿数据
print(next(it))
print(next(it))
print(it.__next__())
print(it.__next__())
模拟for循环
s = "我是数据"
it = s.__iter__() # 拿到迭代器
while 1:
try:
data = it.__next__()
print(data) # for循环的循环体
except StopIteration:
break
6.2. 生成器和生成器表达式
生成器(generator):
生成器的本质就是迭代器 (可以像使用迭代器那样使用生成器)
创建生成器两种方式:
- 生成器函数
- 生成器表达式
-
生成器函数
生成器函数中有一个关键字yield
生成器函数执行的时候, 并不会执行函数, 得到的是生成器.yield: 只要函数中出现了yield. 它就是一个生成器函数 作用: (1) 可以返回数据 (2)可以分段的执行函数中的内容, 通过__next__()可以执行到下一个yield位置 优势: 用好了, 特别的节省内存
def order():
lst = []
for i in range(10000):
lst.append(f"衣服{i}")
if len(lst) == 5:
yield lst
# 下一次拿数据
lst.clear()
gen = order()
print(gen)
print(gen.__next__())
print(gen.__next__())
# 输出
<generator object order at 0x0000020060E45B48>
['衣服0', '衣服1', '衣服2', '衣服3', '衣服4']
['衣服5', '衣服6', '衣服7', '衣服8', '衣服9']
- 生成器表达式
生成器表达式与列表推导式的区别是: 列表推导式使用的是中括号,生成器表达式使用的小括号。
(数据 for循环 if判断)
-> 不是元组推导式, 根本就没有元组推导式. 这玩意叫生成器表达式
# 生成器
gen = (i**2 for i in range(10))
print(gen)
# 遍历生成器
# for item in gen:
# print(item)
# 生成器转list
lst = list(gen) # list里面是有for循环的,可以把每一项拿出来组成一个列表
print(lst)
# 输出
<generator object <genexpr> at 0x00000171BD415BC8>
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
6.3. 推导式
- 语法:
- 列表推导式: [数据 for循环 if判断]
- 集合推导式: {数据 for循环 if判断}
- 字典推导式: {k:v for循环 if判断}
注意:
不要把推导式妖魔化.
(数据 for循环 if判断) -> 不是元组推导式, 根本就没有元组推导式. 这玩意叫生成器表达式
# 列表推导式
lst = [i for i in range(10) if i % 2 == 1]
print(lst)
# 字典推导式: 请将下列的列表修改成字典, 要求 索引做为key, 数据作为value
lst = ['赵本山', "潘长江", "高达", "奥特曼"]
dic = {i: lst[i] for i in range(len(lst))}
print(dic)
# 输出
[1, 3, 5, 7, 9]
{0: '赵本山', 1: '潘长江', 2: '高达', 3: '奥特曼'}