一、字符串
1.1 表示
字符串是0个或多个字符的集合,可记为:s=a1a2a3...an,一般用单引号或者双引号包围起来。
s1 = '今天'
s2 = "开始"
#三引号包围的字符串可以换行
s3 = """
我要
自己上厕所
"""
print(s1,s2,s3)
1.2 编码
字符串主要有两种类型:str和byte
str | Unicode字符 | 在内存中,常以Unicode字符表示 |
byte | 二进制数据(包括编码的文本) | 在磁盘上或在网络传输时,需要把str转换为byte,就需要编码 |
常见字符串编码标准:
ASCII | 最早的字符串编码 | |
UTF-8 | 国际通用 | 英文1字符,中文3字符 |
GBK,GB2312 | 中文编码标准 | 英文1字符,中文2字符 |
编码:字符串转化为字节(byte)
# 使用str.encode([encoding='utf-8'],[errors='strict'])编码
str1 = '12345'
# 编码默认为utf-8
print(str1.encode('utf-8'))
print(str1.encode()) # 结果一样
解码:字节转化为字符串
# 使用byte.decode([encoding='utf-8'][,errors='strict'])解码
str1 = '12345'
# 编码
byte = str1.encode('utf-8')
# 解码
print(byte.decode('utf-8')) #'12345'
1.3 转义
字符串中,可以用反斜杠“\”对字符进行转义
转义后的字符 | 含义 |
\n | 换行 |
\t | 制表(横向跳格) |
\' | 字符' |
\" | 字符" |
\\ | 字符\ |
还可以用\跟八进制或十六进制数来表示字符,如:
\八进制数 | \141=a |
\十六进制数 | \x61=a |
\Unicode字符编码 | \5218=刘 |
和正则表达式一样,如果不想用反斜杠\转义,在字符串前加“r”,如:
# 字符串
s = r'\nhello world\n'
print(s) # \nhello world\n
# 正则
r = r'^\d+\n$' # 可以匹配到123\n,78\n
1.4 运算
常见的字符串运算符:
比较运算符:==,!=,>,<,>=,<=,
连接运算符:+,+=,*,
成员运算符:in,not in,
索引和切片:[i],[a:b:step]
演示:
str1 = '123'
str2 = '456'
print(str1 * 2)
# '123123'
print(str1 + str2)
#'123456'
str1 += str2
print(str1)
# '123456'
print('1' in str1)
# True
print('6' not in str1)
# False
print(str1[1])
# 2
# 切片:前含后不含
print(str1[0:3]) # 123
print(str1[1:5:2]) # 24
print(str1[-2:-1]) # 56
print(str1[-4:-1:2]) # 35
#比较
print(str1==str2) # False
print(str1!=str2) # True
print(str1>=str2) # False
print(str1<=str2) # True
print(str1>str2) # False
print(str1<str2) # True
1.5 处理
常见的格式:str.函数名(),练习:
str1 = 'abc123'
# 计算字符长度
print(len(str1)) # 6
# 计算字节长度(汉字GBK算2,utf-8算3)
print(len(str1.encode())) # 6
# 判断是否只包含数字
print(str1.isdigit()) # False
# 判断是否只包含字母
print(str1.isalpha()) # False
# 是否只包含字母或数字
print(str1.isalnum()) # True
# 获得字符串首字母大写的拷贝
print(str1.capitalize()) # Abc123
# 获得字符串全部单词首字母大写的拷贝
print(str1.title()) # Abc123
# 获得字符串全部大写的拷贝
print(str1.upper()) # ABC123
# 获得字符串全部小写的拷贝
print(str1.lower()) # abc123
# 查找子串:find/rfind
print(str1.find('a')) # 找到时,返回索引,0
print(str1.find('s')) # 找不到时,返回-1
# 查找子串:index/rindex
print(str1.index('a')) # 找到时,返回索引,0
# print(str1.index('s')) # 找不到会报错
# 所以最好跟if搭配使用
if 's' in str1:
print(str1.index('s'))
# 查找子串出现次数
print(str1.count('a',0,3)) # 1
# 检查是否以某字符开头
print(str1.startswith('a')) # True
# 检查是否以某字符结尾
print(str1.endswith('a')) # False
# 将字符串以指定宽度居中并在两侧填充指定字符
print(str1.center(50,'-'))
# 将字符串以指定宽度靠右对齐并在左侧填充指定字符
print(str1.rjust(50,'-'))
# 将字符串以指定宽度靠左对齐并在右侧填充指定字符
print(str1.ljust(50,'-'))
# 获取字符串修剪两侧空格后的拷贝strip/rstrip/lstrip
str2 = ' abc '
print(str2.strip()) # abc
# 字符串连接
print(str1+''+str2) # abc123 abc
# 连接字符串列表
list1 = ['abcd','123','456']
str3 = '-'.join(list1)
print(str3) # abcd123456
# 字符串替换
print(str1.replace('ab','cc')) # ccc123
print(str1.replace('a','c',1)) # cbc123 #1 只替换一次,字符串第一次出现该字符时替换
print(str1.replace('a',' ').replace('c','b')) # 链式替换 bb123
# 字符串分割
print(str1.split('b')) # ['a','c123']
# 字符串截取
print(str1[1]) # b
print(str1[1:3]) # bc
print(str1[0:5:2]) # a c 2
1.6 格式化输出
有三种字符串格式化的方法,假设x=3,y=5:
方法 | 写法 | 结果 | 版本 |
% | print('a=%d,b=%d' % (x,y)) | a=3,b=5 | 传统方法 |
str.format | print('a={},b={}'.format(x,y)) | a=3,b=5 | 3.1版本后 |
f-string | print(f'a={x},b={y}') | a=3,b=5 | 3.6版本后 |
二、列表、元组、集合、字典
2.1 比较
列表 | 元组 | 字典 | 集合 | |
英文 | list | tuple | dictionary | set |
格式 | list=['',''] | tuple=('',) | dict={'':'','':''} | set={'',''} |
特性 | 值可变 | 值不可变 | key唯一不可变,value可变 | 值唯一,可计算 |
创建 | a. list=[] b. list(range(8)) ,list(其他对象) | 同列表 | a. dict(zip (list1,list2)) b. dict(key1 =value1,key2 =value2) c. dict={list1:list2} | a. set={} b. set(可迭代对象),如range对象,列表,元组,创建后值去重 |
访问 | print(list[2]) print(list[:2]) | 同列表 | a.print(dict[key]) b.print(dict. get(key)) | - |
遍历 | a. for i in list: print(i) b. for i in range(len(list)): print(list[i]) c. for index,i in enumerate(list): print(index,i) | 同列表 | a. 获取键值对 for i in dict.items(): print(i) b. 获取键值 for key,value in dict.items(): print(key,value) | - |
修改 | list[2]='abc' # 重新给指定index赋值 | tuple=('a',) tuple=('b',) # 重新给元组赋值 | dict[old_key] =new_value # 重新给指定key赋值 | - |
添加 | a.list.append(i) b. list1.extend( list2) | tuple1= tuple1+tuple2 # 在元组末尾加上新元组 | dict[new_key] =new_value | set.add('1') |
删除 | a. del list[-1] b. if i in list: list.remove(i) | - | a. del if key in dict: del dict[key] | a. del b. remove() c. pop() d. clear() |
排序 | a. 默认升序list.sort(key=len ,reverse=True) b. 生成新list sorted(list,key=, reverse=False) | - | - | - |
计算 | sum(list) # 统计数值列表元素和 min(list)/max(list) # 求列表中最值 list.count(i) # 统计i出现次数 list.index(i) # 求i的下标 | - | - | set1 & set2交集 set1 | set2 并集 set1 - set2 差集 set1 ^set2 对称差集 |
推导式 | ① list=[i ** 2 for i in range(10)] ② list=[i ** 2 for i in list2] ③ list=[i ** 2 for i in list if i > 0] ④ 使用生成器对象: a=(i ** 2 for i in range(10)) print(list(a)) # 可以转化为列表,也可以转化为元组,比①②③占用内存小 | ① 使用生成器对象: a=(i ** 2 for i in range(10)) print(tuple(a)) # 遍历生成器对象: a. for i in a: print(i) b. print( a.__next__()) | ① dict={i:j for i,j in zip(list,list1)} # 列表生成字典 ② dict={i:j for i in range(9) for j in range(4)} ③ dict={i,j for i,j in dict1 if j >0} #字典生成字典 | - |
补充:yield关键字定义生成器
使用yield
定义的函数被称为生成器函数。当调用生成器函数时,它不会立即执行,而是返回一个迭代器。每次从这个迭代器请求一个值时(通常是通过next()
函数或for...in
循环),生成器函数会执行到下一个yield
语句,并产出该语句的值。当函数执行完毕或遇到return
语句时,生成器结束。(引用自“百度AI会话”)
def f():
for i in range(1,10):
# 函数变为生成器函数
yield i
# 创建一个生成器对象
g = f()
print(g)
# 从迭代器请求值
for i in g:
print(i) # 1 2 3 4 5 6 7 8 9
# 或使用next()函数,每次可以请求一个值
print(g.__next__()) # 1
2.2 转化
a = [1,2,3]
b = [4,5,6]
# list-tuple 相互转化
a1 = tuple(a)
b1 = tuple(b)
# list/tuple-dict 列表元组可以转化为字典
c = dict(zip(a,b))
d = dict(zip(a1,b1))
# list/tuple-set 列表元组可以转化为集合
e = set(a)
f = set(a1)
2.3 二维列表
2.3.1 创建
a. 直接定义
list = [[1,2,3],[4,5,6],[7,8,9]]
b. for循环
list = []
for i in range(4):
list.append([]) # 在list中生成i个空列表
for j in range(5):
list[i].append(j) # 在子列表i中加入元素j
# [[0,1,2,3,4],[0,1,2,3,4],[0,1,2,3,4],[0,1,2,3,4]]
c. 列表推导式
list = [(j for j in range(5)) for i in range(4)]
# [[0,1,2,3,4],[0,1,2,3,4],[0,1,2,3,4],[0,1,2,3,4]]
2.3.2 访问
# list[i][j],第i行第j个,i,j表示下标,从0开始
print(list[1][3]) # 第2行第4个
# 2
三、练习
3.1 在屏幕上显示跑马灯文字
import os
import time
def light():
content = '生而无畏,战至终章'
while True:
# 先清屏
os.system('cls') #Unix和Linux系统用clear
print(content)
# 休眠200毫秒
time.sleep(0.2)
# 每行提前1字符的内容,形成跑马灯效果
content = content[1:] + content[0]
if __name__ == '__main__':
light()
3.2 设计一个函数产生指定长度的验证码,验证码由大小写字母和数字构成
import random
def yzm(length=4):
"""
生成指定长度的验证码,不指定默认为4
return: 验证码
"""
# 定义一个空字符串
yz = ''
# 验证码包含大小写字母和数字
x = [chr(i) for i in range(48,58)] # chr(48)-chr(57)是数字0-9的ASCII编码
y = [chr(i) for i in range(65,91)] # chr(65)-chr(90)是数字A-Z的ASCII编码
z = [chr(i) for i in range(97,123)] # chr(97)-chr(122)是数字a-z的ASCII编码
# a是包含A-Za-z0-9的列表
a = x + y + z
# 循环4次,从列表中随机取数
for i in range(length):
index = random.randint(0,len(a)-1) #随机获取一个索引
yz += a[index]
# 删除列表中已经取过的数,确保验证码不同位数不重复
# a.pop(index)
return yz
if __name__ == '__main__':
print(yzm())
补充:random库常见函数
import random
random.random():
# 返回[0.0, 1.0)之间的随机浮点数。
random.randint(a, b):
# 返回在[a, b]范围内的随机整数,其中a <= b。
random.randrange(start, stop, step):
# 返回在[start, stop)范围内的随机整数,步长为step。
random.choice(seq):
# 从非空序列seq中随机选择一个元素。
random.sample(population, k):
# 从population序列中随机选择k个不重复的元素,以列表形式返回。
3.3 设计一个函数返回给定文件名的后缀名
def hz(filename,has_dot=False):
"""
返回指定文件名的后缀名
filename:文件名
has_dot:返回的后缀名是否需要带点
return:后缀名
"""
if '.' in filename:
# 如果文件有后缀名,拆分文件名
filelist = filename.split('.')
# 截取文件名拆分后的列表的最后一位
print(filelist[-1] if not has_dot else '.'+filelist[-1])
else:
print('')
if __name__ == '__main__':
hz('1.txt')
# 方法2
def hz(filename,has_dot=False):
"""
返回指定文件名的后缀名
filename:文件名
has_dot:返回的后缀名是否需要带点
return:后缀名
"""
# 从右侧开始查找filename第一次出现.的位置,默认.右边的部分就是后缀名
site = filename.rfind('.')
# 如果查找结果不等于-1,说明.存在,截取.之后的部分
if site in range(0,len(filename)-1):
index = site if has_dot else site+1 #自右计算的索引,加1默认左移一位
print(filename[index:])
else:
print('')
if __name__ == '__main__':
hz('1.txt')
3.4 设计一个函数返回传入的列表中最大和第二大的元素的值
def top2(list1):
"""
获取传入列表中的最大和第二大元素
:param list1:输入list内容,如:[1,2,3,4,5,6]
:return: list中最大和第二大的元素
"""
for i in range(2):
a = max(list1)
print(a) # 获取当前列表的最大值
list1.remove(a) # 删除本次获取的最大值
if __name__ == '__main__':
list1 = [1,2,3,4,5,6]
top2(list1)
方法2:
def top2(l):
m1,m2 = (l[0],l[1]) if l[0]>l[1] else (l[1],l[0])
for i in range(2,len(l)):
if l[i]>m1:
m2 = m1
m1 = l[i]
elif m1>l[i]>m2:
m2 = l[i]
return m1,m2
if __name__ == '__main__':
print(top2([1,2,3,4,5,6,7,8])) # tuple
3.5 计算指定的年月日是这一年的第几天
def is_leap_year(year):
"""
函数用于判断某年是不是闰年,如果年份除以4可以整除但除以100不能整除就是闰年,或者年份可以整除400也是闰年,
:param year: 要判断的年份
:return: 如果是闰年,返回1
"""
if (year % 4 ==0 and year % 100 != 0) or year % 400 == 0:
return 1
def which_day(year,month,day):
"""
函数用于计算某个具体年月日是当年的第几天
:param year: 年
:param month: 月
:param day: 日
:return: 当年的第几天
"""
big = [31,28,31,30,31,30,31,31,30,31,30,31] # 大月
# 闰年大于2月加1,闰年1月,2月正常按day算入,不受影响
num = (sum(big[0:month])+1) if (is_leap_year(year) == 1 and month >2) else sum(big[0:month])
# 当月月份的天数多算了,比如一月,之前算了big[0:0],已经算了31天,需要把多算的减去
total = num-(big[month-1] - day)
return total
if __name__ == '__main__':
print(which_day(2020, 2, 29)) # 60
print(which_day(2023, 4, 28)) # 118
print(which_day(2020, 1, 28)) # 28
print(which_day(2020, 4, 28)) # 119
# upgrade_vision
def is_leap_year(year):
"""
函数用于判断某年是不是闰年,如果年份除以4可以整除但除以100不能整除就是闰年,或者年份可以整除400也是闰年,
:param year: 要判断的年份
:return: 如果是闰年,返回1
"""
return (year % 4 ==0 and year % 100 != 0) or year % 400 == 0 #返回一个等式是否成立的布尔值,True or False
def which_day(year,month,day):
"""
函数用于计算某个具体年月日是当年的第几天
:param year: 年
:param month: 月
:param day: 日
:return: 当年的第几天
"""
days = [[31,28,31,30,31,30,31,31,30,31,30,31],
[31,29,31,30,31,30,31,31,30,31,30,31]][is_leap_year(year)]
# is_leap_year(year)是索引,返回True即调用index=1的子序列,2月=29
total = 0
for i in range(month-1):
total += days[i]
return total + day
if __name__ == '__main__':
print(which_day(2020,2,29)) # 60
print(which_day(2023,4,28)) # 118
print(which_day(2020,1,28)) # 28
print(which_day(2020,4,28)) # 119
3.6 打印杨辉三角
# vision 1.1
def main():
row_number = int(input('please input row_number'))
num = []
for i in range(row_number):
num.append([])
# 每行第一个数字必是1
num[i].append(1)
# 从第二行开始
if i >=1 :
# i=1,range(2),j=1,j是第i行的索引,但i-1行不一定存在,所以不存在我们就作为0
# 为什么从1开始是因为j=0开始j-1就等于-1了,会额外多加上上一序列的最后一个值
for j in range(1,len(num[i-1])+1):
try:
a = num[i-1][j]
except IndexError: #如果i-1列表中,索引j不存在,直接默认=0
a = 0
try:
b = num[i-1][j-1]
except IndexError: #如果i-1列表中,索引j不存在,直接默认=0
b = 0
num[i].append(a + b)
# 循环列表,输出元素
for i in range(row_number):
for j in range(len(num[i])):
print(num[i][j],end='\t')
print()
if __name__ == '__main__':
main()
# vision 1.2
def main():
num = int(input('请输入行数'))
main = [[]] * num
for i in range(num):
# 用None先占位,灵活运用列表乘法
main[i] = [None] * (i+1)
for j in range(len(main[i])):
if j == 1 or j == len(main[i])-1:
# 每行第一个和最后一个数字都等于1
main[i][j] = 1
else:
main[i][j] = main[i-1][j] + main[i-1][j-1]
print(main[i][j],end='\t')
print()
if __name__ == '__main__':
main()
# vision 1.3
def main():
num = int(input('请输入行数: '))
# 使用列表推导式来正确创建 main 列表
main = [[0] * (i + 1) for i in range(num)]
for i in range(num):
for j in range(i + 1):
if j == 0 or j == i:
# 每行第一个和最后一个数字都等于1
main[i][j] = 1
else:
# 其他位置是上一行相邻两个数字的和
main[i][j] = main[i - 1][j - 1] + main[i - 1][j]
# 打印当前行的所有数字
print('\t'.join(map(str, main[i])))
# 调用 main 函数
main()
3.7 设计一个函数返回斐波拉切数列前20个值
def qblq(i):
a,b = 0,1
for i in range(i):
a,b = b,a+b # 递归 b=a+b,b上轮的b,a是上上轮的b
yield a
def main():
for i in qblq(20):
print(i,end=' ')
if __name__ == '__main__':
main()
# 递归常见案例:阶乘 n! = n * (n-1)!,当 n > 0,0! = 1
def factorial(n):
if n == 0: # 基本情况
return 1
else: # 递归情况
return n * factorial(n-1)
print(factorial(4))
四、综合练习
4.1 双色球选号
4.2 约瑟夫环问题
"""
《幸运的基督徒》
有15个基督徒和15个非基督徒在海上遇险,为了能让一部分人活下来不得不将其中15个人
扔到海里面去,有个人想了个办法就是大家围成一个圈,由某个人开始从1报数,报到9的
人就扔到海里面,他后面的人接着从1开始报数,报到9的人继续扔到海里面,直到扔掉15
个人。由于上帝的保佑,15个基督徒都幸免于难,问这些人最开始是怎么站的,哪些位置
是基督徒哪些位置是非基督徒。
"""