1. Python速览
官方文档
一定要注意使用的是哪个版本 有些效果会不同
1.1 安装教程
- 官网安装
- 使用IDLE自带软件编辑
2. 基础内容
2.1 用作计算器
通过python可以实现大部分的计算器功能。我们通过IDLE进行编译。
2.1.1 数字
运算符+
、-
、*
、/
的用法,同时也支持括号 (()
) 用来分组。
2+2
2*2
(50-5*6)/4
除法运算/
返回浮点数。用//
运算符返回整数;计算求余用%
;
混合类型运算数的运算会把整数转换为浮点数
17 / 3 # 除法运算
5.666666666666667
17 // 3 # 获得整数
5
17 % 3 # 求余
2
4 * 3.75 - 1 # 混合运算
14.0
用**
进行乘方
5 ** 2 # 5的平方
25
等号(=
)用于给变量赋值,变量未定义(即,未赋值),使用该变量会提示错误
赋值变量符也是最低级的运算符
width = 20
height = 5 * 9
width * height
900
交互模式下,上次输出的表达式会赋给变量 _
意味着上次计算的结果下次可以直接通过 _
调用,不用再赋值。
2.1.2 字符串
Python中的字符串不能被修改! immutable
用单引号('……'
)或双引号("……"
)标注的结果相同,反斜杠 \
用于转义
可以通过将字符串赋值给特殊字符,通过print()函数输出
'"Yes," they said'
'"Yes," they said'
s=_ # 使用了 _ 进行直接调用最近的输出结果
print(s)
"Yes," they said
将r
添加在在引号前,原始输出字符 也叫原符号
print('C:hello\nhaha')
C:hello
haha
print(r'C:hello\nhaha')
C:hello\nhaha # \n后面的字符正常输出了,不换行
一次性多行输出,python也可以实现。 一种实现方式是使用三重引号:"""..."""
或 '''...'''
添加一个\
可以减去print自带换行 只能加在前面
print('''\
多行输输出内容
输出内容和引号内的格式一模一样 可以不断输出
''')
print("""\
这是三对双引号输出内容
""")
字符串可以视为字符组 从左往右索引是0开始,从右开始是负数
还可以获得子字符串
word='hello'
print(word[0])
print(word[-1])
print(word[0:2])
print(word[-3:-1])
print(word[-1:-3]) #无法遍历
print(word[0:]) #遍历到最后
注意:从0-->2 遍历hello时不会输出word[2] 应该是输出了 word[0] word[1]
同时负数的遍历也是从小到大的,写反是没有用的
分支结构
单分支结构
money=1000
s=int(input('请输入取款金额:'))
if money >= s: #双引号
money-=s
print('取款成功,余额为:',money) #if条件成立才执行缩进代码
双分支结构
a=int(input('请输入一个整数:'))
if a%2==0:
print(a,'\b是整数')
else : #这里即使没有判断语句也要加 :
print(a,'\b是奇数')
多分支结构
a=float(input('考试成绩:'))
if a>=90:
print('sss')
elif a>=70:
print("aaa")
elif a>=60:
print('a')
else:
print('c')
可以有零个或多个 elif
部分,以及一个可选的 else
部分。 关键字 'elif
' 是 'else if' 的缩写,适合用于避免过多的缩进。 一个 if
... elif
... elif
... 序列可以看作是其他语言中的 switch
或 case
语句的替代。
嵌套if使用
p=input('您是会员吗?y/n')
pay=float(input('输入您的消费金额:'))
if p=='y':
if pay>=200:
print('打八折,消费金额为:',pay*0.8)
elif pay>=100:
print('打九五折,消费金额为:',pay*0.95)
else:
print('不打折,消费金额为:',pay)
else:
if pay>=200:
print('打九五折,消费金额为:',pay*0.95)
else:
print('不打折,消费金额为:', pay)
条件表达式
print ( 条件为真结果 判断语句 条件为假 )
a=int(input('输入一个整数:'))
b=int(input('输入一个整数:'))
print( (str(a)+'大于等于'+str(b)) if a>=b else ( str(a)+'小于等于'+str(b)) )
Pass语句
用在还未想到怎么写的地方 执行pass 程序不报错
if a>=b:
pass
else:
pass
循环
内置函数range
-
用于生成一个整数序列
-
创建range有三种方式:
- range(stop) 创建一个[0,stop)的步长为1的序列,不包含stop
- range(start,stop) 创建一个[start,stop)步长为1的序列
- range(start,stop,step) 创建一个[start,stop)之间的序列 步长为step
-
如果要查看序列内整数 需要用到 list()函数
r=range(1,10) print(list(r)) # 1,2,3,4,5,6,7,8,9
-
只有在使用上range对象时,才会去计算range内的元素 不然无论序列多大 都只保存start stop step三个值
while循环
一般执行N+1次 因为最后一次是执行失败的
四步循环法
- 初始化变量
- 条件判断
- 条件执行体(循环体)
- 改变变量
# 要求从0累加到4 输出结果
sum,a=0,0 #初始化变量
while a<5: #条件判断
sum+=a #条件执行体(循环体)
a+=1
print('输出结果是:'+str(sum))
练习
1到100之间的偶数和
# sum=0 #累加总和
i=1 #初始化变量
sum=0
while i>0 and i<=100:
if i%2==0:
sum=sum+i
i+=1
print(sum)
for-in循环
-
in表达会从(字符串 序列等可迭代对象)中依次取值,也叫遍历
-
语法结构:
for 自定义的变量 in 可迭代的对象:
循环体
for item in range(1,4): print(item) n='word' for a in n: print(a)
-
如果循环体不需要访问自定义变量 自定义变量用下划线表示 那么for-in循环可以作为一种循环次数使用
for _ in range(4): print('人生苦短,我用Python')
练习
# 1到100之间的偶数和
sum=0
for item in range(1,101):
if item%2==0:
sum+=item
print(sum)
# 100到999的水仙花数
for item in range(100,1000):
a=item//100
b=(item%100)//10
c=item-a*100-b*10
if item==pow(a,3)+pow(b,3)+pow(c,3):
print(item)
流程控制语句break
用于结束循环结构,通常与分支结构If一起使用
#while循环
i=0
while i<3:
psw = str(input('输入密码:'))
if psw=='ffff':
print('密码正确!')
break
else:
print('密码不正确')
i+=1
# for-in循环
for _ in range(3):
psw=str(input('输入密码:'))
if psw=='yexin':
print('密码正确!')
break
else:
print('密码不正确')
流程控制语句continue
用于结束当前循环,进入下一次循环,通常与分支结构中的if一起使用
# 要求输出1到50之间5的倍数
for item in range(1,51):
if item%5!=0:
continue
else:
print(item)
i=1
while i<51:
if 5*i<=50:
print(i*5)
i+=1
else:
continue
else语句
三种搭配方法:
- if: else:
- while: else: 没有碰到break时执行else
- for : else: 没有碰到break时执行else
练习
#while循环
i=0
while i<3:
psw = str(input('输入密码:'))
if psw=='ffff':
print('密码正确!')
break
else:
print('密码不正确')
i+=1
else:
print('对不起三次错误,无法输入')
# 最后else是与while配对的 while三次结束 输出else内容 对for-in同理
# for-in循环
for _ in range(3):
psw=str(input('输入密码:'))
if psw=='yexin':
print('密码正确!')
break
else:
print('密码不正确')
else:
print('对不起三次错误,无法输入')
嵌套循环
for-in嵌套循环
'''
打印一个三行四列的矩形
'''
for _ in range(3):
for _ in range(4):
print('+', end='\t')
print()
# 打印一个直角三角形
# print(str(i)+'*'+str(j)+'='+str(j*i),end='\t') 可修改为打印乘法表
for j in range(1,5):
for i in range(1,5):
if i<=j:
print('*',end='\t')
print()
while嵌套循环
# 用while嵌套循环 打印一个三行四列的矩形
i,j=0,0
while i<3:
while j<4:
print('*',end='\t')
j+=1
j=0
i+=1
print()
# 打印一个直角三角形
i,j=1,1
while i<5:
while j<=i:
print('*',end='\t')
j+=1
i+=1
j=1
print()
二重循环中的break和continue
注意:二重循环中的break和continue只影响本层循环 外层循环不受影响
列表
列表相当于数组 LIST 存储的对象的引用 只有在真正使用时才会调用
列表的特点
- 列表元素按照顺序有序排列
- 索引映射唯一个数据
- 列表可以存储重复数据
- 任意数据类型混存
- 根据需要动态分配和回收内存
列表的创建与删除
-
使用中括号
lst=['hello','world',98]
-
调用内置函数list
lst2=list(['hello','world',98])
列表存储的是列表内每个元素的id,且列表对象是连续完整的,同样我们创建的lst变量存储的是列表对象的id。通过层层的查询才能找到元素的value
-
空列表的创建
lst=[] lst1=list()
列表的查询操作
-
按值查找
index 根据元素查找对应索引 按照顺序查找 查到便停止遍历 对重复元素不友好
支持区间查找 如果没有该元素会报错
lst=['hello','world',99,'hello']
print(lst.index('hello'))
print(lst.index('hello',1,4)) #[1,4)的位置查询元素hello的索引
-
根据索引获取元素 可以正向、逆向索引
正向是 0 到 N-1 ,逆向是从 -N 到 -1,索引不存在抛出indexError
lst=['hello','world',99,'hello'] print(lst[2]) print(lst[-3])
3.获取列表中的多个元素 切片 步长 list[start,stop,step] 获取的其实也是个新的列表 其id不同
lst=['hello','world',99,'hello']
print(lst[1:4:1])
lst2=lst[1:4:1]
print(lst2,id(lst2))
print(lst,id(lst))
当step是负数时,
-
[:stop:step] 切片的第一个元素默认为最后一个元素开始
-
[start::step] 切片的最后一个元素默认为第一个元素开始
print(lst[1::-1]) #从左第一个 向左边遍历 直到第零个 ['world', 'hello'] print(lst[:2:-1]) #从右边第一个开始 向右遍历 直到从左边数第二个为止 不包括第二个 ['hello']
-
判断元素是否存在 in not in
print( 'hello' in lst) #返回布尔类型 True False
-
遍历
for item in lst: print(item,end='\t')
列表元素的增加操作
- append() 在列表的末尾添加一个元素
- extend() 在列表的末尾至少添加一个元素
- insert() 在列表的任意位置插入一个元素
- 切片 在列表的任意位置添加至少一个元素 原列表的元素会右部分被替换
注意:一个元素可以是单元素也可以是一个列表 将列表也视为一个元素
lst=['hello','world',99,'hello']
lst2=lst[1:4:1]
lst.append(88)
print(lst) #['hello', 'world', 99, 'hello', 88] 88作为最后一个元素
lst.append(lst2) # ['hello', 'world', 99, 'hello', 88, ['world', 99, 'hello']] lst2整体作为最后一个元素进入
print(lst)
print(lst[5] ,type(lst[5])) # ['world', 99, 'hello'] <class 'list'> 类型还是list不变
lst.extend(lst2) # ['hello', 'world', 99, 'hello', 88, 'world', 99, 'hello'] lst2不再是一个整体了 作为lst的扩展连上了
lst.insert(1,'yexin') #在第一个位置上插入
print(lst)
lst[1:]=lst2 #将lst2在lst[1:)位置开始插入 同时原列表对应位置上的元素会被替换掉
print(lst)
列表元素的删除操作
-
remove() 一次删除一个元素 删除不存在的会报错 重复元素只删除的第一个
-
pop() 删除一个指定索引位置上的元素 如果不指定就是默认最后一个元素 删除不存在的元素会报错
-
切片 一次删除不少于一个元素
-
clear() 清除列表
-
del 直接删除列表
lst=['hello',88,'new','yes'] lst.remove('new') lst.pop() lst.pop(1) new_lst=lst[1:4] print(new_lst) lst.clear() #[] # 删除列表 del lst
列表的修改操作
列表是可变序列 支持修改 例如字符串就不是可变序列 不支持修改
- 为指定索引上的元素赋予一个新值
- 为指定的切片赋予一个新值
lst=[10,20,30,40]
lst[2]=70
print(lst)
lst[1:3]=[99,22,33,44] #修改了1到3(不包括3)的元素
print(lst)
列表元素的排序
- 调用sort()方法,默认对列表进行升序排序, sort(reverse=True) 进行降序 原列表发生变化,但不创建新列表
- 调用内置函数sorted(),同样sorted(reverse=True)进行降序,但原列表不发生变化,创建了新列表
lst=[10,20,55,40,12]
lst.sort()
print(lst) #[10, 12, 20, 40, 55] 升序也就是reverse=False
lst.sort(reverse=True)
print(lst) #[55, 40, 20, 12, 10]
print('-------调用内置函数sorted()---------')
lst2=[31,10.1,44,1]
new_lst=sorted(lst2)
print(new_lst)
new_lst=sorted(new_lst,reverse=True)
print(new_lst)
列表生成式
lst=[i*2 for i in range(1,6)]
print(lst) #[2, 4, 6, 8, 10]
字典
什么是字典
Python中常用的数据结构 具有一一对应的映射关系 也是可变序列 且无序的序列
d = {key1 : value1, key2 : value2 } 花括号 键和值之间用分号连接
注意:key必须是唯一的 用字符串来当 重复使用会替换前者的key value可以重复
字典的原理
哈希函数hash(key) 根据key来分配位置存储value 所以key不能重复 且不可变 因此适用字符串当key
查找也是根据key查找value所在位置
哈希的优点字典就有 缺点同样也是 浪费较大的空间
字典的创建
-
使用花括号 实参创建
dict1={'王五':13,'张三':77,'柳六':'缺考'}
-
内置函数dict()创建
score=dict(name='jack',age=19) print(score)
-
创建空字典
dict2={} dict3=dict() print(dict2)
字典元素的获取
- 根据key直接获取对应value
print(score['name'])
print(score['name1']) #报错KeyError: 'name1'
- 调用函数get()获取
print(score.get('name'))
print(score.get('name2')) #查找的键不存在 返回None
print(score.get('name2','没有')) #查找的键不存在 可以自己补充一个默认的值 没有
字典元素的删除
del score['name'] #根据key来删除对应的value 如果没有对应的key 报错KeyError
print(score)
字典的查询操作
-
判断key是否在字典中 key是字符串 必须要带引号
if 'name' in score: print(score['name']) b='name' in score print(b) #True print( 'name' not in score) # Flase
字典元素的增加
socre['jack']=99 #key必须唯一 不然会替换前者的value
获取字典视图
-
获取字典所有key 函数keys()
print(dict1.keys(),type(dict1.keys())) # dict_keys(['王五', '张三', '柳六']) <class 'dict_keys'> print(list(dict1.keys())) #['王五', '张三', '柳六'] keys可以转化为list输出
-
获取字典所有value 函数values()
print(dict1.values(),type(dict1.values())) # dict_values([90, 77, '缺考']) <class 'dict_values'> print(list(dict1.values())) # [90, 77, '缺考'] 同样转化为list输出
-
获取字典所有key value对 函数items()
list输出会成为元组
print(dict1.items(),type(dict1.items())) # # dict_items([('王五', 90), ('张三', 77), ('柳六', '缺考')]) <class 'dict_items'> print(list(dict1.items())) # [('王五', 90), ('张三', 77), ('柳六', '缺考')] 输出为元组 <class 'list'> # 小括号外面还有方括号的叫做元组
字典元素的遍历
for item in dict1:
print(item,dict1[item],end='\t')
# 获取对应key 然后进行遍历操作
字典生成式
-
采用内置函数zip() 将可迭代的对象作为参数 (列表)将对象中的元素打包并且输出成新列表
items=['Fruits','Books','Others'] prices=[99,12,31] lst=zip(items,prices) #zip()函数打包成元组 print((list(lst)))
-
字典生成器
item price是自定义变量 从zip()打包的元组中获取数据
d={ item.upper() : price for item , price in zip(items,prices) }
print(d,type(d)) #{'FRUITS': 99, 'BOOKS': 12, 'OTHERS': 31} <class 'dict'>
元组 tuple
什么是元组
t=('Python','hello',90) # 小括号 逗号 元素对象
是Python内置的数据结构之一,是不可变序列
- 不可变序列有:字符串、元组
- 没有增删改的操作 修改时一般是创建新的对象地址
- 可变序列:列表、字典
- 可以对序列执行增、删、改操作,同时对象地址不发生改变
元组的创建方式
-
直接小括号
t=('Python','hello',90) print(t)
-
使用内置函数tuple() 注意有两队小括号
t=tuple(('hello','Python',90))
-
只包含一个元组的元素需要使用逗号和小括号
t=('hello',) #这是个元组 t1=('hello') #这是个字符串 需要注意两者的区别 print(t,t1,type(t),type(t1)) #('hello',) hello <class 'tuple'> <class 'str'>
-
空元组的创建
t1=() t2=tuple() print(t1,t2)
为什么元组要设计为不可变序列?
- 在多任务环境下,同时操作对象时不需要加锁
- 在程序中尽量使用不可变序列
- 元组中存储的是对象的引用
- 如果元组中的对象本身是不可变对象,则不能再引用其他对象
- 如果元组中的对象是可变对象,则可变对象的引用不允许改变,但数据可以改变
t1=('hello','cao',12,[12,31])
print(t1,ype(t1[3])) # <class 'list'>
# t1[3]=100 无法对元组的元素进行修改 TypeError: 'tuple' object does not support item assignment
t1[3].append(100) #t[3]作为可变序列存在 因此可以对元组的可变对象进行修改
print(t1,id(t1[3])) # ('hello', 'cao', 12, [12, 31, 100])
元组的遍历
元组是可迭代对象,所以可以使用for...in进行遍历
因为会有超出元组元素个数的可能 所以使用for....in遍历最为方便
t=tuple(('Python','hello',90))
for item in t:
print(item)
集合
什么是集合
Python语言提供的内置数据结构
与列表,字典一样属于可变类型的序列
集合是没有value的字典
集合和字典一样根据hash()函数来构造的 所以存储时无序的 同时无法存储重复的元素
集合的创建
-
直接{}创建
-
s={'Python','hello',90}
-
-
使用内置函数set()
-
s=set(range(6)) # 整数序列转为集合 print(s) print(set([3,12,43])) # 列表转为集合 print(set('Python')) # 元组转为集合 print(set({123,4,4,2,1})) # 字典转为集合 重复的元素会去除 输出元素顺序不同 print(set(),type(s2)) #空集合 不能使用s={} 系统会识别为空字典 # 顺序时乱的 # {0, 1, 2, 3, 4, 5} # {43, 3, 12} # {'y', 'o', 'h', 'n', 'P', 't'} # {1, 2, 123, 4} # set() <class 'set'>
-
集合元素的判断操作
- in
- not in
集合元素的新增操作
- 调用add()方法,一次添加一个元素
s.add('zhang')
print(s)
- 调用update()方法至少添加一个元素
s.update({'21','fwff','fwewe'}) # 添加集合
print(s)
s.update([21,31,44,11]) #添加列表
print(s)
s.update(range(4)) # 添加序列
print(s)
s.update({'yexin1':14}) #只存储key value不存储
print(s)
集合元素的删除操作
- 调用remove()方法,一次删除一个指定元素,如果指定元素不存在 抛出KeyError
s.remove('hello')
print(s)
# KeyError: 'hello11' 不存在的抛出异常
- 调用discard()方法,一次删除一个指定元素,但指定元素不存在 不会抛出异常
s.discard('yexin1')
print(s) #没有指定的元素 集合内的元素也没有改变
- 调用pop()方法,一次只删除一个任意元素
s.pop()
print(s) #删除的是任意一个元素 并不会是最后一个
- 调用clear()方法,清空集合
s.clear()
print(s) #set()
集合的关系
-
两个集合是否相等
- 可以使用运算符==或!=进行判断 只要元素内容相同就是相等
s1={21,31,44,1} s2={21,1,44,31} print(s1,s2) print(s1 == s2) print(s1 != s2)
-
一个集合是否是另一个集合的子集
- 可以调用方法issubset进行判断 子集调用函数 参数是集合
- B是A的子集
# B.issubset(A) s1={21,31,44,1} s3={21,1} print(s3.issubset(s1)) #True
-
一个集合是否是另一个集合的超集
- 可以调用方法isuperset进行判断 调用函数的集合大于被作为参数的集合
- A是B的超集
# A.issuperset(B) s1={21,31,44,1} s3={21,1} print(s1.issuperset(s3)) #True
-
两个集合是否没有交集
- 可以调用方法isdisjoint进行判断 两者都可以调用 结果都一样
S4={90,11} print(S4.isdisjoint(s1)) #True print(s1.isdisjoint(S4)) #True
集合数学操作
- 交集 A和B的交 调用函数intersection() 无论谁当参数 返回的结果都是交集集合
# (1) 交集方法1 intersection()
s1={10,20,30,40}
s2={20,30,40,50,60}
print(s1.intersection(s2))
print(s2.intersection(s1))
# (2) 交集方法2 s1 & s2
s4=s3 & s1
print(s4)
print(s1 & s2)
-
并集 A和B的全部 调用函数union() 无论参数是谁 两者的结果都一样 或者使用 | 操作
会去除重复的元素
# (2) 并集
print(s1 | s2)
print(s2 | s1)
print(s1.union(s2))
print(s2.union(s1))
#
- 差集 A有而B没有的集合 或者 A没有而B有的集合 注意这时候参数发生变化 输出的元素顺序不同
# (3) 差集操作
print(s1.difference(s2)) #{10}
print(s2.difference(s1)) #{50, 60}
# 方法二:s1-s2 获得s1有的而s2没有的
print(s1-s2)
-
对称差集 对于A没有的B有和对于B没有的A有的集合
对于函数参数的不同 输出元素的顺序不同 但集合相同 因为集合不看顺序只看结果
s5=s1.difference(s2) | s2.difference(s1) #这是将各自的差集 再并到一起获得集合 不算对称差集 因为结果不同(输出的元素顺序可能不同)
print(s5)
print(s1.symmetric_difference(s2)) #symmertic_difference()
print(s2.symmetric_difference(s1))
print(s1 ^ s2)
print(s2 ^ s1)
集合生成式
公式:s={i for i in range(6)}
与列表生成式类似 列表生成式用的是[] 集合采用{}
由于集合采用的哈希存储方式 因此数据过多时 元素存储的位置时错乱的 输出结果同样没有顺序
# 列表生成式
lst1=[i for i in range(6)]
print(lst1,type(lst1)) #[0, 1, 2, 3, 4, 5] <class 'list'>
# 集合生成式
set1={i for i in range(6)}
print(set1,type(set1)) #{0, 1, 2, 3, 4, 5} <class 'set'>
# 存储元素过多时 输出结果错乱
set2={i*i for i in range(10)}
print(set2) # {0, 1, 64, 4, 36, 9, 16, 49, 81, 25}
数据结构小结
目前学习的有:列表、元组、字典、集合
数据结构 | 是否可变 | 是否重复 | 是否有序 | 定义字符 |
---|---|---|---|---|
列表(list) | 可变 | 可以重复 | 有序 | [] |
元组(tuple) | 不可变 | 可以重复 | 有序 | () |
字典(dict) | 可变 | key不能重复 value不能重复 | 无序 | {key:value} |
集合 | 可变 | 不可重复 | 无序 | {} |
字符串
字符串的驻留机制
- 字符串
- 基本的数据类型,和元组一样是不可变的字符类型
- 为什么叫字符串驻留机制呢?
- 仅保留一份相同且不可变字符串的方法,不同的值会被存放在字符串的驻留池中,Python的驻留机制对相同的字符串只保留一份拷贝,后续创建相同字符串时,不会开辟新的空间,而是把改字符串的地址赋值给新创建的变量
a='Python'
b="Python"
c="""Python"""
print(a,id(a)) #Python 1274270800304
print(b,id(b)) #Python 1274270800304
print(c,id(c)) #Python 1274270800304
驻留机制的几种情况(交互模式)
-
字符串的长度为0或者1时
s1='' s2='' s1 is s2 True s1 == s2 True
-
符号标识符的字符串
s1='abc%' s2='abc%' s1==s2 True #内容是相同的 s1 is s2 False #不符合标识符的规范 开辟了新的地址 但是在Pycharm中会优化处理 >>> id(s1) 2820976690224 >>> id(s2) 2820976690160 #地址不同 没有触发驻留机制
-
字符串只在编译时进行驻留
a='abc' b='ab'+'c' c=''.join(['ab','c']) print(a,b,c) #abc abc abc 输出结果都是一样的 a is b #True a is c #False 原因是C是在程序运行的时候开辟新空间产生的 而b和a都是在之前
-
[-5,256]之间的整数数字
a=-6 b=-6 a is b # False超过的整数范围
-
sys中的intern方法强制2个字符串指向同一个对象 不能对整数进行强制
a='abc%' b='abc%' a is b #False import sys a=sys.intern(b) a is b # True
-
PyCharm对字符串进行了优化处理 需要用IDE自带的编译器编辑 或者终端
字符串驻留机制的优缺点
-
当需要值相同的字符串时,可以直接从字符串池子李拿来使用,避免频繁的创建和销毁,提升效率和节约内存,因此拼接字符串和修改字符串是会比较影响性能的。
-
在需要进行字符串拼接时建议使用str类型的join方法,而非+,因为join()方法是先计算出所有字符中的长度,然后再拷贝,只new一次对象,效率要比'+'高
-
join()函数的使用 用于将序列中的元素以指定的字符连接生成一个新的字符串
print(''.join('ab'+'c')) #以空字符来连接字符'ab' 'c'获得新的字符串 symbol = "-" seq = ("a", "b", "c") # 字符串序列 print(symbol.join( seq )) # a-b-c
字符串的常用操作
查询操作的方法
- index() 查找子串substr第一次出现的位置,如果查找的子串不存在,抛出ValueError
- rindex() 查找子串substr最后一次出现的位置,如果查找的子串不存在,抛出ValueError
- find() 查找子串substr第一次出现的位置,如果查找的子串不存在,返回-1
- rfind() 查找子串substr第一次出现的位置,如果查找的子串不存在,返回-1
大小写转换操作的方法
注意:字符串是不可变序列 因此大小写转换其实是创建了新的字符串 新的存储空间
- upper() 把所有字符串中所有字符都转成大写字母
- lower() 把字符串中所有字符都转成小写字母
- swapcase() 把字符串所有大写字母转成小写字母,把所有小写字母转成大写字母
- title() 把每个单词的第一个字符转成大写,把每个单词的剩余字符转成小写
- istitle() 判断首字符是否大写 true false
s='new'
print(s,id(s)) # new 1311626644656
print(s.upper(),id(s.upper())) # NEW 1311631544816
print(S.lower(),id(S.lower())) # new 2987811391600 转大写后再转小写 不会有驻留机制 仍然存储新空间
字符串内容对齐操作的方法
- center() 居中对齐,第1个参数指定宽度,第2个参数指定填充符号,第2个参数是可选的,默认是空格,如果设置宽度小于实际宽度则返回原字符串
- ljust() 左对齐,第1个参数指定宽度,第2个参数指定填充符,第2个参数是可选的,默认是空格如果设置宽度小于实际宽度则返回原字符串
- rjust() 右对齐,第1个参数指定宽度,第2个参数指定填充符,第2个参数是可选的,默认是空格。如果设置宽度小于实际宽度则返回原字符串
- zfill() 右对齐,左边用0填充,该方法只接受一个参数,用于指定字符串的宽度,如果指定的宽度小于等于字符串的长度,返回字符串本身
str1='helloPython'
print(str1.center(20,'*'))
print(str1.center(20))
print(str1.center(10))
print('--------左对齐----------')
print(str1.ljust(20,'*'))
print(str1.ljust(20))
print(str1.ljust(10))
print('--------右对齐----------')
print(str1.rjust(20,'*'))
print(str1.rjust(20))
print(str1.rjust(10))
print('-1392'.rjust(20,'0')) #000000000000000-1392
print('--------右对齐,0填充----------')
print(str1.zfill(20))
print(str1.zfill(10))
print('93021'.zfill(20))
print('-1392'.zfill(20)) #-0000000000000001392
注意两个注释,zfill()函数用0填充,对于负数是保留了负数字符串的含义 但rjust()则是以字符串的形式严格执行 会将0填充在负号前面
字符串劈分操作得到方法
-
split()
- 从字符串的左边开始劈分,默认的劈分符号是空格字符串,返回的值都是一个列表
- 以通过参数sep指定劈分字符串是劈分符
- 通过参数maxsplit指定劈分字符串时的最大劈分次数,在经历最大次劈分之后,剩余的子串会的单独作为一部分
s='hello|worldPython' print(s,id(s)) print(s.split('|'),id(s.split('|')),type(s.split('|'))) # hello|worldPython 2774456863200 # ['hello', 'worldPython'] 2774456144384 <class 'list'>
分割后转化为列表了 分割符会消失
-
rsplit()
- 从字符串的右边开始劈分,默认的劈分符号是空格字符串,返回的值都是一个列表
- 以通过参数sep指定劈分字符串是劈分符
- 通过参数maxsplit指定劈分字符串时的最大劈分次数,在经历最大次劈分之后,剩余的子串会的单独作为一部分
s='hello|world|Python' print(s,id(s)) lst=s.rsplit('|',maxsplit=1) print(lst) s='helloworldPython' print(s.rsplit('o')) # hello|world|Python 2497213434416 # ['hello|world', 'Python'] # ['hell', 'w', 'rldPyth', 'n']
从右侧开始分割 但是输出顺序不变
判断字符串的操作方法
方法名称 | 作用 |
---|---|
isidentifier() | 判断指定的字符串是不是合法的标识符 |
isspace() | 判断指定的字符串是否全部由空白字符组成(回车\r、换行\n,水平制表符\t等) |
isalpha() | 判断指定的字符串是否全部由字母组成 |
isdecimal() | 判断指定字符串是否全部由十进制的数字组成 |
isnumeric() | 判断指定的字符串是否全部由数字组成 |
isalnum() | 判断指定字符串是否全部由字母和数字组成 |
字符串操作的其他方法
功能 | 方法名称 | 作用 |
---|---|---|
字符串替换 | replace() | 第一个参数指定被替换的子串,第2个参数指定替换该子串的字符串,该方法返回替换后得到的字符串,替换前的字符串不发生变化,调用该方法时可以通过第3个参数指定最大替换次数 |
字符串的合并 | join() | 将列表或元组中的字符串合并成一个字符串 列表或者元组作为参数(单个字符串也可以视为字符序列) 而连接符作为调用函数的参数 可以是空格 |
s='hello|world|python'
print(s.replace('o','*****',2))
lst1=s.split('|')
print(lst1,id(lst1))
print(lst1[1].join(lst1[1]))
# 列表 [] 元组() 字典{key:value} 集合{}
t=('hello','world','python')
print('+'.join(t),type('+'.join(t))) #注意join()函数的参数是被连接的字符组合 列表或者元组
print('|'.join(lst1),type('|'.join((lst1))))
print('*'.join('Python')) #P*y*t*h*o*n 将字符串python视为序列进行连接
字符串的比较操作
- 运算符:> , >= , < , <= , == , !=
- 比较规则:首先比较两个字符串中的第一个字符,如果相等则继续比较下一个字符,依次比较下去,直到两个字符串中的字符不相等时,其比较结果就是两个字符串的比较结果,两个字符串中的所有后续字符将不再被比较
- 返回的是boolen类型
- 比较原理:两上字符比较时,比较的是其ordinal value(原始值),调用内置函数ord可以得到指定字符的ordinal value。 与内置函数ord对应的是内置函数chr,调用内置函数chr时指定ordinal value 可以得到其对应的字符
- ord() 参数为string chr()参数是int
print('a' > 'A') #True
print(ord('a')) #97
print(chr(97)) #a
print(chr(ord('a'))) #a
字符串的切片操作
- 字符串是不可变类型
- 不具备增删改等操作
- 切片操作将产生新的对象
- 公式: s[start:stop:step] 和序列操作一样
# 字符串切片
s='helloPython'
print(s,id(s))
print(s[:5],id(s[:5]))
print(s[:5:-1])
print(s[:-6:1])
print(s[5::1])
格式化字符串
-
两种方式
-
%作占位符(以前的c或者c++里的)
- 公式 print('%d' & 99) 必须要有 %d & 99
- %s 字符串
- %d 整数 %3d表示宽度为3 三个字符的位置
- %f 浮点 %.3f表示保留后三位小数
# (1) % 占位符 name='张三' age=10 print('我是%s,今年%d岁' % (name,age)) print('%.3f % 3.115926')
-
{}作占位符
- {}里用数字
# (2) {} print('我叫{0},今年{1}岁'.format(name,age)) #是字符串调用format() print('{0:.3}'.format(3.1415926)) #表示保留三位数 print('{0:.3f}'.format(3.1415926)) #表示保留三位小数 print('{0:3.3f}'.format(3.1415926)) #表示宽度是3 同时保留三位小数
- f-string {}里用的变量要被实参化
print(f'我叫{name},今年{age}岁') #注意要加f 不然name age不能做变量来调用
-
字符串的编码
- 编码与解码的方式
- 编码:将字符串转换为二进制数据(bytes)
- 解码:将bytes类型的数据转换成字符串类型
函数
应给函数指定描述性名称,且只在其中使用小写字母和下 划线。描述性名称可帮助你和别人明白代码想要做什么。给模块命名时也应遵循上述约定.
函数的创建和调用
-
为什么需要函数
- 复用代码
- 隐藏实现细节
- 实现可维护性
- 提高可读性便于调用
-
函数的创建
关键字
def
来告诉Python你要定义一个函数def 函数名 ([输入参数]): 函数体 [return xxx] def display_message(username): print(username + '在本章学的是函数!') display_message('yexin') // yexin在本章学的是函数!
函数的参数传递
-
位置实参
函数定义和函数使用上的变量一一对应
-
关键字实参
根据形参名进行实参传递 函数使用时指定变量
指定传递的变量必须放在最后面
无论是函数定义还是调用在使用默认值时,在形参列表中必须先列出没有默认值的形参,再列出有默认值的实参。
def calc1(a,b): return a+b def calc2(a,b=2): return a+b def calc2(a=1,b=2): //默认值设定 return a+b print(calc2(1,2)) //虽然形参实例了但是不影响实参传递 print(calc2(a=1,b=2)) //关键字传递不关心a、b位置 print(calc3(3,b=1)) // 指定传递的变量必须放在最后面
函数的返回值
函数在定义时,是否需要返回值,视情况而定
- 函数返回多个值时,结果为
元组
def others():
return 12,31
print(others(),type(others()))
//(12, 31) <class 'tuple'> 元组类型
- 函数的返回值,如果是1个,直接返回类型
- 函数如果没有返回值(函数执行完毕后,不需要给调用处提供数据) return可以省略不写
"""返回整洁的姓名"""
def get_formatted_name(first_name, middle_name, last_name):
full_name = first_name+' '+middle_name+' '+last_name
return full_name.title()
musician = get_formatted_name('john', 'lee', 'hooker')
print(musician)
返回字典
def get_formatted_name(first_name,last_name):
'''返回一个字典 包含名和姓'''
return {'first':first_name,'last':last_name}
print(get_formatted_name('Jon','Snow'))
//{'first': 'Jon', 'last': 'Snow'}
函数的参数定义
参数的类型 | 函数的定义 | 函数的调用 | 备注 |
---|---|---|---|
位置实参 | √ | ||
将序列中的每个元素都转换为位置实参 | √ | 使用* | |
关键字实参 | √ | ||
将字典中的每个键值对都转换为关键字实参 | √ | 使用** | |
默认值形参 | √ | ||
关键字形参 | √ | 使用* | |
个数可变的位置形参 | √ | 使用* | |
个数可变的关键字形参 | √ | 使用** |
def fun1(a,b,c):
print('a= ',a,type(a))
print('b= ',b)
print('c= ',c,type(c))
lst1=[1,2,3]
lst2=[4,5,6]
dict1 = {'a':1,'b':6,'c':3}
fun1(*lst1) #序列中的每个元素都转换为位置实参
fun1(lst1,b=1,c=2) #序列作为一个整体传入
fun1(**dict1) #字典中的每个键值对都转换为关键字实参
fun1(*dict1) #字典中的每个键值都转换为关键字实参
def fun2(*args):
print(args)
# 位置传递 返回序列
def fun3(**args2):
print(args2)
# 关键字传递,返回字典
def fun3(a,b,*,c,d): # 要求c、d只能是关键字传递
print('a= ',a)
print('b= ',b)
print('c= ',c)
print('d= ',d)
函数定义时的形参的顺序问题
def funn1(*args1,**args2):
pass
def funn2(a,b,*,c,d,**arfs):
pass
def fun3(a,b,*c,d,**arfs):
pass
代码示例
def get_formatted_name(first_name,last_name,middle_name=''):
'''判断middle_name不为空表示true'''
if middle_name:
full_name = first_name + ' ' + middle_name + ' ' + last_name
else:
full_name = first_name+' '+last_name
return full_name.title()
musician = get_formatted_name('John','Hello')
print(musician)
musician = get_formatted_name('John','Hello','Snow')
print(musician)
/**
根据这三部分创建一个字符串, 在适当的地方加上空格,并将结果转换为首字母大写格式
对函数进行重构 无论是否有中间名都能能使用 有默认值的必须放在最后面
*/
参数是列表
列表包含的可能是名字、数字或更复杂的对象(如字典)
使用函数来提高处理列表的效率
在函数中对列表所做的任何修改都是永久性的
但如果禁止函数修改原列表的话 切片表示法[:]创建列表的副本 不影响原先的列表
function_name(list_name[:]) //函数调用的书写规范 函数声明中不需要
参数个数可变的位置形参
- 定义函数时,可能无法事先确定传递的行参数的个数,使用可变的位置参数
- 使用
*
定义个数可变的位置形参 - 结果为一个元组
- 可数可变的位置形参
只能是一个
def fun1(*args):
print(args,type(args))
fun1(10,21,33)
// (10, 21, 33) <class 'tuple'>
参数个数可变的关键字形参
关键字指的是指定了的 函数的返回值中有讲解
- 定义函数时,无法事先确定传递的关键字实参的个数,使用可变的关键字形参
- 使用
**
定义个数可变的关键字形参 - 结果为一个字典
- 可数可变的关键字形参
只能是一个
def fun2(**args):
print(args,type(args))
fun2(a=12,b=21,c=33)
// {'a': 12, 'b': 21, 'c': 33} <class 'dict'>
如果既有可变形参,也有可变关键字形参,那么要求
可变的位置形参在可变的关键字形参前
变量的作用域
- 程序代码能访问该变量的区域
- 根据变量的有效范围可分为
- 局部变量
- 函数内定义使用的变量,只在函数内部有效,局部变量使用global声明,这个变量就会成为全局变量
- 全局变量
- 函数体外定义的变量。可作用于函数内外
- 局部变量
def fun(a,b):
c=a+b
print(c)
def fun2(a,b):
global d #定义全局变量
d=a+b
print(d)
fun(1,2)
fun2(1,2)
print(d)
递归函数
- 组成部分
- 递归调用与递归终止条件
- 调用过程
- 每递归调用一次函数,都会在栈内存分配一个栈帧
- 每执行完一次函数,都会释放相应的空间
- 优缺点
- 占用内存多,效率低下
- 思路和代码简单
使用递归来计算阶乘
def fun(a):
if a-1:
return fun(a-1)*a
else:
return 1
print(fun(3))
print(fun(6))
使用递归来做费波那契数列
def fun(a):
if a==1 or a==2:
return 1
else:
return fun(a-1)+fun(a-2)
# 获取i个数的费波那契数列
lst1=[]
for i in range(1,5):
lst1.append(fun(i))
print(fun(i))
Python的异常处理机制
- Python提供了异常处理机制,可以在异常出现时即时捕获,然后内部“消化“,让程序继续执行
try ... except ...
结构
try:
n1 = int(input('请输入一个整数:'))
n2 = int(input('请输入另一个整数:'))
result = n1/n2
except ZeroDivisionError: # 捕获的异常 如果出现n2为0会跳出并且不中断
print('除数不允许为零')
except ValueError:
print('输入数字串')
print('程序结束')
except BaseException as e
print(e)
# except 捕获的错误例如ValueError ZeroDivisionError 都是终端中获得的 不是自己编写的
捕获异常的顺序按照先子类后父类的顺序,为了避免遗漏可能出现的异常,可以在最后增加BaseException
try ... except ... else
结构
如果没有出现异常 就执行else
try:
n1 = int(input('请输入一个整数:'))
n2 = int(input('请输入另一个整数:'))
result = n1/n2
except ZeroDivisionError:
print('除数不允许为零')
except ValueError:
print('输入数字串')
# 如果没有出现异常 就执行else
else:
print('结果为:',result)
print('程序结束')
try ... except ... else ... finally
结构
finally 块无论是否发生异常都会被执行,常能用来释放try块中申请的资源
try:
n1 = int(input('请输入一个整数:'))
n2 = int(input('请输入另一个整数:'))
result = n1/n2
except ZeroDivisionError:
print('除数不允许为零')
except ValueError:
print('输入数字串')
# 如果没有出现异常 就执行else
else:
print('结果为:',result)
finally:
print("程序无论异常与否,总会被执行")
print('程序结束')
Python常见的异常类型
序号 | 异常类型 | 描述 |
---|---|---|
1 | ZeroDivisionError | 除(或取模)零(所有数据类型) |
2 | IndexError | 序列中没有此索引(index) |
3 | KeyError | 映射中没有这个键 |
4 | NameError | 未声明/初始化对象(没有属性) |
5 | SyntaxError | Python语法错误 |
6 | ValueError | 传入无效的参数 |
trackback模块
- 使用trackback模块打印异常信息
import trackback
try:
print('1.----------------')
num=10/0
except:
trackback.print_exc()
编程思想
面向对象
面向过程
类和对象的创建
Python中万物皆为对象 类其实是类对象 对象是类对象的实例 叫做实例对象
-
类
-
数据类型
- 不同的数据类型属于不同的类
- 使用内置函数查看数据类型
print(type(100))
-
创建类的语法
class Student: #Student为类名 由一个或多个单词组成,首字母大写 pass
-
类的组成
-
类属性
-
类中方法外的变量称为类属性,被该类的所有对象所共享(包括类对象)
-
任何对象或是类对象都可以调用或者修改类属性
print(Student.native_place) #访问类属性 Student.native_place='北京' print(stud1.native_place) #北京
-
-
实例方法
- 类内部定义的函数才称作方法
- 实例方法需要有实例对象才能调用
-
静态方法
-
使用@classmethod修饰的方法,使用类名直接访问的方法
Student.sm() #调用类方法
-
-
类方法
-
使用@classmethod修饰的方法,使用类名直接访问的方法
Student.cm() #调用静态方法
-
class Student: #Student为类名 由一个或多个单词组成,首字母大写 native_place='江西' #直接写在类里的变量,称为类属性 #初始化方法 必须构造 def __int__(self,name,age): # name age是实例属性 self.name = name # 赋值操作 self.name称为实例属性 self.age = age # 实例方法 在类之内定义的称为方法 类之外的成为函数 def info(self): print('我的名字叫:',self.name,'年龄是:',self.age) # 类方法 @classmethod def cm(cls): #要求传递一个cls print('我使用了classmethod进行修饰,所以我是类方法') #静态方法 @staticmethod def sm(): #不允许写self 这是规定 print('我使用了staticmethod进行修饰,所以我是静态方法')
-
-
-
对象
-
100,99都是int类之下包含的相似的不同个例,个例的专业术语称为实例或对象
-
对象的创建又称为类的实例化
-
语法:
- 实例名=类名()
# 对象名.方法名() 传统的方法调用方式,先实例化,再通过对象调用方法 print('-------------------------------') Student.eat(stud1) Student.info(stud1) stud=Student('Jack',20) print(stud.name) #实例属性 print(stud.age) #实例属性 stud.info() #实例方法 # 类名.方法名(实例对象) ---> 实际上就是方法定义处的self 这是新的方法调用方式 print('-------------------------------') Student.eat(stud1) Student.info(stud1) # 创建Student类的对象 stud1 = Student('张三',20) print(id(stud1)) #检查是否开启内存空间 print(type(stud1)) #stud1的值 print(stud1) print('-------------------------------') print(id(Student)) #Student是类的名称 Student是类对象,stud1是实例对象 Python皆可对象 print(type(Student)) print(Student)
-
意义:有了实例,就可以调用类中的内容
-
动态绑定属性和方法
-
Python是动态语言,在创建对象之后,可以动态地绑定属性和方法
只适用于当前绑定的对象 其他对象无法调用
def show(): print('定义在类之外的称为函数') stu= c10demo1.Student('Jack',20) stu.gender='男' #动态绑定性别 print(stu.name,stu.age,stu.gender) stu.show=show #动态绑定方法 stu.show()
面向对象的三大特征
封装
-
提高程序的安全性
- 将数据(属性)和行为(方法)包装到类对象中。在方法内部对属性进行操作,在类对象的外部调用方法。这样,无需关心内部的具体实现细节,从而隔离了复杂度。
- 在Python中没有专门的修饰符用于属性的私有,如果该属性不希望在类对象外部被访问,前边使用两个"_"
class Student: def __init__(self,name,age): self.name=name self.__age=age #不希望age在外部被调用或查看 def show(self): print(self.name,self.__age) #可以在内部使用 # print(stud1.age) 报错找不到age属性 # 非得访问用以下方法 但既然都不希望被访问就不要访问了 print(dir(stud1)) #查找stud1内的函数和属性 找到_Student__age print(stud1._Student__age) #以另一种方式获取到了age
继承
-
提高代码的复用性
-
语法格式
-
class 子类类名(父类1,父类2....): pass
-
-
如果一个类没有继承任何类,则默认基础object
-
Python支持多继承 使用super()需要注意
# 多继承 class A(object): def __init__(self,age): self.age=age class B(object): def __init__(self,name): self.name=name class C(A,B): def __init__(self,age,name,score): #要想调用特定父类的构造器可以使用父类名.__init__方式。多继承使用super,会有一个坑 A.__init__(self,age) B.__init__(self,name) self.score=score c=C(11,'ee',90) print(c.name,c.age,c.score)
-
定义子类时,必须在其构造函数中调用父类的构造函数 俗称实例化父类
# 定义父类 不继承任何类 带object 也可以不写 最好还是写 以便区分父类 class Person(object): def __init__(self,age,name): self.name=name self.__age=age def show(self): print(self.name,'同学请来办公室一趟,恭喜你',self.__age,'岁了!') # 创建子类 括号内写父类 调用父类方法用super() class Student(Person): def __init__(self,name,age,score): super().__init__(name,age) #通过super()调用父类方法 self.score=score stud1=Student(18,'Jack',100) stud1.show() print(stud1.score)
方法重写
-
如果子类对继承自父类的某个属性或者方法不满意,可以在子类中对其(方法体)进行重新编写
-
子类重写后的方法中可以通过
super().xxx()
调用父类中被重写的方法class Person(object): def __init__(self,name,age): self.name = name self.age = age def info(self): print('{0},的年龄是{1}'.format(self.name,self.age)) class Student(Person): def __init__(self,name,age,score): Person.__init__(self,name,age) self.score=score def info(self): # Person.info(self) 两个方法都能用 super().info() print(self.name,'的成绩是',self.score) stud1=Student('yexin',18,99) stud1.info()
object类
-
object类是所有类的父类,因此所有类都有object类的属性和方法
-
内置函数dir()可以查看指定对象所有属性
o=object() p=Person('Jack',20) print(dir(o)) print(dir(p))
-
object有一个
_str_()
方法,用于返回(return)
一个对于“对象的描述”,对应于内置函数str()经常用于print()方法,帮我们查看对象的信息,所以我们经常会对_str_()
重写class Person(object): def __init__(self,name,age): self.name = name self.age = age def __str__(self): return '{0}的年龄是{1}'.format(self.name,self.age) #return返回信息 o = object() p = Person('Jack',20) print(p) #重写__str__()函数 可以直接print(p)对象信息 print(p.__str__()) # 也可以对象调用 print(dir(o) == dir(object)) print(dir(p)) print(dir(Person)) print(dir(p) == dir(Person)) print(dir(p) is dir(Person))
多态
-
提高程序的可扩展性
-
具有多种形态 即便不知道一个变量所引用的对象到底是什么类型,仍然可以通过这个变量调用方法,在运行过程中根据变量所引用对象的类型,动态决定调用哪个对象中的方法
# 多态 class Animal(object): def eat(self): print('Animal会吃') class Cat(Animal): def eat(self): print('Cat会吃鱼') class Dog(Animal): def eat(self): print('Dog吃骨头') class Person(): def eat(self): print('人吃五谷杂粮') def fun(obj): obj.eat() fun(Dog()) fun(Cat()) fun(Animal()) fun(Person()) #即使Person没有继承Animal 只要Person里有eat()方法就会动态调用
静态语言与动态语言
- 静态语言和动态语言关于多态的区别
- 静态语言实现多态的三个必要条件
- 继承
- 方法重写
- 父类引用指向子类对象
- 静态语言实现多态的三个必要条件
- 多态语言的多态崇尚“鸭子类型”当看到一只鸟走起来像鸭子、游泳起来像鸭子、收起来也像鸭子,那么这只鸟就可以被称为鸭子。在鸭子类型中,不需要关心对象是什么类型,到底是不是鸭子,只关心对象的行为(即方法)
特殊方法和特殊属性
-
特殊属性
-
__dict__
获得类对象或实例对象所绑定的所有属性和方法的字典 -
__class__
输出对象所属的类 -
__base__
输出对象类型继承的第一个父类类型 -
__bases__
输出对象类型继承的所有父类类型 -
__mro__
输出对象类型继承的层次结构# 特殊属性 class Person(object): def __init__(self,name,age): self.name = name self.age = age class Animal(object): pass class Student(Person,Animal): def __init__(self,name,age,score): super().__init__(name,age) self.score = score stud1 = Student('yexin',12,100) print(stud1.__class__) # <class '__main__.Student'> print(stud1.__dict__) # {'name': 'yexin', 'age': 12, 'score': 100} print(Student.__dict__) print(Student.__mro__) # (<class '__main__.Student'>, <class '__main__.Person'>, <class '__main__.Animal'>, <class 'object'>) print(Student.__base__) # <class '__main__.Person'> print(Student.__bases__) # (<class '__main__.Person'>, <class '__main__.Animal'>)
-
-
特殊方法
-
__len__()
通过重写该方法,让内置函数len()的参数可以是自定义类型class Person(object): def __init__(self,name): self.name = name def __add__(self, other): return self.name+other.name #还可以是别的属性相加 只要是return就行 def __len__(self): return len(self.name) #重写让对象类拥有len方法,并且参数自定义 lst=[21,33,11,41,55] print(len(lst)) print(len(a)) print(len(a.__add__(b)))
-
__add__()
通过重写该方法,可使用自定义对象具有”+”功能class Person(object): def __init__(self,name): self.name = name def __add__(self, other): return self.name+other.name #还可以是别的属性相加 只要是return就行 a=Person('王权') b=Person('富贵') c=a+b print(c) print(a.__add__(b)) #使用方式
-
__new__()
通过创建对象 -
__init__()
对创建的对象进行初始化class Person(object): def __new__(cls, *args, **kwargs): print('__new__被调用了,cls的id值是{0}'.format(id(cls))) obj = super().__new__(cls) print('创建的对象的id为:{0}'.format(id(obj))) return obj def __init__(self,name,age): print('__init__被调用了,self的id为{0}'.format(id(self))) self.name = name self.age = age p1 = Person('Jack',19) print('Object这个类的id是{0}'.format(id(object))) print('Person这个类的id是{0}'.format(id(Person))) print('创建的实体对象的id是{0}'.format(id(p1))) # __new__被调用了,cls的id值是1989172968160 # 创建的对象的id为:1989172624928 # __init__被调用了,self的id为1989172624928 # Object这个类的id是140724810562384 # Person这个类的id是1989172968160 # 创建的实体对象的id是1989172624928
从new开始创建了对象,然后对象传递到初始化方法内,初始化结束返回到实体对象中去
-
类的浅拷贝与深拷贝
-
变量的赋值操作
- 只是形成两个变量,实际上还是指向同一个对象
class CPU: pass class Disk: pass class Computer: def __init__(self,cpu,disk): self.cpu=cpu self.disk=disk #(1) 变量的赋值 cpu1=CPU() cpu2=cpu1 # 直接将对象赋值 不需要先创建新对象再赋值 真简单 指向的作用 print(cpu1,id(cpu1)) print(cpu2,id(cpu2))
-
浅拷贝
- Python拷贝一般都是浅拷贝,对象包含的子对象内容不拷贝,源对象与拷贝对象会引用同一个子对象
class CPU: pass class Disk: pass class Computer: def __init__(self,cpu,disk): self.cpu=cpu self.disk=disk #(2) 浅拷贝 print('-----------------') cpu1=CPU() disk=Disk() computer=Computer(cpu1,disk) import copy computer2=copy.copy(computer) print(computer,computer.cpu,computer.disk) print(computer2,computer2.cpu,computer2.disk) # computer与computer2地址不同,但其子对象的内容一样,采用引用的方法
-
深拷贝
- 使用copy模块的deepcopy函数,递归拷贝对象中包含的子对象,源对象和拷贝对象所有的子对象也不相同
class CPU: pass class Disk: pass class Computer: def __init__(self,cpu,disk): self.cpu=cpu self.disk=disk #(3)深拷贝 print('---------------------------') disk=Disk() cpu1=CPU() computer=Computer(cpu1,disk) computer3=copy.deepcopy(computer) print(computer,computer.cpu,computer.disk) print(computer3,computer3.cpu,computer3.disk) # computer与computer3及其子对象的存储地址都不相同
模块
- 函数与模块的关系
- 一个模块中可以包含N多个函数
- 在Python中一个扩展名为.py文件就是一个模块
- 使用模块的好处
- 方便其他程序的脚本的导入并使用
- 避免函数名和变量名冲突
- 提高代码的可维护性
- 提高代码的可重用性
自定义模块
-
创建模块
- 新建一个.py文件,名称尽量不要与Python自带的标准模块名称相同
-
导入模块
import 模块名称 [as 别名] #导入该模块的所有内容 from 模块名称 import 函数/变量/类 #导入该模块的指定内容 # from math import pi import math print(math.pi) #需要通过模块.内容来使用 print(pow(2,3)) print(math.pow(2,3)) # 导入自定义模块 from c11demo2 import Person
以主程序形式运行
-
在每个模块的定义中都包括一个记录模块名称的变量
_name_
,程序可以检查该变量,以确定他们在哪个模块中执行。如果一个模块不是被导入到其他程序中执行,那么它可能在解释器的顶级模块中执行。顶级模块的_name_
变量的值为_main_
if _name_ = ='_main_': pass
模块中(.py文件中)只会执行pass的内容 其他内容不执行
import c11demo2 p1 = c11demo2.Person('yexin') p1.get_name()
class Person(): def __init__(self,name): self.name=name def get_name(self): print('你好',self.name) # 只会在这里执行 被导入到其他文件但不会执行以下内容 if __name__ == '__main__': print('这是c11demo2文件,欢迎导入')
Python中的包(package)
-
包是一个分层次的目录结构,它将一组功能相近的模块在一个目录下
-
作用
- 代码规范
- 避免模块名称冲突
-
包与目录的区别
- 目录是directory
- 包含
_init_.py
文件的目录称为包 - 目录里通常不包含
_init_.py
文件
-
包的导入
import 包名.模块名 import package1.module1 # 使用import方式进行导入时,只能跟进包名或者模块名 from package1.module1 import a
Python中常用的内置模块
模块名 | 描述 |
---|---|
sys | 与Python解释器及其环境操作相关的标准库 |
time | 提供与时间相关的各种函数的标准库 |
os | 提供了访问操作系统服务功能的标准库 |
calendar | 提供与日期相关的各种函数的标准库 |
urlib | 用于读取来之网上(服务器)的数据标准库 爬虫使用的到 |
json | 用于使用JSON序列化和反序列化对象 |
re | 用于在字符串中执行正则表达式匹配和替换 |
math | 提供标准算术运算函数的标准库 |
decimal | 用于进行精准控制运算精度、有效数位和四舍五入操作的十进制运算 |
logging | 提供了灵活的记录事件、错误、警告和调试信息等目录信息的功能 |
import sys
print(sys.getsizeof(24))
print(sys.getsizeof(True))
import time
print(time.time())
print(time.localtime(time.time()))
import urllib.request
print(urllib.request.urlopen('http://www.baidu,com').read())
第三方模块的安装及使用
-
第三方模块的安装
-
pip install 模块名
没有报错说明导入成功 可以在编译器中导入使用或者直接终端编译
-
-
第三方模块的使用
-
import 模块名
import schedule import time def job(): print('做核酸了!!!') # 每隔三秒做一件事 休眠一秒 schedule.every(2).seconds.do(job) while True: schedule.run_pending() time.sleep(1)
-
编码格式的介绍
- 常见的字符编码格式
- Python的解释器使用的是Unicode(内存)
- .py文件在磁盘上使用UTF-8存储(外存)
文件的读写原理
-
文件的读写俗称“IO操作”
-
文件读写操作流程
-
操作原理
python操作文件------------->打开或新建文件------------->读、写文件------------->关闭资源
文件读写操作
文件的类型
- 按文件中数据的组织形式,文件分为以下两大类
- 文本文件:存储的是普通“字符”文本,默认为unicode字符集,可以使用记事本程序打开
- 二进制文件:把数据内容用“字节”进行存储,无法用记事本打开,必须使用专用的软件打开,举例:mp3音频文件,jpg图片,doc文档等
打开模式 | 描述 |
---|---|
r | 以只读模式打开文件,文件的指针将会放在文件的开头 |
w | 以只写模式打开文件,如果文件不存在则创建,如果文件存在,则覆盖原有内容,文件指针在文件开头 |
a | 以追加模式打开文件,如果文件不存在则创建,文件指针在文件开头,如果文件存在,则文件末尾追加内容,文件指针在源文件末尾 |
b | 以二进制方式打开文件,不能单独作用,需要与共它模式一起使用,rb,或者wb |
+ | 以读写方式打开文件,不能单独使用,需要它与其他模式一起使用,a+ |
通常使用rb
,wb
open('logo.png','rb') #读取模式打开文件
文件对象的常用方法
- read([size])
- 从文件中读取size个字节或字符的内容返回。若省略[size],则读取到文件末尾,即一次读取文件所有内容
- readline()
- 从文本读取中读取一行内容
- readlines()
- 把文本文件中每一行都作为单独的字符串对象,并将这些对象放入列表返回
- wirte(str)
- 将字符串str内容写入文件
- writelines(s_list)
- 将字符串列表s_list写入文本文件,不添加换行符
- seek(offset[,where])
- 将文本指针移动到新的位置,offset表示相对where的位置:
- offset:为正往结束方向移动,为负往开始方向移动
- where不同的值代表不同含义:
- 0:从文件头开始计算(默认值)
- 1:从当前位置开始计算
- 2:从文件尾开始计算
- 将文本指针移动到新的位置,offset表示相对where的位置:
- tell()
- 返回文件指针的当前位置
- flush()
- 把缓冲区的内容写入文件,但不关闭文件
- close()
- 把缓冲区的内容写入文件,同时关闭文件,释放文件对象相关资源
with语句(上下文管理器)
-
with语句可以自动管理上下文资源,无论什么原因跳出with块,都能确保文件正确的关闭,以此来达到释放资源的目的
-
不用手动关闭资源
with open('logo.png','rb') as src_file: src_file.read()
离开运行时上下文,自动调用上下文管理器的特殊方法
_exit_()
class MyContentMgr(object): def __enter__(self): print('enter方法被调用了') return self def __exit__(self, exc_type, exc_val, exc_tb): print('exit方法被调用执行了') def show(self): print('show方法被调用执行了') with MyContentMgr() as file: #相当于file=MyContentMgr() file.show()
目录操作
-
os模块是Python内置的与操作系统功能和文件系统相关的模块,该模块中的语句的执行结果通常与操作系统有关,在不同的操作系统上运行,得到的结果可能不一致。
-
os模块与os.path模块用于对目录或文件进行操作
# os模块与操作系统相关的一个模块 import os os.system('calc.exe') # 直接调用可执行文件 os.startfile('D:\\Program Files\\CloudMusic\\cloudmusic.exe')
函数 说明 getcd() 返回当前的工作目录,返回str listdir(path) 返回指定路径下的文件和目录信息(返回list列表) mkdir(path[,mode]) 创建目录,不是包 makedirs(path1/path2...[,mode]) 创建多级目录 rmdir(path) 删除目录 精确到要删除的目录下才能删除 removedirs(path1/path2......) 删除多级目录 必须把多级目录的每个文件都写出来 chdir(path) 将path设置为当前工作目录 修改当前的工作目录 import os print(os.getcd()) print(os.listdir('../chap11')) os.makedirs('../chap11/package2/package3/package4') os.rmdir('package2') #目录不是空的 os.rmdir('../chap11/package2/package3/package4') #精确到要删除的目录下才能删除 os.removedirs('../chap11/package2/package3') #必须把多级目录的每个文件都写出来 print(os.getcd()) os.chdir('../chap10') #前后两次工作目录不同 print(os.getcwd())
-
os.path模块操作目录相关函数
函数 说明 abspath(path) 用于获取文件或目录的绝对路径 exists(path) 用于判断文件或目录是否存在,如果存在返回True,否则返回False join(path,name) 将目录与目录或者文件名拼接起来 splitext() 分离为文件名和扩展名 basename(path) 从一个目录提取文件名 dirname(path) 从一个路径中提取文件路径,不包括文件名 isdir(path) 用于判断是否为路径 import os print(os.path.abspath('../../pythonProject2')) print(os.path.exists('../chap11')) print(os.path.exists('../chap111')) print(os.path.join('../chap11','c11demo7.py')) print(os.path.split(os.getcwd())) print(os.path.splitext('c11demo7.py')) print(os.path.basename('../chap11/c11demo7.py')) print(os.path.dirname('../chap11/c11demo7.py')) print(os.path.isdir('../chap11/c11demo7.py'))
小练习 列出指定目录下的所有Py文件 if filename.endwith('.py') 判断文件是否以'.py'做结尾
# 列出指定目录下的所有Py文件 import os i = 'y' while i == 'y': dir2 = '../' dir1 = dir2 + input('请输入要查找Py文件的目录:') if os.path.exists(dir1): lst = os.listdir(dir1) for i in lst: if '.py' in os.path.splitext(i): print(i,'是py文件') i = input('查找结束,请问是否再次查找?y/n') else: i = input('查找不到该目录,请问是否重新输入?y/n')