#Life is short,I need python.
import this
The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren't special enough to break the rules. Although practicality beats purity. Errors should never pass silently. Unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch. Now is better than never. Although never is often better than *right* now. If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea -- let's do more of those!
#dir(__builtins__)可以查看python的BIF内置函数
temp = input("猜一个数字")
guess = int(temp)
if guess == 8:
print("猜对了")
else:
print("猜错了")
猜一个数字8 猜对了
#导入random模块,random.randint(a,b)随机生成[a,b]之间的整数
import random
ans = random.randint(1,10)
counts = 3
while counts>0:
temp = input("猜一个数字")
guess = int(temp)
if guess == ans:
print("猜对了")
break
else:
if guess<ans:
print("小了")
else:
print("大啦")
counts = counts - 1
print("游戏结束了")
猜一个数字5 小了 猜一个数字8 小了 猜一个数字9 小了 游戏结束了
#random生成的随机数可以被重现
x = random.getstate() #获取随机生成数的内部状态,也称种子
print(random.randint(1,10))
print(random.randint(1,10))
random.setstate(x)
print(random.randint(1,10))
print(random.randint(1,10))
8 3 8 3
x = 3
print(x)
3
#python3开始支持中文变量
幸运数 = 6
print(幸运数)
6
#交换两个变量的值,python提供一种十分优雅的方式
x = 3
y = 5
x,y = y,x
print(x,y)
5 3
#字符串必须用引号(注意单双引号的配对)引起来,否则python会将其混淆成中文变量名
print('I love China.')
print("I love python.")
print("Let's go!")
#还可以使用转义字符来实现
print("\"Life is short,let\'s learn python.\"")
#转义字符加n还能表示换行
print("I love python.\nI love fishc.")
I love China. I love python. Let's go! "Life is short,let's learn python." I love python. I love fishc.
#原始字符串
print("D:\\three\\two\\one\\now") #输出一个转义字符
print(r"D:\three\two\one\now") #r表示原始字符串,原始字符串转义字符将不再有效
D:\three\two\one\now D:\three\two\one\now
#长字符串使用三个单引号或者三个双引号
str = """
面朝大海
春暖花开
"""
print(str)
面朝大海 春暖花开
#字符串的加法和乘法
print('520' + '1314')
print('love' * 3)
5201314 lovelovelove
#python的整数长度是不受限制的,可以进行大数运算
print(11125563314 * 15365576687)
170950696287340860718
#python浮点数有一定精度上的误差,一般不用做条件的判断
print(0.1 + 0.2)
print(0.1 + 0.2 == 0.3)
print(0.1 + 0.2 > 0.3)
0.30000000000000004 False True
#借助decimal模块,实现浮点数的精确运算
import decimal
a = decimal.Decimal('0.1')
b = decimal.Decimal('0.2')
print(a + b)
0.3
#python使用E记法
print(0.00005)
5e-05
x = 1 + 2j
print(x.real) #获取实部
print(x.imag) #获取虚部
1.0 2.0
#常见的有加减乘除四则运算,不再一一列举
#1.地板除 --运算结果为向下取整的整数
print(3//2)
print(-3//2)
1 -2
#2.取余数操作%
print(3%2)
##这里就发现一个公式x == (x//y)*y + x%y
1
#3.divmod(x,y)返回地板除、取余数的结果
print(divmod(3,2))
(1, 1)
#4.abs(x)返回绝对值或复数的模
print(abs(-520))
print(abs(1+2j))
520 2.23606797749979
#5.类型转换
#int('520')、int(9.99)转换成整数
#float('3.14')、float(520)转换成浮点数
#complex('1+2j')转换成复数
#6.幂运算
#pow(x,y)与x**y等价计算x的y次方
#pow(x,y,z)与x**y%z等价
当字符串为空时,返回False,哪怕只有一个空格也返回True,整数、浮点数为0时返回False,其他非零值返回True
#逻辑运算and、or、not
print(3<4 and 5>3)
True
print(3 and 4)
print(3 or 4) #短路逻辑核心思想:只有当第一个操作数无法确定逻辑运算结果时,才对第二个操作数进行求值
4 3
优先级问题not优先级最高,其次是and,最后是or
if语句来实现分支结构,主要有五种语法结构
#第一种
if 2+3==5:
print("True")
True
#第二种
if "小甲鱼" == "小姐姐":
print("小甲鱼是小姐姐")
else:
print("小甲鱼不是小姐姐")
小甲鱼不是小姐姐
#第三种
score = input("请输入你的分数:")
score = int(score)
if 0 <= score < 60:
print("D")
elif 60 <= score < 80:
print("C")
elif 80 <= score < 90:
print("B")
elif 90 <= score < 100:
print("A")
elif score == 100:
print("S")
请输入你的分数:95 A
#第四种
score = input("请输入你的分数:")
score = int(score)
if 0 <= score < 60:
print("D")
elif 60 <= score < 80:
print("C")
elif 80 <= score < 90:
print("B")
elif 90 <= score < 100:
print("A")
elif score == 100:
print("S")
else:
print("请输入一个合法的分数")
请输入你的分数:101 请输入一个合法的分数
#第五种
#第五种比较炫酷,语法格式为:条件成立时执行的语句 if conditions else 条件不成立时执行的语句
age = 16
print("未满18岁禁止访问") if age<18 else print("欢迎~")
未满18岁禁止访问
除此之外,条件语句if也支持嵌套
love = 'yes'
while love == 'yes':
love = input("今天你还爱我吗?")
今天你还爱我吗?yes 今天你还爱我吗?yes 今天你还爱我吗?yes 今天你还爱我吗?no
break语句和continue语句的区别是:break是跳出循环,continue是跳过本轮循环
#循环里面的else语句,当循环语句不再为真时,就执行else语句里面的内容
i = 1
while i<5:
print("循环里i的值为",i)
if i == 2:
break
i += 1
else:
print("循环外i的值为",i) #这里不会执行else里面的内容,因为跳出循环时,条件仍为真
循环里i的值为 1 循环里i的值为 2
i = 1
while i<5:
print("循环里i的值为",i)
i += 1
else:
print("循环外i的值为",i) #当循环语句不再为真时,就执行else语句里面的内容
循环里i的值为 1 循环里i的值为 2 循环里i的值为 3 循环里i的值为 4 循环外i的值为 5
else实质性的作用是:可以非常容易的检测循环的退出状况
#嵌套循环实现九九乘法表的打印
i = 1
while i <= 9:
j = 1
while j <= i:
print(j,'*',i,'=',j*i,end = ' ')
j += 1
print()
i += 1
1 * 1 = 1 1 * 2 = 2 2 * 2 = 4 1 * 3 = 3 2 * 3 = 6 3 * 3 = 9 1 * 4 = 4 2 * 4 = 8 3 * 4 = 12 4 * 4 = 16 1 * 5 = 5 2 * 5 = 10 3 * 5 = 15 4 * 5 = 20 5 * 5 = 25 1 * 6 = 6 2 * 6 = 12 3 * 6 = 18 4 * 6 = 24 5 * 6 = 30 6 * 6 = 36 1 * 7 = 7 2 * 7 = 14 3 * 7 = 21 4 * 7 = 28 5 * 7 = 35 6 * 7 = 42 7 * 7 = 49 1 * 8 = 8 2 * 8 = 16 3 * 8 = 24 4 * 8 = 32 5 * 8 = 40 6 * 8 = 48 7 * 8 = 56 8 * 8 = 64 1 * 9 = 9 2 * 9 = 18 3 * 9 = 27 4 * 9 = 36 5 * 9 = 45 6 * 9 = 54 7 * 9 = 63 8 * 9 = 72 9 * 9 = 81
先认识一下与for出生入死的好兄弟range
#range的语法结构如下:
#range(stop) #生成0到stop-1的序列
#range(start,stop) #生成start到stop-1的序列
#range(start,stop,step) #生成start到stop-1的序列,并且以step为跨度
#找出2到10之间所有的素数
for i in range(2,11):
for j in range(2,i):
if i%j == 0:
print(i,'=',j,'*',i//j)
break
else:
print(i,'是素数') #循环里面的else语句,当循环语句不再为真时,就执行else语句里面的内容
2 是素数 3 是素数 4 = 2 * 2 5 是素数 6 = 2 * 3 7 是素数 8 = 2 * 4 9 = 3 * 3 10 = 2 * 5
a = [1,2,3,4,5,'上山打老虎']
for i in a:
print(i)
1 2 3 4 5 上山打老虎
#访问列表中的元素可以使用下标索引,-1代表倒数第一个元素,-2是倒数第二个以此类推
#内置函数len()可以返回列表长度
print(len(a))
print(a[-1])
6 上山打老虎
#a[0:3]访问下标索引为0、1、2的元素
#a[3:6]访问下标索引为3、4、5的元素
#a[:3]访问0到2的元素
#a[3:]访问3到结束的元素
#a[:]访问整个列表
#a[0:6:2]访问0到5且间隔为2的元素
#a[::-1]倒序输出列表
#1.增
heros = ["钢铁侠","绿巨人"]
#增加一个元素
heros.append("黑寡妇") #切片也可以实现这句代码,等价于heros[len(heros):] = ["黑寡妇"]
#增加一个可迭代对象,extend()方法的参数必须是一个可迭代对象,新的内容追加到原列表最后一个元素后面
heros.extend(["雷神","鹰眼"]) #切片也可以实现这句代码,等价于heros[len(heros):] = ["雷神","鹰眼"]
#在任意位置插入一个元素使用insert()方法
s = [1,3,4,5]
s.insert(1,2) #第一个参数指定待插入的位置,第二个为待插入的元素
s.insert(0,0) #相当于在列表开头插入元素
s.insert(len(s),6) #相当于在列表末尾插入元素
#2.删
heros = ['钢铁侠', '绿巨人', '黑寡妇', '雷神', '鹰眼']
#使用remove()方法,删除指定元素,如果列表中有多个匹配的元素,只会删除下标最小的那个
heros.remove("钢铁侠")
#如果要删除的元素压根不存在就会报错
#heros.remove("喜羊羊")会报错
heros = ['钢铁侠', '绿巨人', '黑寡妇', '雷神', '鹰眼']
#使用pop()方法,删除指定位置上的元素
heros.pop(2) #黑寡妇就被踢出来了
#使用clear()方法清空列表
heros.clear() #列表就变成了一个空列表
#3.改
heros = ['钢铁侠', '绿巨人', '黑寡妇', '雷神', '鹰眼']
#直接用索引就能实现改
heros[0] = "蜘蛛侠"
#也能利用切片
heros[:3] = ["武松","林冲","宋江"] #前三个元素就被改了
nums = [1,3,3,5,8,9,6]
nums.sort()
#sort()方法,实现从小到大排序,有两个参数可以指定,key为一个函数的名字可以实现按什么方式排序,reverse默认为False
#reverse = True时,实现从大到小排序
nums.reverse()
#reverse()方法实现逆序
#4.查
#使用count()方法,可以查看一个列表的某个元素出现了几次
nums = [1,8,8,0,3,4,2,5,5,0,4]
nums.count(0) #查找0这个元素在列表中出现了几次,返回为2
#使用index()方法,可以查看某个元素的索引值,如果有多个元素匹配返回下标索引最小的
nums.index(0) #返回为3
#index()方法还有两个可选参数,index(x,start,end)指定开始查找到结束查找的位置
#使用copy()方法实现python列表的浅拷贝
nums_copy1 = nums.copy()
nums_copy2 = nums[:] #切片也可实现同样的效果,和上面那行代码实现的效果是一模一样的
#列表的加法实际上就是拼接
s = [1,2,3]
t = [4,5,6]
s + t
[1, 2, 3, 4, 5, 6]
#列表的乘法就是重复列表内所有元素若干次
s = [1,2,3]
s * 3
[1, 2, 3, 1, 2, 3, 1, 2, 3]
mat = [[1,2,3],
[4,5,6],
[7,8,9]]
#嵌套列表就是一个矩阵,可以使用嵌套循环来访问所有数据
for each in mat:
for i in each:
print(i,end = ' ')
print()
1 2 3 4 5 6 7 8 9
#循环语句创建并初始化一个二维列表
A = [0] * 3
for i in range(3):
A[i] = [0] * 3
B = [[0]*3]*3
# B = [[0]*3]*3 这种做法是错误的而且错的很高级,这样做B的3个元素(即3行)都是同一个对象
#可以用is运算符来判断是否为同一个对象
is运算符又称为同一性运算符,用于判断两个变量是否指向同一个对象
x = "python"
y = "python"
x is y
True
x = [1,2,3]
y = [1,2,3]
x is y
False
之所以会出现上面这种情况,是因为python对于不同对象的存储机制是不同的
A[0] is A[1]
False
B[0] is B[1]
#返回为True,这就解释了为什么B[0][0] = 100 会出现牵一发而动全身的现象
True
先说一下什么叫引用
x = [1,2,3]
y = x #以后对x操作,y也会产生同样的变化,这就是引用,x和y都指向同一个对象
浅拷贝:
#前面说到浅拷贝:列表的copy()方法或者切片操作
x = [1,2,3]
y = x.copy() #等价于y = x[:]
#这时x与y是独立的,copy()方法拷贝的是整个列表对象,而不仅仅是对象的引用
#但是仅仅是在一维列表的时候不会出现问题,当出现嵌套列表时浅拷贝的问题就出现了
#浅拷贝还有第三种实现方式:调用copy模块的copy()函数
#以上代码还等价于:
#import copy
#y = copy.copy(x)
import copy
x = [[1,2,3],[4,5,6],[7,8,9]]
y = copy.copy(x)
x[1][1] = 0 #此时y会随着x的改变而改变,这是因为浅拷贝只是拷贝了外层对象,如果包含嵌套对象,那么拷贝的只是其引用
深拷贝:
x = [[1,2,3],[4,5,6],[7,8,9]]
y = copy.deepcopy(x)
x[1][1] = 0 #深拷贝会拷贝每一层嵌套里面的数据,x[1][1] = 0 y不为所动
#列表推导式语法为:[each的表达式 for each in 可迭代对象]
a = [1,2,3]
b = [i*2 for i in a] #实现每个元素都乘以2,当然for循环也能实现,但其效率远远不如列表推导式
#利用列表推导式来创建和初始化一个二维列表
A = [[0]*3 for i in range(3)] #搞定
for i in A:
for j in i:
print(j,end = ' ')
print()
0 0 0 0 0 0 0 0 0
列表推导式还有更高阶的玩法1.带有条件的列表推导式2.嵌套列表推导式
#1.带有条件的列表推导式
#语法为:[each的表达式 for each in 可迭代对象 if conditions],先执行for里面内容,再执行condition,最后执行表达式
#例如:找出一个列表中F开头的单词
words = ["Fishc","Hello","python","Fantastic"]
fwords = [i for i in words if i[0] == 'F']
for each in fwords:
print(each)
Fishc Fantastic
#2.嵌套列表推导式
#语法为:[表达式 for target1 in 可迭代对象1 for target2 in 可迭代对象2 ]
#例如:将一个二维列表展开为一维
a = [[1,2,3],[4,5,6],[7,8,9]]
b = [col for row in a for col in row] #外层循环在前面,内层循环在后面
#3.列表推导式终极语法:上面两者的结合
#[表达式 for target1 in 可迭代对象1 if 条件1 for target2 in 可迭代对象2 if 条件2...]
#元组的创建
a = (1,2,3,4,5) #与列表不同的是,创建元组使用的是圆括号,也可以不带括号 a = 1,2,3,4,5
#元组也可以通过下标来获取元素
print('a[1] = ',a[1])
#元组是不可修改的,试图修改元组内容的行为是会报错的,例如:
#a[0] = 100
#和列表一样,元组也支持切片操作
#元组只有查字可讲count()方法和index()方法
nums = (3,1,9,4,8,3,5,3)
print('nums.count(3)返回值为',nums.count(3))
heros = ("蜘蛛侠","雷神","鹰眼","绿巨人")
print('heros.index("雷神")返回值为',heros.index("雷神"))
#加号和乘号两个运算符也是可以使用的
#s = (1,2,3)
#t = (4,5,6)
#s + t == (1,2,3,4,5,6)拼接
#s * 3 == (1,2,3,1,2,3,1,2,3)重复
#元组也支持嵌套
#例如:w = (s,t)
#元组还支持迭代,是一个可迭代对象,对于嵌套的元组就要使用嵌套的循环来访问它
#与列表推导式相类似的写法,可以得到一个生成器,这可不是元组推导式,事实上元组推导式并不存在
a[1] = 2 nums.count(3)返回值为 3 heros.index("雷神")返回值为 1
#如何生成只有一个元素的元组
#应该这样写x = (520,),而不是 x = (520),这样的话type(x)会是int型
#打包和解包,这种方式不仅适用于元组,列表和字符串也是可以的,例如:
x,y,z = (1,2,3)
print(x,y,z)
x,y,z = [10,20,30]
print(x,y,z)
x,y,z = 'abc'
print(x,y,z)
#赋值号左侧的变量名数量和右侧必须是一致的否则会报错
#例如x,y,z = 'abcde',会报错,除非使用一个小技巧x,y,*z = 'abcde',加一个星号,此时z的值为'cde'
#其实python引以为傲的多重赋值,其本质就是打包和解包
#x,y = 1,2 #上面提到,元组的创建可以省去圆括号
#元组真的就固若金汤不可修改吗?未必,如果元组里面的元素指向一个可以修改的列表那么它就可以被修改,例如:
#a = ([1,2,3],[4,5,6],[7,8,9])
#可以通过a[1][1] = 0来进行修改
1 2 3 10 20 30 a b c
python的字符串是十分强大的,例如:判断一个数是否为回文数,只需一行代码即可实现
#判断一个数是否为回文数
x = '12321'
"yes" if x == x[::-1] else "no"
'yes'
#1.capitalize()方法,将字符串第一个字母大写,其他全部小写
x = "I Love FishC"
x.capitalize() #注意:它是返回一个全新的字符串,而不是改变原来的字符串
'I love fishc'
#2.casefold()方法,返回一个所有字母都是小写的字符串
x = "I Love FishC"
x.casefold()
'i love fishc'
#3.title()方法,将字符串中每个单词的首字母变为大写,其他字母都为小写
x = "I Love FishC"
x.title()
'I Love Fishc'
#4.swapcase()方法,将字符串中所有的字母大小写反转
x = "I Love FishC"
x.swapcase()
'i lOVE fISHc'
#5.upper()方法,是将字符串中所有字母都变为大写
x = "I Love FishC"
x.upper()
'I LOVE FISHC'
#6.lower()方法,是将字符串中所有字母都变为小写,它与casefold()方法的不同之处是lower()方法只能处理英文字母
x = "I Love FishC"
x.lower()
'i love fishc'
#一共有四个方法,前三个方法都有一个叫width的参数和fillchar的参数,第四个方法一般只有一个width参数
#width参数,设置字符串长,如果它的值小于等于原字符串,直接原字符串输出
#fillchar参数,指定填充的字符
#1.center(),左右用空格去填充,原字符串居中
#2.ljust(),实现左对齐
#3.rjust(),实现右对齐
#4.zfill(),是用0填充左侧,在做数据报表的时候比较实用,它也会机智的去处理带负号的情况
#1.count(sub,start,end),查找子字符串sub在原字符串中出现的次数,start和end用于指定查找的范围对应索引start到end-1
x = "上海自来水来自海上"
x.count('海',0,5)
1
#2.find(sub,start,end),返回从左往右第一个出现子字符串的索引下标值
x.find('海')
1
#3.rfind(sub,start,end),返回从右往左第一个出现子字符串的索引下标值
x.rfind('海')
7
#4.index(sub,start,end)
#5.index(sub,start,end)
#它们两个的用法与find()方法是十分类似的,区别是如果找不到子字符串,find()返回的是-1,而index()会抛出异常
#1.expandtabs(tabsize)将一段字符串中的Tab替换为几个空格,其中参数指定替换空格的个数
#2.replace(old,new,count = -1),将所有old参数指定的子字符串替换为new,count参数为替换的次数,不指定则替换全部
"在吗,我在你家楼下".replace('在吗','想你')
'想你,我在你家楼下'
#3.translate()方法,返回一个根据table参数转换后的新字符串
table = str.maketrans("ABCDEFG","1234567") ##先使用maketrans()方法来制作一个转换表格
"I Love FishC".translate(table)
'I Love 6ish3'
#maketrans()还支持第三个参数,表示将指定的字符串给忽略,示例如下:
table = str.maketrans("ABCDEFG","1234567","Love")
"I Love FishC".translate(table)
'I 6ish3'
子字符串匹配
#1.startswith(prefix,start,end)方法,用于判断prefix指定的子字符串是否出现在原字符串的起始位置
#start,end用于指定开始和结束匹配的位置
x = "我爱python"
print(x.startswith("我"))
#2.endswith(suffix,start,end)方法,用于判断suffix指定的子字符串是否出现在原字符串的结束位置
print(x.endswith("python"))
#除此之外,这两个方法的第一个参数还支持以元组的方式传入,表示多个待匹配参数
x = "她爱python"
if x.startswith(("你","我","她")):
print("匹配成功")
True True 匹配成功
判断字符串构成的方法
#1.istitle()方法,用于判断字符串中所有单词是否都是以大写字母开头,其余字母均为小写
x = "I love Python"
print(x.istitle())
#2.isupper()方法,判断字符串中是否所有字母均为大写
print(x.isupper())
#3.islower()方法,判断字符串中是否所有字母均为小写
print(x.islower())
#4.isalpha()方法,判断一个字符串是否只由字母构成
print(x.isalpha())
#5.isspace()方法,判断一个字符串是否为空白字符串,空白字符串包括空格,Tab,\n等转义字符
x = ' \n\t'
print(x.isspace())
#6.isprintable()方法,判断一个字符串是否是可打印的,里面如果包含转义字符则不可打印
x = 'I love Python\n'
print(x.isprintable())
False False False False True False
判断数字的方法
#1.isdecimal()方法
#2.isdigit()方法 e = "2²"可以判断为True
#3.isnumeric()方法
#以上三个方法都是判断字符串是否由数字构成,只是有的时候尺度不同,isnumeric()方法尺度很大
a = "12345"
b = "一二三四五"
c = "ⅠⅡⅢⅣⅤ"
d = "壹贰叁肆伍"
e = "2²"
print(a.isnumeric())
print(b.isnumeric())
print(c.isnumeric())
print(d.isnumeric())
print(e.isnumeric())
True True True True True
一个集大成者
#isalnum()方法,只要isalpha()、isdecimal()、isdigit()和isnumeric()方法任意一个返回True,它就返回True
判断一个字符串是否为一个合法的python标识符,以及判断是否为python的关键字
#1.isidenyifer()方法,用于判断一个字符串是否为一个合法的python标识符
x = "I love python"
y = "I_love_python"
print(x.isidentifier())
print(y.isidentifier())
#2.判断是否为python的关键字,可以使用keyword模块里的iskeyword()函数
import keyword
print(keyword.iskeyword("if"))
print(keyword.iskeyword("py"))
False True True False
#1.lstrip()方法,去掉左侧的空白
print(' 左侧不要留白'.lstrip())
#2.rstrip()方法,去掉右侧的空白
print('右侧不要留白 '.rstrip())
#3.strip()方法,去掉左右侧空白
print(' 左右都不留白 '.strip())
#其实上面三个方法是有一个chars参数的,默认为Null,表示删除空格
print('www.ilovefishC.com'.lstrip('www.')) #这里传入的虽然是一个字符串,但是它是按照单个字符匹配的
print('www.ilovefishC.com'.rstrip('com.')) #只要匹配到其中任何一个字符,都会把它剔除掉
#4.removeprefix(prefix)方法,删除指定的前缀
print('www.ilovefishC.com'.removeprefix('www.'))
#5.removesuffix(suffix)方法,删除指定的后缀
print('www.ilovefishC.com'.removesuffix('.com'))
#4和5两个方法匹配的是参数指定的整个字符串,而不是单个字符
左侧不要留白 右侧不要留白 左右都不留白 ilovefishC.com www.ilovefishC ilovefishC.com www.ilovefishC
拆分方法
#1.partition(sep)方法
#2.rpartition(sep)方法
'www.ilovefishC.com'.partition('.')
#以上两个方法都是将字符串以参数指定的分割符进行分割,最后返回一个三元组
#partition()是从左到右找指定的分割符,找到后一刀切,返回左半部分、分割符、右半部分组成的三元组
#rpartition()是从右到左找
('www', '.', 'ilovefishC.com')
#3.split(sep,maxsplit = -1)方法
#不指定sep参数就会按照空格分割,最后返回一个列表
#maxsplit参数默认是-1,就是遇到分隔符就切,如果指定这个参数,就会按它作为分割的次数
'苟日新 日日新 又日新'.split()
#'苟日新,日日新,又日新'.split(',')
#4.同样,还有rsplit(sep,maxsplit = -1)方法
['苟日新', '日日新', '又日新']
#5.splitlines(keepends = False)方法,将字符串按行进行分割,将结果以列表方式返回
#keepends参数默认False,指定结果是否包含换行符,如果是True,换行符就会被带到上一个元素里面去
'苟日新\n日日新\r\n又日新'.splitlines(keepends = True)
['苟日新\n', '日日新\r\n', '又日新']
拼接方法
#join(iterable)方法
'.'.join(["www","ilovefishC","com"]) #'.'.join(("www","ilovefishC","com")),用一个元组包裹也没问题
'www.ilovefishC.com'
#使用join()方法实现字符串的加法
#与普通字符串加法不同的是效率问题,join()方法的拼接效率很高
"".join(["Fishc","Fishc"])
'FishcFishc'
format()方法的使用示例如下:
"1+1={} 2的平方是{}".format(1+1,2*2)
'1+1=2 2的平方是4'
#使用{}里面的数字,来表示要使用哪一个参数
"{1}看到{0}就很激动".format("小甲鱼","漂亮的小姐姐")
'漂亮的小姐姐看到小甲鱼就很激动'
#同一个索引值也是可以被使用多次的
"{0}{0}{1}{1}".format('风','雨')
'风风雨雨'
#也可以使用关键字索引
"{name}爱{fav}".format(name = '我',fav = 'python')
'我爱python'
#位置参数可以与关键字参数组合使用
"我叫{name},我爱{0},喜欢{0}的人,运气都不会太差".format("python",name = '小甲鱼')
'我叫小甲鱼,我爱python,喜欢python的人,运气都不会太差'
#如何单纯的输出一对花括号呢?
#1.一般方法
#"{},{},{}".format('1','{}','2')
#2.使用花括号注释花括号
#"{},{{}},{}".format('1','2')
格式化选项语法:
1.align有四个可选参数,<强制字符串在可用空间内左对齐,>强制字符串在可用空间内右对齐,^强制字符串在可用空间内居中,=强制将填充放置在符号之后数字之前(这适合以"+00000520"的形式打印字符串)
2.width前面的0表示为数字类型启用感知正负号的0填充效果,当format后面的参数是字符串的时候会报错
3.fill用于指定填充符号
4.符号类型sign,这个选项仅对数字类型有效,有三个值:'+'在正数前添加正号,负数前添加负号;'-'只在负数前添加负号,默认行为;'空格'在正数前添加空格,负数前添加负号
5.grouping_option千分位分割符,仅对数字类型有效
6..precision精度,对于不同类型的参数,它的效果是不一样的。如果是f类型的浮点数,是限定小数位后显示多少数位;如果是g类型的浮点数,是限定小数点前后一共显示多少位数;对于非数字类型,限定最大字段的大小;对于整型数据,不允许使用这个选项
7.type类型,b将参数以二进制形式输出;c将参数以Unicode字符形式输出;d将参数以十进制形式输出;o将参数以八进制形式输出;x和X将参数以十六进制形式输出;n跟d类似不同之处在于它会使用当前语言环境设置的分隔符插入到适合的位置;none跟d一样;e和E将参数以科学计数法的形式输出;f和F将参数以定点表示法的形式输出(默认为6精度);g和G小数以f形式输出、大数以e形式输出;%将参数以%形式输出(默认精度为6个小数位)
8.#选项作用:参数以二进制、八进制、十六进制输出时,它会自动追加一共前缀
#align为^、width为10
"{:^10}".format(250)
' 250 '
#:是必须的,它的左边是位置参数或关键字参数,右边是格式化选项
"{1:>10}{0:<10}".format(520,250)
' 250520 '
#width前面的0表示为数字类型启用感知正负号的0填充效果,当format后面的参数是字符串的时候会报错
"{:010}".format(-520)
'-000000520'
#fill用于指定填充符号
"{:%^10}".format(520)
'%%%520%%%%'
#符号类型sign,这个选项仅对数字类型有效
"{:+}{:-}".format(520,-520)
'+520-520'
#grouping_option千分位分割符,仅对数字类型有效
"{:,}".format(12345678)
'12,345,678'
precision精度示例:
"{:.2f}".format(3.1415)
'3.14'
"{:.2g}".format(3.1415)
'3.1'
"{:.6}".format("I love FishC")
'I love'
"{:.2%}".format(0.98)
'98.00%'
type类型示例:
"{:b}".format(80)
'1010000'
"{:c}".format(80)
'P'
"{:d}".format(80)
'80'
"{:o}".format(80)
'120'
"{:x}".format(80)
'50'
"{:e}".format(123456789)
'1.234568e+08'
"{:g}".format(123456789)
'1.23457e+08'
"{:g}".format(12345.6789)
'12345.7'
"{:%}".format(0.98)
'98.000000%'
"{:#b}".format(80)
'0b1010000'
"{:#o}".format(80)
'0o120'
"{:#X}".format(80)
'0X50'
更灵活的玩法:通过关键字参数来设置格式化选项的值
"{:.{prec}f}".format(3.1415,prec = 2)
'3.14'
"{:{fill}{align}{width}.{prec}{type}}".format(3.1415,fill = '+',align = '^',width = 10,prec = 2,type = 'f')
'+++3.14+++'
f字符串format()方法的一个语法糖,只需在字符串前面加上一个f或者F即可。但要注意f字符串是python3.6以上的版本才有的,低于这个版本如果使用会报错
f"{3.1415:.2f}"
'3.14'
f"{3.1415:+^10.2f}"
'+++3.14+++'
前面学到的列表、元组、字符串都属于序列。序列可分为可变序列(列表)和不可变序列(元组、字符串)。但一般序列都有以下共同点:
#1.都可以通过索引获取元素
#2.第一个元素的索引下标都为0
#3.都可以通过切片来获取一个范围
#4.都有许多共同的运算符
共同运算符: +和*,加号表示拼接,乘号表示重复
拓展一下:1.id()用于查看一个对象的id值,内存中每一个对象都有一个唯一的标识符叫id,对象都有三个部分组成:id、类型和值 2.is和is not运算符,判断两个对象是否为同一个id。3.in和not in运算符,判断的是包含问题,判断某个元素是否包含着某个序列内。4.del语句用于删除一个指定的对象、还可以删除可变序列中的指定元素
#del语句示例
x = [1,2,3,4,5]
del x #直接删除x这个对象
x = [1,2,3,4,5]
del x[:] #删除x里面的所有元素
x = [1,2,3,4,5]
del x[1:4] #删除索引下标为1、2、3的元素,切片大法也可实现等价于x[1:4] = []
#del语句也可以完成切片大法搞不了的事
x = [1,2,3,4,5]
del x[::2] #x = [::2] = []是会报错的
1.列表、元组、字符串相互转换的函数list()、tuple()、str(),分别将一个可迭代对象转换成一个列表、元组、字符串
2.min()、max()函数,返回最小或者最大值,有一个default参数,当传入一个空对象时,返回default指定的值,而不是报错
x = []
min(x,default = '屁~啥都没有!')
'屁~啥都没有!'
3.len()函数,返回一个对象的长度
4.sum(start)函数,求和。start参数指定求和计算的初始值
x = [1,2,3,4,5]
print(sum(x,start = 10))
25
5.sorted()函数,注意区别列表的sort()方法!此外sorted()函数还支持两个参数,key(可以指定key为一个函数的名称,进而指定排序的方法),reverse(默认为False,执行从小到大)
x = [1,2,3,0,6]
sorted(x) #与列表sort()方法不同的是,sorted()函数返回的是一个全新的列表原来的列表不会受到影响
[0, 1, 2, 3, 6]
#这里演示一下key参数的使用
x = ["FishC","Apple","book","Banana","pen"]
sorted(x)
['Apple', 'Banana', 'FishC', 'book', 'pen']
x = ["FishC","Apple","book","Banana","pen"]
sorted(x,key = len)
#这里就按照len()函数的方式进行排序了,在比较前将每一个元素调用这个len()函数,将返回值进行比较
['pen', 'book', 'FishC', 'Apple', 'Banana']
#另外注意,列表的sort()方法只支持列表排序,而sorted()函数支持任何可迭代对象
6.reversed()函数,注意其返回值是一个反向迭代器,可以用list()函数将它转换成列表
x = [1,2,5,8,0]
reversed(x)
<list_reverseiterator at 0x1d5d8c9d930>
list(reversed(x))
[0, 8, 5, 2, 1]
7.all()函数判断可迭代对象中是否所有元素都为真。any()函数判断可迭代对象中是否存在元素为真。
8.enumerate()函数用于返回一个枚举对象,它的功能是将可迭代对象中的每一个元素及从0开始的序号,共同构成一个二元组的列表。注意得到的是一个枚举对象,要通过list()函数转化一下。还有一个start参数的值用于指定序号开始的值。
season = ["Spring","Summer","Fall","winter"]
list(enumerate(season,start = 1))
[(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'winter')]
9.zip()函数用于创建一个聚合多个可迭代对象的迭代器,它会将作为参数传入的每个可迭代对象的每个元素依次组合成元组,即第i个元组包含每个参数的第i个元素
x = [1,2,3]
y = [4,5,6]
z = [7,8,9]
zipped = zip(x,y,z)
list(zipped)
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]
#传入的可迭代对象的长度不一致时,会以最短的那个为准
x = [1,2,3]
y = [4,5,6]
z = "fishc"
zipped = zip(x,y,z)
list(zipped)
[(1, 4, 'f'), (2, 5, 'i'), (3, 6, 's')]
#如果非要在意那个被丢掉的元素,可以使用itertools模块的zip_longest()函数
import itertools
x = [1,2,3]
y = [4,5,6]
z = "fishc"
zipped = itertools.zip_longest(x,y,z)
list(zipped)
[(1, 4, 'f'), (2, 5, 'i'), (3, 6, 's'), (None, None, 'h'), (None, None, 'c')]
10.map()函数会根据提供的函数对指定的可迭代对象的每个元素进行运算,并返回运算结果的迭代器
mapped = map(ord,"FishC")
list(mapped)
[70, 105, 115, 104, 67]
#如果指定的函数需要多个参数,我们只需对应的修改后面提供的可迭代对象的数量即可
mapped = map(pow,[1,2,3],[1,2,3])
list(mapped)
[1, 4, 27]
#如果后面提供的几个可迭代对象的长度不一致,会以短的为准
mapped = map(pow,[1,2,3,4],[1,2,3,4,5])
list(mapped)
[1, 4, 27, 256]
11.filter()函数会根据提供的函数对指定的可迭代对象的每个元素进行运算,并将运算结果为真的函数,以迭代器的形式返回
list(filter(str.islower,"FishC"))
#所以这也是为什么这个函数叫过滤器的原因
['i', 's', 'h']
拓展内容(迭代器与可迭代对象的关系):一个迭代器肯定是一个可迭代对象,两者最大的区别就是:可迭代对象可以重复操作,而迭代器是一次性的
#示例如下:
mapped = map(ord,"FishC")
for each in mapped:
print(each)
list(mapped)
70 105 115 104 67
[]
#iter()函数,可以将一个列表、元组、字符串转换成一个迭代器
x = [1,2,3,4,5]
y = iter(x)
print(type(y))
<class 'list_iterator'>
next()函数,可以将迭代器里面的内容逐个提取出来,当提取完的时候如果再调用,就会抛出一个异常,如果不希望抛出异常可以使用next()函数的第二个参数
x = [1,2,3,4,5]
y = iter(x)
for i in range(6):
print(next(y,"没有了,已经被掏空了"))
1 2 3 4 5 没有了,已经被掏空了
在映射类型数据的获取上,字典的效率是远远快于列表的。
经典的字典的创建方式是:dict = {键1:值1,键2:值2...}
后续通过指定一个不存在于字典中的键,就可以创建新的键值对了
#下面是字典创建的六种方法
a = {"吕布":"口口布","关羽":"关习习","刘备":"刘Baby"} #经典方法
b = dict(吕布 = "口口布",关羽 = "关习习",刘备 = "刘Baby") #注意键不要加""
c = dict([("吕布","口口布"),("关羽","关习习"),("刘备","刘Baby")]) #传入的是一个列表,列表的元素是键值二元组
d = dict({"吕布":"口口布","关羽":"关习习","刘备":"刘Baby"}) #无病呻吟型
e = dict({"吕布":"口口布","关羽":"关习习"},刘备 = "刘Baby") #混合拳法
f = dict(zip(["吕布","关羽","刘备"],["口口布","关习习","刘Baby"]))#使用zip()函数
print(a == b == c == d == e == f) #它们是等价的
True
1.增
#使用字典的fromkeys(iterable,value)方法,传入的iterable参数作为键,键的值全部初始化成value指定的数
d = dict.fromkeys("FishC",250)
print(d)
{'F': 250, 'i': 250, 's': 250, 'h': 250, 'C': 250}
#之后可以通过键来修改对应的值
d['F'] = 70
print(d)
#通过指定一个不存在于字典中的键,就可以创建新的键值对了
d['Q'] = 80
print(d)
{'F': 70, 'i': 250, 's': 250, 'h': 250, 'C': 250} {'F': 70, 'i': 250, 's': 250, 'h': 250, 'C': 250, 'Q': 80}
2.删
#使用字典的pop(key)方法删除指定的键值对,它返回的是删除键所对应的值
d = dict.fromkeys("FishC",250)
print(d)
d.pop('F')
print(d)
{'F': 250, 'i': 250, 's': 250, 'h': 250, 'C': 250} {'i': 250, 's': 250, 'h': 250, 'C': 250}
#如果删除一个不存在的键就会抛出异常,如果不希望抛出异常可以指定default参数,那么返回的就是default指定的值了
d = dict.fromkeys("FishC",250)
print(d)
d.pop('A',"不存在这个键")
{'F': 250, 'i': 250, 's': 250, 'h': 250, 'C': 250}
'不存在这个键'
#python3.7之后字典的元素变有序了
#popitem()方法,删除最后一个加进字典的键值对
d = dict.fromkeys("FishC",250)
print(d)
d.popitem()
print(d)
{'F': 250, 'i': 250, 's': 250, 'h': 250, 'C': 250} {'F': 250, 'i': 250, 's': 250, 'h': 250}
#del关键字也可以删除字典的键值对
d = dict.fromkeys("FishC",250)
print(d)
del d['F']
print(d)
{'F': 250, 'i': 250, 's': 250, 'h': 250, 'C': 250} {'i': 250, 's': 250, 'h': 250, 'C': 250}
#clear()方法清空字典
d = dict.fromkeys("FishC",250)
print(d)
d.clear()
print(d)
{'F': 250, 'i': 250, 's': 250, 'h': 250, 'C': 250} {}
3.改
d = dict.fromkeys("FishC")
print(d) #默认下,键的值全是none
#直接指定键,去修改键对应的值
d['F'] = 70
print(d)
#同时修改多个键值对,也可使用update()方法,传入的参数可以是一个字典,也可以是多个键值对
d.update({'i':80,'s':90})
print(d)
d.update(h = 100,C = 110)
print(d)
{'F': None, 'i': None, 's': None, 'h': None, 'C': None} {'F': 70, 'i': None, 's': None, 'h': None, 'C': None} {'F': 70, 'i': 80, 's': 90, 'h': None, 'C': None} {'F': 70, 'i': 80, 's': 90, 'h': 100, 'C': 110}
4.查
#最简单的查找方法就是给它一个键,它返回一个值
d = dict.fromkeys("FishC",0)
d['F']
#但是,当指定的键并不存在时,它就会报错,所以用户体验不加,例如:
#d['a'] 会报错
#使用get(key,default)方法,default可以指定在键不存在时返回的值
d.get('a',"不存在这个键")
'不存在这个键'
#setdefault(key,default)方法,指定的键如果在,就返回其对应的值,如果不在,则创建一个新的键值对
d = dict.fromkeys("FishC",0)
print(d.setdefault('s',80))
print(d.setdefault('a',97))
print(d)
0 97 {'F': 0, 'i': 0, 's': 0, 'h': 0, 'C': 0, 'a': 97}
items()、keys()、values()分别用于获取字典的键值对、键和值的视图对象,视图对象的特性是:当字典被修改时,视图对象同时发生改变
d = dict(zip('FishC',[70,65,42,89,93]))
item = d.items()
key = d.keys()
value = d.values()
print(item)
print(key)
print(value)
d['F'] = 95
print(item)
print(key)
print(value)
dict_items([('F', 70), ('i', 65), ('s', 42), ('h', 89), ('C', 93)]) dict_keys(['F', 'i', 's', 'h', 'C']) dict_values([70, 65, 42, 89, 93]) dict_items([('F', 95), ('i', 65), ('s', 42), ('h', 89), ('C', 93)]) dict_keys(['F', 'i', 's', 'h', 'C']) dict_values([95, 65, 42, 89, 93])
#copy()得到一个字典的浅拷贝
d = dict(zip('FishC',[70,65,42,89,93]))
d_copy = d.copy()
print(d is d_copy) #说明这个浅拷贝得到的d_copy与d是独立的
False
#len()获取键值对的数量
len(d)
5
#也可以用in或者not in来判断一个键是否在字典中
'i' in d
True
#list()得到的是字典键构成的列表
list(d) #相当于list(d.keys())
#如果要得到所有的值构成的列表,应该是list(d.values())
['F', 'i', 's', 'h', 'C']
#iter()函数,将字典的键构成一个迭代器
e = iter(d)
for i in range(6):
print(next(e,"被掏空了"))
F i s h C 被掏空了
一个键的值是一个字典,就实现了字典的嵌套
d = {"吕布":{"语文":60,"数学":70,"英语":80},"关羽":{"语文":70,"数学":80,"英语":90}}
print("吕布的数学成绩是:",d["吕布"]["数学"])
吕布的数学成绩是: 70
#嵌套的也可以是一个列表
d = {"吕布":[60,70,80],"关羽":[70,80,90]}
print("吕布的数学成绩是:",d["吕布"][1])
吕布的数学成绩是: 70
#用字典推导式,将一个字典的键与值的位置互换
d = {"吕布":"口口布","关羽":"关习习","刘备":"刘Baby"}
b = {v:k for k,v in d.items()}
print(b)
{'口口布': '吕布', '关习习': '关羽', '刘Baby': '刘备'}
#当然也可以加上筛选条件
d = {"吕布":"口口布","关羽":"关习习","刘备":"刘Baby"}
b = {v:k for k,v in d.items() if k != "吕布"} #这样吕布就进不来了
print(b)
{'关习习': '关羽', '刘Baby': '刘备'}
#用字典推导式来求字符的编码值
code_box = {i:ord(i) for i in "FishC"}
print(code_box)
{'F': 70, 'i': 105, 's': 115, 'h': 104, 'C': 67}
#还有一个有意思的问题,注意字典已经存在的键赋值会覆盖
d = {x:y for x in [1,3,5] for y in [2,4,6]}
print(d)
{1: 6, 3: 6, 5: 6}
集合最大的特点就是唯一性和无序性
#创建集合一共有三种方法
#1.经典创建方法
a = {"one","two","three"}
print(a)
#2.使用集合推导式
b = {s for s in "FishC"}
print(b)
#3.使用类型构造器
c = set("FishC")
print(c)
{'two', 'one', 'three'} {'h', 'F', 'C', 's', 'i'} {'h', 'F', 'C', 's', 'i'}
由于集合是无序的,所以不能用下标索引的方式去访问它
#可以用in和not in来查看一个元素是否在一个集合
'one' in a
True
#集合也可以作为可迭代对象被访问
for each in a:
print(each)
two one three
#利用集合的唯一性可以轻松的实现去重操作
a = set([8,9,4,0,8,8,6])
print(a)
{0, 4, 6, 8, 9}
#检测一个列表是否存在相同的元素
x = [1,2,3,4,5,1]
"不存在相同的元素" if len(set(x)) == len(x) else "存在相同的元素"
'存在相同的元素'
#copy()方法实现浅拷贝
s = {1,2,3}
t = s.copy()
print(s is t)
False
#isdisjoint()方法判断两个集合有没有交集,有交集返回False
s = set("fishc")
t = set("python")
s.isdisjoint(t) #有'h'这个元素是公共的,因此返回False
#传入一个可迭代对象就可以,不一定参数非得是集合如:s.isdisjoint("python")
False
#检测是否为另一个集合或者可迭代对象的子集,使用issunset()方法
s = set("fishc")
t = "ilovefishc.com"
s.issubset(t)
True
#检测是否为另一个集合或者可迭代对象的超集,使用issuperset()方法
s = set("fishc")
t = 'fish'
s.issuperset(t)
True
#使用union()方法,可以计算和另一个集合的并集,支持多参数
s = set("fishc")
t = set("python")
s.union(t)
{'c', 'f', 'h', 'i', 'n', 'o', 'p', 's', 't', 'y'}
#使用intersection()方法,可以计算和另一个集合的交集,支持多参数
s = set("fishc")
t = {'i','s','c'}
s.intersection(t)
{'c', 'i', 's'}
#使用difference()方法,可以计算差集,A与B差集运算相当于A-B,支持多参数
s = set("fishc")
t = {'i','s','c'}
s.difference(t)
{'f', 'h'}
#求对称差集,对称差集定义为:对于两个集合A和B,先求A和B的并集和交集,由并集减去交集即为对称差集
#使用symmetric_difference()方法
s = set("fishc")
t = set("php")
s.symmetric_difference(t)
{'c', 'f', 'i', 'p', 's'}
python也提供了运算符来实现上述六种方法,<=可以检测子集,<可以检测真子集,>=可以检测超集,>检测真超集,|做并集运算,&做交集运算,-做差集运算,^做对称差集运算。注意使用运算符,运算符两边的数据都必须是集合而不能是其他可迭代对象了。
#创建一个不可变集合
s = frozenset("fishc")
#上面有关方法既适用于set也适用frozenset,因为它们都不会对集合产生改动
#下面介绍几种改动集合的方法,它们仅适用于set
#1.update()方法,采用并集的方式来更新集合
s = set("fishc")
s.update([1,1],'23') #集合s发生变化,所以对frozenset操作就会报错
print(s)
{1, '3', 'i', 'h', '2', 'f', 's', 'c'}
#2.intersection_update()方法
#3.difference_update()方法
#4.symmetric_difference_update()方法
#上面三个分别用于求交集、差集、对称差集,只不过原集合会发生改变,因此全部不适用frozenset
#5.往集合里面添加元素,使用add()方法
s = set("fishc")
s.add('A')
print(s)
{'i', 'h', 'f', 'A', 's', 'c'}
#6.删除集合里面的元素,使用remove()方法、discard()方法
#它们两个的区别是如果删除的元素不存在,remove()会抛出异常,而discard()会静默处理
s = set("fishc")
s.discard('A')
#7.pop()方法,随机从集合里面弹出一个元素
s = set("fishc")
for i in range(2):
print(s.pop())
i h
#8.clear()方法,直接清空集合
s = set("fishc")
s.clear()
print(s)
set()
拓展知识:可哈希,一般来说,不可变的对象是可哈希的,比如:字符串、元组等等,而可变对象是不可哈希的,比如:列表等等。只有可哈希的对象才有资格作为字典的键、集合的元素。
由上可知,集合是不能嵌套的,因为集合也是一个可变的容器,它是不可哈希的。但是如果硬要实现集合的嵌套,只能使用frozenset了。
写一个最简单的函数
def func():
pass
#一个函数没有写返回值它会返回一个None的值
def func():
pass
print(func())
None
我们把python中位置固定的参数称为位置参数
#位置参数,调用时只需要按顺序传递正确的参数即可
def func(s,v,o):
return "".join([o,v,s])
func('我','打了','小甲鱼')
'小甲鱼打了我'
使用关键字参数,只需要知道参数的名字即可,不用管参数的传递顺序
def func(s,v,o):
return "".join([o,v,s])
func(o = '我',v = '打了',s = '小甲鱼')
'我打了小甲鱼'
位置参数和关键字参数混合使用时,要注意顺序,位置参数必须在关键字参数之前
def func(s,v,o):
return "".join([o,v,s])
#func(s="苹果","吃",o="小甲鱼")是错误的
python允许函数在定义的时候指定默认参数,这样在函数调用的时候,如果没有传入实参,就使用默认值
def func(s,v,o = "小甲鱼"):
return "".join([o,v,s])
func(v = '吃',s = '香蕉')
'小甲鱼吃香蕉'
#注意一点,定义函数时,如果有默认参数,必须放在其他参数的后面,下面这种定义方式错误
#def func(s = "苹果",v,o = "小甲鱼"):
# return "".join([o,v,s])
#正确的如下:
def func(v,s = "苹果",o = "小甲鱼"):
return "".join([o,v,s])
拓展知识:在使用help()来查看一个函数时,像abs、sum函数参数列表会出现一个/,这个/代表的意思是:/左侧的参数必须传递位置参数,而不能是关键字参数,而/右侧使用位置参数可以,关键字参数也行。
同样的也有限制只能使用关键字参数的语法,那就是*,星号左侧既可以是位置参数也可以是关键字参数,星号右侧只能是关键字参数
help(abs)
Help on built-in function abs in module builtins: abs(x, /) Return the absolute value of the argument.
#由上可知
#abs(x = -1.5)写法错误
help(sum)
Help on built-in function sum in module builtins: sum(iterable, /, start=0) Return the sum of a 'start' value (default: 0) plus an iterable of numbers When the iterable is empty, return the start value. This function is intended specifically for use with numeric values and may reject non-numeric types.
#sum([1,2,3],start = 10)
#sum([1,2,3],10)
#两种均可
#自己定义的函数也是遵循这个规则的
def func(a,/,b,c):
print(a,b,c)
func(1,2,3) #正确
#func(a=1,2,3)错误
#func(a=1,b=2,c=3)也错误
func(1,b=2,c=3) #正确
1 2 3 1 2 3
#星号左侧既可以是位置参数也可以是关键字参数,星号右侧只能是关键字参数
def func(a,*,b,c):
print(a,b,c)
#func(a=1,2,3)错误
#func(1,2,3)错误
func(a=1,b=2,c=3) #正确
func(1,b=2,c=3) #正确
1 2 3 1 2 3
收集参数其实我们早就见过,想一想我们刚学python接触的print()函数就是一个例子
def func(*args): #这里实际上发生里元组的打包
print(f"传入了{len(args)}个参数")
print(type(args))
print(args)
func("小甲鱼","小姐姐","不二如是")
传入了3个参数 <class 'tuple'> ('小甲鱼', '小姐姐', '不二如是')
#函数返回多个返回值时,利用了元组的打包
def func():
return 1,2,3
x,y,z = func()
print(x,y,z)
1 2 3
如果在收集参数后面还要指定其他参数,就必须要使用关键字参数,因为如果不指定关键字参数,那么这些参数就会被前面的收集参数吃掉。
def func(*args,a,b):
print(args,a,b)
#func(1,2,3,4,5)会报错,说参数不足,实际上这5个参数都被收集参数吃掉了,所以才参数不足
#正确的做法
func(1,2,3,a=4,b=5)
(1, 2, 3) 4 5
知识拓展1:上面星号分割参数列表的本质
#星号左侧既可以是位置参数也可以是关键字参数,星号右侧只能是关键字参数
def func(a,*,b,c):
print(a,b,c)
#本质上说,这个*是一个匿名的收集参数
知识拓展2:除了将参数打包成元组,收集参数其实还可以将参数打包成字典,做法就是使用两个连续的星号。对于这种情况在传递参数的时候就必须要使用关键字参数了。
def func(**kwargs):
print(type(kwargs))
print(kwargs)
func(a=1,b=2,c=3)
<class 'dict'> {'a': 1, 'b': 2, 'c': 3}
#混合起来就更加灵活了
def func(a,*b,**c):
print(a,b,c)
func(1,2,3,4,x=5,y=6)
1 (2, 3, 4) {'x': 5, 'y': 6}
知识拓展3:解包参数
#解包参数
def func(a,b,c,d):
print(a,b,c,d)
args = (1,2,3,4)
kwargs = {'a':1,'b':2,'c':3,'d':4}
#func(args)是不行的,必须加上一个星号
func(*args) #解包元组
func(**kwargs) #解包字典
1 2 3 4 1 2 3 4
1.局部作用域,定义在函数内部的变量仅限在函数内部访问,如果在函数外部访问它,就会报错。
2.全局作用域,定义在函数外部的变量,函数内部可以访问到。但是如果函数内部出现一个同名的局部变量就会发生覆盖,导致两个变量的id不一样,函数内部修改这个局部变量,函数外部的全局变量不发生改变。
#示例如下:
x = 880
def func():
x = 520
print(id(x))
func()
print(x)
print(id(x))
2710528573040 880 2710528570352
x = 880
def func():
print(id(x))
func()
print(id(x))
2710528573392 2710528573392
global语句允许在函数内部修改全局变量的值,示例如下:
x = 880
def func():
global x
x = 520
print(x)
func()
print(x)
#尽管有global语句,但是也不提倡经常使用,因为在函数中去肆意修改全局变量的值有时候会造成难以排查的bug
520 520
3.嵌套函数
#嵌套函数
def funA():
x = 520
def funB():
x = 880
print("In funB x = ",x)
funB()
print("In funA x = ",x)
funA()
#注意:在外层函数的外部是不能直接调用嵌套的内部函数的,例如:直接在外部funB()是会报错的
In funB x = 880 In funA x = 520
#嵌套函数有一个nonlocal语句,允许内部函数去修改外层函数的变量,例如:
def funA():
x = 520
def funB():
nonlocal x
x = 880
print("In funB x = ",x)
funB()
print("In funA x = ",x)
funA()
In funB x = 880 In funA x = 880
拓展知识:LEGB规则:当局部作用域与全局作用域发生冲突时,python会使用局部作用域的变量,除非使用global语句声明;当嵌套函数发生时,局部作用域又会覆盖外层函数的作用域,除非使用nonlocal语句声明。最后一个是B(即BIF内置函数),只要起一个变量名和BIF内置函数的函数名一样就可以把这个内置函数给毁了。
str = "小甲鱼把str()函数给毁了"
#此时,str(520)就不能实现把整型520转换为字符串了
str
'小甲鱼把str()函数给毁了'
引入上面的例子:
def funA():
x = 880
def funB():
print(x)
funB()
funA()
880
如何做到不通过funA()而调用到funB()函数呢?答案如下:
def funA():
x = 880
def funB():
print(x)
return funB #注意将函数作为返回值或者参数的时候是不需要小括号的,只有函数定义或调用的时候才需要小括号
funny = funA()
funny() #这两行代码等价于funA()()
880
这就是闭包,闭包可以做很多事情,比如:利用闭包可以做函数加工厂,示例如下:
def power(exp):
def exp_of(base):
return base**exp
return exp_of
square = power(2)
cube = power(3)
print(square(10))
print(cube(5))
100 125
闭包配合nonlocal语句还能实现角色移动的小游戏,示例如下:
我们利用内层函数能够记住外层函数作用域这个特性,并且使用nonlocal语句,让它可以修改外层函数作用域里面的变量,就可以实现一个带记忆功能的函数。
def outer():
x = 0
y = 0
def inner(step_x,step_y):
nonlocal x,y
x += step_x
y += step_y
print(f"现在位置是:x = {x} y = {y}")
return inner
move = outer()
action = input("出发or结束?")
while action == "出发":
x1 = input("x方向移动多少?")
y1 = input("y方向移动多少?")
x1 = int(x1)
y1 = int(y1)
move(x1,y1)
action = input("出发or结束?")
出发or结束?出发 x方向移动多少?3 y方向移动多少?4 现在位置是:x = 3 y = 4 出发or结束?出发 x方向移动多少?-2 y方向移动多少?2 现在位置是:x = 1 y = 6 出发or结束?结束
函数可以作为另一个函数的参数,传参数的时候只要写函数名即可
先写一个时间管理大师的案例
import time
def time_master(func):
print("开始运行程序")
start = time.time()
func()
print("结束运行程序")
end = time.time()
print(f"程序运行了{(end-start):.2f}秒")
def myfunc():
time.sleep(2)
print("hello")
time_master(myfunc)
开始运行程序 hello 结束运行程序 程序运行了2.00秒
上面的代码,我们每次要知道一个函数的执行时间时都要显式的调用time_master()函数。更好的实现方法是用装饰器,在调用myfunc()时,它能自觉地调用time_master()函数。
import time
def time_master(func):
def call_func():
print("开始运行程序")
start = time.time()
func()
print("结束运行程序")
end = time.time()
print(f"程序运行了{(end-start):.2f}秒")
return call_func
#装饰器的语法糖
#它的作用是在调用myfunc()的时候并不是直接调用myfunc(),而是把myfunc作为参数传入装饰器中,将返回的结果赋值给myfunc
@time_master
def myfunc():
time.sleep(2)
print("hello")
myfunc()
开始运行程序 hello 结束运行程序 程序运行了2.00秒
#上面是装饰器的语法糖,下面写出它的本质
import time
def time_master(func):
def call_func():
print("开始运行程序")
start = time.time()
func()
print("结束运行程序")
end = time.time()
print(f"程序运行了{(end-start):.2f}秒")
return call_func
def myfunc():
time.sleep(2)
print("hello")
myfunc = time_master(myfunc)
myfunc()
开始运行程序 hello 结束运行程序 程序运行了2.01秒
多个装饰器可以用在同一个函数上
def add(func):
def inner():
x = func()
return x+1
return inner
def cube(func):
def inner():
x = func()
return x*x*x
return inner
def square(func):
def inner():
x = func()
return x*x
return inner
@add
@cube
@square #顺序是从下到上
def test():
return 2
print(test())
65
#上面例子的本质是:
def add(func):
def inner():
x = func()
return x+1
return inner
def cube(func):
def inner():
x = func()
return x*x*x
return inner
def square(func):
def inner():
x = func()
return x*x
return inner
def test():
return 2
test = square(test)
test = cube(test)
test = add(test)
print(test())
65
进阶知识:如何给装饰器传递参数?
与没有参数的装饰器相比,有参数的装饰器只是多了一次参数的调用
import time
def logger(msg):
def time_master(func):
def call_func():
start = time.time()
func()
stop = time.time()
print(f"{msg}一共耗费了{(stop-start):.2f}秒")
return call_func
return time_master
@logger(msg='A')
def funA():
time.sleep(1)
print("正在调用funA")
@logger(msg='B')
def funB():
time.sleep(2)
print("正在调用funB")
funA()
funB()
正在调用funA A一共耗费了1.00秒 正在调用funB B一共耗费了2.00秒
#上面例子的本质是:
import time
def logger(msg):
def time_master(func):
def call_func():
start = time.time()
func()
stop = time.time()
print(f"{msg}一共耗费了{(stop-start):.2f}秒")
return call_func
return time_master
def funA():
time.sleep(1)
print("正在调用funA")
funA = logger(msg='A')(funA)
def funB():
time.sleep(2)
print("正在调用funB")
funB = logger(msg='B')(funB)
funA()
funB()
正在调用funA A一共耗费了1.00秒 正在调用funB B一共耗费了2.00秒
语法:lambda arg1,arg2,arg3...: expression
冒号左边是传入的参数,冒号右边是返回值的表达式
#利用lambda来写传统的求平方函数
square = lambda x:x*x
square(6)
36
与传统方式定义的函数不同的是,lambda是一个表达式,可以放在传统函数不可能存在的地方,比如列表里面。
y = [lambda x:x*x,2,3]
y[0](y[1])
4
mapped = map(lambda x:ord(x),"FishC")
list(mapped)
[70, 105, 115, 104, 67]
一般来说,lambda表达式通常用于实现一些简单的功能,而要想定制功能复杂的函数还得依靠传统方式。
#生成器使用yield关键字
def counter():
i = 0
while i<=5:
yield i
i += 1
c = counter()
#这里得到的c就是一个生成器对象,类似于迭代器,生成器也是可迭代对象,也支持next()操作,不走回头路
print(type(c))
for i in range(7):
print(next(c,"掏空了"))
<class 'generator'> 0 1 2 3 4 5 掏空了
#利用生成器来实现斐波那契数列
def func():
i = 1
x = 1
y = 1
while i<=10:
yield x
x,y =y,x+y #两个赋值是同时进行的不分先后
i += 1
list(func())
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
之前提到的把列表推导式的方括号换成圆括号,得到的可不是元组推导式,而是生成器表达式,事实上元组推导式并不存在。
c = (i**2 for i in range(10))
print(type(c))
list(c)
<class 'generator'>
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
综上,得到生成器的两种方法是:1.yield关键字2.生成器表达式
下面给出递归函数的几个实例:
#递归函数实现求一个数的阶乘
def factRecur(n):
if n == 1:
return 1
else:
return n*factRecur(n-1)
factRecur(5)
120
#递归函数求斐波那契数列的第n项
def fibRecur(n):
if n == 1 or n == 2:
return 1
else:
return fibRecur(n-1)+fibRecur(n-2)
fibRecur(12)
144
我们发现:递归的实现逻辑跟文字描述是非常类似的。递归的实现方式非常优雅、实现逻辑非常简单,但要注意退出递归的条件,防止出现无限递归的现象。
另外:每一次调用递归函数,它并不会立刻返回而是等到最底层的那个函数返回,它才会一层一层往上走,这个过程是十分耗费资源的。递归的好处就在于它有着更加简单的实现逻辑,写代码可以偷懒,但是效率不如迭代。
#汉诺塔例子
#1.计算汉诺塔移动次数,输入参数为层数n
def hanoi(n):
if n == 1:
return 1
else:
return 2*hanoi(n-1)+1
hanoi(3)
7
#2.打印汉诺塔的移动方式,输入参数是汉诺塔层数n,A、B、C三根柱子,目标从A移到C
def hanoi(n,x,y,z):
if n == 1:
print(x,"-->",z)
else:
hanoi(n-1,x,z,y)
print(x,"-->",z)
hanoi(n-1,y,x,z)
hanoi(3,'A','B','C')
A --> C A --> B C --> B A --> C B --> A B --> C A --> C
可以利用help()快速查看一个函数的文档
help(print)
#文档里面有函数的参数列表,及各个参数的意义
Help on built-in function print in module builtins: print(...) print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False) Prints the values to a stream, or to sys.stdout by default. Optional keyword arguments: file: a file-like object (stream); defaults to the current sys.stdout. sep: string inserted between values, default a space. end: string appended after the last value, default a newline. flush: whether to forcibly flush the stream.
#如何给自定义的函数编写文档呢?
#函数文档一定是位于函数定义的最顶部,用三引号来包裹
def exchange(dollar,rate = 6.32):
"""
功能:汇率转换,美元-->人民币
参数:
- dollar:美元的数量
- rate:汇率,默认值为6.32
返回值:
- 人民币数量
"""
return dollar*rate
exchange(20)
126.4
#完成函数文档的书写,就可以用help()函数来查看
help(exchange)
Help on function exchange in module __main__: exchange(dollar, rate=6.32) 功能:汇率转换,美元-->人民币 参数: - dollar:美元的数量 - rate:汇率,默认值为6.32 返回值: - 人民币数量
#函数的作者希望传入的s参数是字符串类型、n参数是整型、返回值是字符串类型
def func(s:str,n:int = 3) -> str:
return s*n
func("love")
'lovelovelove'
#但是使用类型注释只不过是表达了函数作者的期望,如果调用者非要胡来,python也不会出面阻止
#例如:
func(5,5)
25
#期望传入的s参数是一个整型列表
def func(s:list[int],n:int = 3) ->list:
return s*n
func([1,2,3])
[1, 2, 3, 1, 2, 3, 1, 2, 3]
#期望传入的字典,键的类型是字符串型,值的类型是整型
def func(s:dict[str,int],n:int = 3) ->list:
return list(s.keys())*n
func({'a':3,'b':4,'c':5},2)
['a', 'b', 'c', 'a', 'b', 'c']
def func(s:str,n:int = 3) -> str:
return s*n
#1.通过.__name__查看函数名
func.__name__
'func'
#2.通过.__annotations__查看类型注释
func.__annotations__
{'s': str, 'n': int, 'return': str}
#3.查看函数文档可以使用.__doc__
def exchange(dollar,rate = 6.32):
"""
功能:汇率转换,美元-->人民币
参数:
- dollar:美元的数量
- rate:汇率,默认值为6.32
返回值:
- 人民币数量
"""
return dollar*rate
exchange.__doc__
'\n 功能:汇率转换,美元-->人民币\n 参数:\n - dollar:美元的数量\n - rate:汇率,默认值为6.32\n 返回值:\n - 人民币数量\n '
#阅读不是很友好可以用print
print(exchange.__doc__)
功能:汇率转换,美元-->人民币 参数: - dollar:美元的数量 - rate:汇率,默认值为6.32 返回值: - 人民币数量
当一个函数接收另一个函数当作参数,那么它就是一个高阶函数,我们学过的map()、filter()、sum()以及装饰器等等都是高阶函数
下面举几个其他高阶函数的例子(它们都来自functools模块):
1.reduce()函数
#reduce()函数,时代的眼泪,python3之后functools模块收留了它
import functools
def add(x,y):
return x+y
functools.reduce(add,[1,2,3,4,5])
#第一个参数是一个函数名,第二个参数是一个可迭代对象
#它做的事是,把可迭代对象的每一个元素依次传入参数指定的这个函数,最终返回累计结果
15
#它做的事相当于
add(add(add(add(1,2),3),4),5)
15
functools.reduce(lambda x,y:x*y,range(1,6))
120
2.偏函数
#与闭包类似
square = functools.partial(pow,exp = 2)
square(5)
25
cube = functools.partial(pow,exp = 3)
cube(5)
125
3.@wraps装饰器
import time
def time_master(func):
def call_func():
print("开始运行程序")
start = time.time()
func()
print("结束运行程序")
end = time.time()
print(f"程序运行了{(end-start):.2f}秒")
return call_func
@time_master
def myfunc():
time.sleep(2)
print("hello")
#前面讲的装饰器其实有一个副作用,那就是对myfunc()内省的时候
myfunc.__name__
'call_func'
import time
import functools
def time_master(func):
@functools.wraps(func) #这时,内省的副作用就没有了
def call_func():
print("开始运行程序")
start = time.time()
func()
print("结束运行程序")
end = time.time()
print(f"程序运行了{(end-start):.2f}秒")
return call_func
@time_master
def myfunc():
time.sleep(2)
print("hello")
myfunc.__name__
'myfunc'
#1.open()函数,它有两个参数,第一个是文件路径及文件名,第二个参数是打开方式
#打开方式有
#'w'表示写入(如果文件之前有内容,会将内容全部清空)
#'r'表示读
#'r+'表示更新,既可以读,又可以写
f = open("test.txt",'w') #成功打开文件后会返回一个文件对象
#2.write()方法,返回写入文件的字符个数
f.write("I love python.\n")
#3.writelines()方法,同时将多个字符串写入文件
f.writelines(["I love fishc.\n","I love my wife"])
#4.只有将文件对象关闭,才能看到写入的东西,相当于保存,关闭文件用close()方法
f.close()
f = open("test.txt",'r+')
#5.可以用readable()和writable()来测试读取和写入功能
f.readable()
True
f.writable()
True
#6.1由于python的文件支持迭代,可以使用for语句对文件进行读取
for each in f:
print(each)
I love python. I love fishc. I love my wife
#6.2读取可以使用read()、readline()、readlines()进行
#read()读取所有
#readline()读取一行
#readlines()读取所有,并把每一行作为一个元素,组成列表返回
f.read()
#啥也没有读到的原因是因为文件内部有一个文件指针,它指向文件的当前位置,当读到一个字符的时候文件指针就会指向下一个字符
#由于上面已经用for语句把文件读完了,故文件指针已经指向EOF了,当然就读不到内容了
''
#7.tell()方法用于追踪文件指针的位置
f.tell()
45
#8.seek()方法修改文件指针的位置
f.seek(0)
0
f.read()
'I love python.\nI love fishc.\nI love my wife'
f.seek(0)
f.readline()
'I love python.\n'
f.seek(0)
f.readlines()
['I love python.\n', 'I love fishc.\n', 'I love my wife']
#9.flush()方法,在不关闭文件的前提下,将写入的东西保存
#f.flush()
#10.truncate()方法,将文件进行截断
#f.truncate(30),文件指针指向30之后的内容会被清空
with语句和上下文管理器为文件操作提供了更优雅的方式
#传统的文件操作方法
f = open("test.txt",'w')
f.write("I love Fishc.")
f.close()
#with语句和上下文管理器实现方法,和上面实现的是同样的效果
with open("test.txt",'w') as f:
f.write("I love python.")
#不再需要手动关闭文件,文件处理代码只需放入with语句的缩进里面即可
#而且使用with语句最大的优势在于:可以确保资源的释放
pickle允许你将字符串、列表、字典等python对象给保存为文件的形式
所谓python对象序列化就是将python对象转换为二进制字节流的过程。主要用了dump和load两个函数。示例如下:
#1.首先是把对象保存为文件
x,y,z = 1,2,3
s = "FishC"
l = ["小甲鱼",520,3.14]
d = {'one':1,'two':2}
import pickle
#要保存为pickle文件,后缀名必须为.pkl
#而且必须是以二进制的方式打开
with open("data.pkl",'wb') as f:
pickle.dump((x,y,z,s,l,d),f) #为了方便这里使用元组打包
#pickle.dump(x,f)
#pickle.dump(y,f)
#pickle.dump(z,f)
#pickle.dump(s,f)
#pickle.dump(l,f)
#pickle.dump(d,f)
#2.读取
with open("data.pkl",'rb') as f:
x,y,z,s,l,d = pickle.load(f) #解包操作
#x = pickle.load(f)
#y = pickle.load(f)
#z = pickle.load(f)
#s = pickle.load(f)
#l = pickle.load(f)
#d = pickle.load(f)
print(x,y,z,s,l,d,sep='\n')
1 2 3 FishC ['小甲鱼', 520, 3.14] {'one': 1, 'two': 2}
#基本的语法格式为:
#try:
# 检测范围
#except:
# 异常处理代码
#示例一
#如果检测出try语句中的异常,就会执行except中的语句
#如果try语句中没有检测出异常,那么except中的语句就不会被执行
try:
1/0
except:
print("出错了")
出错了
#示例二
#可以在except后面指定一个具体的异常
#只有当捕获到的异常是你指定的异常类型是才会执行except后面的语句
try:
1/0
except ZeroDivisionError:
print("出错了")
出错了
#示例三
#加上as,将异常的原因给提取出来
try:
1/0
except ZeroDivisionError as e:
print(e)
division by zero
#示例四
#将多个可能出现的异常用元组包裹起来
#只要出现其中指定的一个异常,就执行except里面的内容
try:
1/0
520 + '1314'
except(ZeroDivisionError,ValueError,TypeError):
print("出错了")
出错了
#示例五
#也可以单独处理不同的异常,使用多个except语句即可
try:
520 + '1314'
1/0 #执行到520 + '1314'就跳出执行except里面的内容了,这行代码被跳过了
except ZeroDivisionError:
print("除数不能为0")
except ValueError:
print("值出错")
except TypeError:
print("类型出错")
类型出错
#示例六
#try-except-else语句,当try语句没有检测出任何异常就会执行else里面的内容
try:
1/1
except:
print("逮到了")
else:
print("没逮到")
没逮到
#示例七
#1.try-except-finally语句,无论异常是否发生,都会执行finally里面的内容
try:
1/1
except:
print("逮到了")
else:
print("没逮到")
finally:
print("逮没逮到都会执行")
没逮到 逮没逮到都会执行
#2.#这个finally通常用于执行一些收尾工作,如文件的关闭
try:
f = open("test.txt",'w')
f.write("hello")
except:
print("出错了")
finally:
f.close()
try:
try:
1/0
except:
print("内部异常")
520 + '1314'
except:
print("外部异常")
内部异常 外部异常
1.raise产生异常
#raise ValueError("值不正确!"),直接抛出一个ValueError类型的异常
#注意不能用raise去生成一个不存在的异常
2.assert语句产生异常
#注意assert语句只能引发AssertionError类型的异常
#通常用于代码调试
s = "FishC"
assert s == "FishC"
#assert s != "FishC"
#如果assert后面这个条件成立的话,啥事不会发生,否则抛出一个AssertionError类型的异常
产生异常有什么用呢?其实用异常可以产生goto语句的效果,在多个嵌套循环的语句里一下跳出来。
try:
while True:
while True:
while True:
for i in range(10):
if i > 3:
raise
print(i)
print("被跳过了")
print("被跳过了")
print("被跳过了")
print("被跳过了")
except:
print("到这里来~") #这里是目的地
0 1 2 3 到这里来~
类也是一种代码封装的方法,通常包含属性与方法,属性就是定义在类里面的变量,方法就是定义在类里面的函数。类名约定俗成用大写字母开头。
class C:
a = 1
b = 2
def say_hello(self):
print("hello")
c = C() #实例化一个对象出来
c.a
1
c.say_hello()
hello
c.d = 4 #可以动态的去添加属性
print(c.d)
4
self是什么?为什么将一个函数放到类里面,就必须要加上一个参数self呢?如果没有加这个self会发生什么呢?
class C:
def get_self(self):
print(self)
c = C()
c.get_self()
<__main__.C object at 0x0000020E26634430>
print(c)
<__main__.C object at 0x0000020E26634430>
如果没有加self在调用的时候,会报一个错误,说函数定义的时候没有参数,但是调用的时候却硬塞了一个给它。通过上面的代码,可以知道,这个硬塞给它的参数就是self,这个self其实就是类C的实例化对象c,这样我们就知道传递给方法的就是实例对象本身。
self的意义是:同一个类可以生成无数个对象,当我们调用类里面的一个方法时,python要通过这个self知道是哪一个对象在调用它,所以类中的每一个方法,默认的第一个参数都是self。
self使得实例对象与类里面的方法进行了绑定,类的实例对象有千千万,但是这些对象却是共享类里面的方法。所以当我们在调用c.get_self()的时候,其实际上的含义是调用类C的get_self()方法,并把实例对象c作为参数传递。
#相当于 C.get_self(c)
实例对象的方法是共享的,但是属性却是自己的。可以通过__dict__内省的方法查看属性。
class C:
pass
c = C()
c.x = 520
c.y = 880
c.__dict__
{'x': 520, 'y': 880}
拓展知识:一个旁门左道的小技巧,可以把一个空类作为字典使用
class C:
pass
C.x = 1
C.y = "小甲鱼"
C.z = [1,2,3]
print(C.x)
print(C.y)
print(C.z)
1 小甲鱼 [1, 2, 3]
#更常见的做法是通过类的实例对象来作为字典
c = C()
c.x = 1
c.y = "小甲鱼"
c.z = [1,2,3]
print(c.x)
print(c.y)
print(c.z)
c.__dict__
1 小甲鱼 [1, 2, 3]
{'x': 1, 'y': '小甲鱼', 'z': [1, 2, 3]}
class A:
x = 520
def hello(self):
print("hello,我是A")
class B(A): #定义类的时候,括号里面写它的父类
pass
b = B()
b.hello()
hello,我是A
class A:
x = 520
def hello(self):
print("hello,我是A")
class B(A): #定义类的时候,括号里面写它的父类
x = 880
def hello(self):
print("hello,我是B")
#如果类B里面出现与其父类同名的属性或方法,子类会覆盖掉父类里面的属性和方法
b = B()
print(b.x)
b.hello()
880 hello,我是B
1.判断一个对象是否属于某个类,可以使用isinstance()函数,这是一个BIF函数
isinstance(b,B)
True
isinstance(b,A) #子类实例化对象,也是属于其父类的
True
2.判断一个类是否为另一个类的子类,使用issubclass()函数
issubclass(A,B)
False
issubclass(B,A)
True
class A:
x = 520
def hello(self):
print("hello,我是A")
class B:
x = 880
y = 250
def hello(self):
print("hello,我是B")
class C(A,B): #继承是从左往右的,父类A,B有同名属性和方法,以左边父类的为准
pass
c = C()
print(c.x)
print(c.y) #只有在当前类中找不到了,而且在父类A中也搜寻未果,才会在父类B中寻找
c.hello()
520 250 hello,我是A
花园和动物们可不是继承关系,这个时候就应该使用组合了
class Cat:
def say(self):
print("喵喵")
class Dog:
def say(self):
print("旺旺")
class Garden:
c = Cat()
d = Dog()
def say(self):
self.c.say()
self.d.say()
g = Garden()
g.say()
喵喵 旺旺
构造函数使得实例化对象在定义的时候实现个性化定制,它会自动去调用。
class C:
def __init__(self,x,y):
self.x = x
self.y = y
def add(self):
return self.x + self.y
def mul(self):
return self.x * self.y
c = C(3,4)
print(c.__dict__)
print(c.add())
print(c.mul())
{'x': 3, 'y': 4} 7 12
如果对继承的父类里面的属性和方法不满意,可以在子类中定义同名的属性和方法对其进行覆盖。
class C:
def __init__(self,x,y):
self.x = x
self.y = y
def add(self):
return self.x + self.y
def mul(self):
return self.x * self.y
class D(C):
def __init__(self,x,y,z):
C.__init__(self,x,y) #直接调用类C的构造函数,这种直接通过类名调用类里面的方法称为调用未绑定的父类方法
self.z = z
def add(self):
return self.x + self.y + self.z
def mul(self):
return self.x * self.y * self.z
d = D(3,4,5)
print(d.add())
print(d.mul())
12 60
#这种直接通过类名调用类里面的方法称为调用未绑定的父类方法
#这种方式有时候可能会出现钻石继承
#示例如下:
class A:
def __init__(self):
print("哈喽,我是A")
class B1(A):
def __init__(self):
A.__init__(self)
print("哈喽,我是B1")
class B2(A):
def __init__(self):
A.__init__(self)
print("哈喽,我是B2")
class C(B1,B2):
def __init__(self):
B1.__init__(self)
B2.__init__(self)
print("哈喽,我是C")
c = C()
哈喽,我是A 哈喽,我是B1 哈喽,我是A 哈喽,我是B2 哈喽,我是C
解决的办法是super()函数,super()函数能够在父类中搜索指定的方法,并自动绑定号self参数
class A:
def __init__(self):
print("哈喽,我是A")
class B1(A):
def __init__(self):
super().__init__()
print("哈喽,我是B1")
class B2(A):
def __init__(self):
super().__init__()
print("哈喽,我是B2")
class C(B1,B2):
def __init__(self):
super().__init__()
print("哈喽,我是C")
c = C()
哈喽,我是A 哈喽,我是B2 哈喽,我是B1 哈喽,我是C
使用super()函数去查找父类的方法,它就会自动按照MRO顺序去搜索父类的相关方法,并且自动避免重复调用的问题。
拓展知识:什么是MRO顺序?M-method、R-resolution、O-order,方法解析顺序
#查看一个类的MRO有两种方法
#1.通过mro()方法
C.mro()
[__main__.C, __main__.B1, __main__.B2, __main__.A, object]
#2.通过mro内省
C.__mro__
(__main__.C, __main__.B1, __main__.B2, __main__.A, object)
class Animal:
def __init__(self,name,age):
self.name = name
self.age = age
def say(self):
print(f"我叫{self.name},今年{self.age}岁")
class FlyMixin:
def fly(self):
print("我还会飞")
class Pig(Animal,FlyMixin):
def special(self):
print("我的技能是拱大白菜")
p = Pig("大肠",5)
p.say()
p.special()
p.fly()
我叫大肠,今年5岁 我的技能是拱大白菜 我还会飞
class Displayer:
def display(self,message):
print(message)
class LoggerMixin:
def log(self,message,filename = "logfile.txt"):
with open(filename,'w') as f:
f.write(message)
def display(self,message):
super().display(message)
self.log(message)
class MySubClass(LoggerMixin,Displayer):
def log(self,message):
super().log(message,filename="subclasslog.txt")
subclass = MySubClass()
subclass.display("This is a test.")
This is a test.
MySubClass.mro()
[__main__.MySubClass, __main__.LoggerMixin, __main__.Displayer, object]
多态会根据不同类型的对象,采取不同的操作,比如我们常见的运算符、以及len()函数等等。
3 + 4
7
"I love " + "python"
'I love python'
3 * 4
12
"love" * 3
'lovelovelove'
len("FishC")
5
len([1,2,3,4,5,6])
6
下面是一个类继承的多态(实际上就是重写):
class Shape:
def __init__(self,name):
self.name = name
def area(self):
pass
class Square(Shape):
def __init__(self,length):
super().__init__("正方形")
self.length = length
def area(self):
return self.length * self.length
class Circle(Shape):
def __init__(self,radius):
super().__init__("圆形")
self.radius = radius
def area(self):
return 3.14 * self.radius *self.radius
class Triangle(Shape):
def __init__(self,base,height):
super().__init__("三角形")
self.base = base
self.height = height
def area(self):
return 1/2 * self.base * self.height
s = Square(5)
print(s.name)
print('边长',s.length)
print('面积',s.area())
c = Circle(10)
print(c.name)
print('半径',c.radius)
print('面积',c.area())
t = Triangle(3,4)
print(t.name)
print('底:',t.base,'高',t.height)
print('面积',t.area())
#这里正方形、圆形、三角形它们都继承Shape类,但是它们又都重写了构造函数和area()方法,这个就是多态的体现
正方形 边长 5 面积 25 圆形 半径 10 面积 314.0 三角形 底: 3 高 4 面积 6.0
自定义函数是如何实现多态接口的?
class Cat:
def __init__(self,name,age):
self.name = name
self.age = age
def intro(self):
print(f"我是一只猫,我叫{self.name},今年{self.age}岁")
def say(self):
print("喵喵")
class Dog:
def __init__(self,name,age):
self.name = name
self.age = age
def intro(self):
print(f"我是一只狗,我叫{self.name},今年{self.age}岁")
def say(self):
print("哟吼")
class Pig:
def __init__(self,name,age):
self.name = name
self.age = age
def intro(self):
print(f"我是一只小猪,我叫{self.name},今年{self.age}岁")
def say(self):
print("ohoh")
def animal(x):
x.intro()
x.say()
c = Cat('web',6)
d = Dog('布布',4)
p = Pig('大肠',5)
animal(c)
animal(d)
animal(p)
#这个animal()函数就具有多态性,该函数接收不同类型的对象作为参数,并且在不检查其类型的情况下执行它的方法
我是一只猫,我叫web,今年6岁 喵喵 我是一只狗,我叫布布,今年4岁 哟吼 我是一只小猪,我叫大肠,今年5岁 ohoh
#最后讲一个鸭子类型
#什么是鸭子类型?
#比如上面的animal()函数,它不需要关心它传入的参数x是什么东西,只要这个x里面有intro()和say()方法它就不会报错
class Bicycle:
def intro(self):
print("我曾经跨过山和大海,也穿过人山人海")
def say(self):
print("都有自行车了,还要什么兰博基尼?")
b = Bicycle()
animal(b)
#尽管这个自行车并不是一个动物,但是还是可以被animal()函数调用
#这就是鸭子类型
我曾经跨过山和大海,也穿过人山人海 都有自行车了,还要什么兰博基尼?
所谓私有变量,就是通过某种手段,使得对象中的属性和方法无法被外部所访问的机制。
class C:
def __init__(self,x):
self.__x = x #在变量前面加上两个下划线,就变成了私有变量,外部直接用对象.访问不到,会报错
def set_x(self,x):
self.__x = x
def get_x(self):
print(self.__x)
c = C(250)
#c.x是会报错的
c.get_x()
250
c.__dict__
{'_C__x': 250}
注意:由上面这行代码,我们发现这个其实就是江湖中传说的名字改编法,所谓私有变量,它其实就是偷偷给你改个名字,找到了它改编后的名字其实在外部还是可以访问到的。
c._C__x
250
#方法也是同样道理
class D:
def __func(self):
print("I love python.")
d = D()
#d.__func()是不能调用到其方法的
d._D__func()
I love python.
在对象诞生之后,不能通过动态添加属性的方式添加一个私有变量,名字改编是发生在类实例化对象的时候。
c.__y = 520
c.__dict__
{'_C__x': 250, '__y': 520}
拓展知识:单个下划线开头的变量,表示内部变量,仅供内部使用;单个下划线结尾的变量,可以用于定义一些作死的变量,比如class是python用来定义类的,我们可不敢随便用,不过你要实在觉得这个变量名必须得叫class,程序跑起来才能得到佛祖的保佑,你可以在class的后面加上一个下划线。
python为了对象的灵活性有时候会牺牲大量的存储空间,比如动态添加属性这个特性。动态添加属性,背后的实现原理是字典,就是我们熟悉的__dict__属性。
class C:
def __init__(self,x):
self.x = x
c = C(250)
c.x
250
c.__dict__
{'x': 250}
c.y = 880
c.__dict__
{'x': 250, 'y': 880}
#甚至我们可以直接给字典添加键值对的方式,增加一个属性
c.__dict__['z'] = 1314
print(c.__dict__)
print(c.z)
#但是字典是比较费内存的,拿空间来换时间
{'x': 250, 'y': 880, 'z': 1314} 1314
如果一个类被创建出来,只是使用那么几个固定的属性将来也不会有动态添加属性的需求,那么我们可以使用__slots__属性。避免了利用字典纯纯浪费内存的弊端。
class C:
__slots__ = ['x','y'] #赋值一个列表给它,列表的元素就是希望固定的几个属性
def __init__(self,x):
self.x = x
c = C(520)
print(c.x) #没有问题
c.y = 250 #也没有问题
print(c.y)
#但是c.z = 880 就会报错
520 250
当然,slots属性不仅仅体现在动态添加属性上,在类内部创建一个不包含slots列表内的属性也是不被允许的。slots属性虽然降低了对象的灵活性,但可以节省很多空间。而且用slots还可以限制属性的滥用。
拓展知识:继承自父类的slots属性是不会在子类中生效的。
class C:
__slots__ = ['x','y']
class E(C):
pass
e = E()
e.z = 100 #是可以动态添加属性的
print(e.z)
print(e.__slots__)
print(e.__dict__)
100 ['x', 'y'] {'z': 100}
这是我们学习类和对象最早接触的一个魔法方法,它不用显示的去调用。
对象诞生时调用的第一个方法是new,它的参数是一个类,先调用new产生一个实例,再将实例传给init。
#在继承不可变数据类型的时候,如果我们要从中作梗就要重写new方法进行拦截
class Capstr(str):
def __new__(cls,string):
string = string.upper() #赶在实例对象被创建之前进行了拦截
return super().__new__(cls,string)
c = Capstr("fishc")
print(c)
#这里之所以可以对不可变对象进行修改,是因为我们赶在实例对象被创建之前进行了拦截
FISHC
对象在销毁之前,会调用一个魔法方法del
class C:
def __init__(self):
print("我来了")
def __del__(self):
print("我走了")
c = C()
del c
我来了 我走了
#要注意的是,并不是调用del一定会触发del魔法方法,调用del方法一定是对象被销毁的时候
#python是垃圾回收机制,当检测到一个对象没有任何引用的时候就销毁对象
c = C()
d = c
del c
我来了
del d
#这才真正的销毁了对象
我走了
拓展知识:烧脑子环节,利用del魔法方法实现对象的重生,有两种方法可以实现:1.全局变量;2.闭包
#1.利用全局变量,在del魔法方法调用时,把要销毁的对象送出来
class C:
def __init__(self,name):
self.name = name
def __del__(self):
global x
x = self
c = C("小甲鱼")
print(c)
print(c.name)
del c
print(x)
print(x.name)
<__main__.C object at 0x0000021F05D94430> 小甲鱼 <__main__.C object at 0x0000021F05D94430> 小甲鱼
#2.利用闭包
class E:
def __init__(self,name,func):
self.name = name
self.func = func
def __del__(self):
self.func(self)
def outer():
x = 0
def inner(y=None):
nonlocal x
if y:
x = y
else:
return x
return inner
f = outer()
e = E("小甲鱼",f)
e
e.name
del e
g = f()
g
g.name
#这里用闭包函数实现的原因是让self保存在外部函数的x变量中,内部函数的作用是窃取这个self对象
#del魔法方法调用它,是有参数的,它就把这个self对象保存在x中
#而在外部调用这个函数,它不带参数,那么它返回刚刚拿到的对象