python基础
Python基础阶段笔记
补充:
Ctrl + / 注释快捷键
ctrl + alt +l 代码格式化
Ctrl + shift + f10 运行代码快捷键
变量的类型:
str -- 字符串
bool -- 布尔(真假)
int -- 整数
float -- 浮点数(小数)
标识符命名规则:
由字母,下划线和数字组成
不能以数字开头
不能与关键字重名
建议不要和类型同名,如:int,str
python 是动态强类型语
动态是指python在定义变量时无需指定数据类型,这就导致在编程时无法确定程序运行后的数据类型,导致不确定的报错;
强制类型转换,例如在 字符加数字时 a + 1,python必须将1转换成字符串类型才可以相加 print('a'+str(1))
python中的内存管理机制:
1.变量无需事先声明,也不需要指定类型,这是动态语言的特性
2.python编程中一般无须关心变量的存亡,一般也不用关心内存的管理
3.python使用引用计数,记录所有对象的引用次数(sys.getrefcount()查看变量引用次数,常量的引用计数会非常高,因为常量在内存中只有一份,解释器都会引用)
4.当对象引用数变为0,它就可以被垃圾回收GC(机制java也有这种机制)
5.如果内存空间中都是分散的内存地址,python解释器会对这些零散的内存做规整,整理出一段连续的内存空间,供其他数据使用(自己总结)
6.计数增加:
赋值给其他变量就是增加引用计数,例如x=3;y=x
实参传参,如foo(y)
7.计数减少
函数运行结束时,局部变量就会被自动销毁,对象引用计数减少
变量被赋值给其他对象。例如x=3,y=x
8.有关性能的时候,就需要考虑变量的引用问题,但是该释放内存还是尽量不释放内存,看需求
# 时间复杂度和空间复杂度
在衡量一个算法所编写出程序的运行效率时,数据结构中,用时间复杂度来衡量程序运行时间的多少;用空间复杂度来衡量程序运行所需内存空间的大小
1.常用的格式化输出符
%d 表示有符号的十进制整数
%s 表示字符串
%f 表示浮点数
不常用的格式化输出符
%c 字符
%u 无符号十进制整数
%o 八进制整数
%x 十六进制整数(小写ox)
%X 十六进制整数(大写OX)
%e 科学计数法 (小写e)
%E 科学计数法(大写E)
%g %f和%e的简写
示例 :
age = 18
name = 'luojie'
weight = 75.5
stu_id = 1
print('我的名字是%s,我的年龄是%d,我的体重是%.2f,我的学号是%03d' % (name, age, weight, stu_id))
注:上述中%.2f表示显示是对浮点数只保留小数点后两位数,%03d表示输出的整数显示位数,不足的位以0补全,超出当前位数则原样输出
1.1 格式化字符串拓展
拓展解释1:
%s表示输出的为字符串,但整数和浮点数也可以用%s字符串格式输出,并且也支持变量引用时 的变量运算
示例:
name = 'tom'
age = 18
weight = 75.5
hight = 175
#我的名字是x,今年x岁了,体重是x公斤
print('我的名字是%s,今年%s岁了,我明年%s岁了,体重%s公斤' % (name, age, age+1, weight))
拓展解释2 :
%s格式化字符串的效果也可以用 f'{表达式}' 语法来代替,并且 f 的格式化输出字符串是python3.6中新增的格式化方法,该方法更简单易读,也可以对变量进行字符运算
示例:
'格式化字符串f的用法在python3.5 ,3.6 之前无法使用,使用会报错'
# %s格式化字符串的效果也可以用 f'{表达式}' 语法来代替 ;或者 F'{表达式}'
name = 'tom'
age = 18
weight = 75.5
hight = 175
print(f'我的名字是{name} ,我的年龄是{age} ,后年我{age + 2},我的体重是{weight} 公斤,下一次我的体重是{weight - 30}公斤')
扩展:格式化字符串函数 format
format函数格式化字符串语法 ---- python鼓励使用
"{}{xxx}".format(*args, **kwargs) -> str
args 是可变位置参数,是一个元组
kwargs是可变关键字参数,是一个字典
花括号表示占位符
{}表示按照顺序匹配位置参数,{n}表示取位置参数索引为n的值
{xxx}表示在关键子参数中搜索名称一致的
{{}} 表示打印花括号
示例:
# 利用dattime模块获取当前时间
>>> import datetime
>>> d = datetime.datetime.now()
>>> d
datetime.datetime(2020, 11, 20, 15, 32, 44, 632804)
>>> "{:%Y-%m-%d %H:%M:%S}".format(d) # 格式化字符串
'2020-11-20 15:32:44'
# 把ip地址转换成16进制
>>> "{:x} {:x} {:x} {:x}".format(127,100,10,20)
'7f 64 a 14'
>>> "{:#02x}{:02X}{:0x}{:02X}".format(127,100,10,20)
'0x7f64a14'
# 格式化字符串
>>> "{} * {} = {}".format(2, 3, 2*3)
'2 * 3 = 6'
>>> "{}*{}={:<2}".format(2, 3, 2*3) # <左对齐
'2*3=6 '
>>> "{}*{}={:>2}".format(2, 3, 2*3) # >右对齐
'2*3= 6'
>>> "{}*{}={:^20}".format(2, 3, 2*3) # 居中
'2*3= 6 '
>>> octets = [192,168,1,1]
>>> '{:02X}{:02X}{:02X}{:02X}'.format(*octets) # 参数解构,转换成16进制
'C0A80101'
1.2 转译字符
\n : 换行
\t : 制表符,一个tab键(4个空格)的距离
print('hello\nworld')
print('hello\tworld')
1.3 结束符
在python中,print(),默认自带end="\n"这个换行结束符,所以导致每两个print直接会换行展示,用户可以按需求更改结束符
格式:print('内容',end="")
注:end冒号中可以为任意字符,不限于换行符和制表符
print('hello py', end="\t")
print('python')
1.4 输入
输入的语法:input("提示信息")
输入的特点:
当程序执行到input,等待用户输入,输入完成之后才继续向下执行。
在python中,input 接收用户输入后,一般存储到变量中,方便使用
在python中,input 会把接收到的任意用户输入的数据当作字符串处理
示例:
passwd = input("请输入密码:")
print(f'输入的密码是{passwd}')
#查看数据类型
print(type(passwd))
2. 转换数据类型
目的:用户输入的内容默认为字符串类型,将其转换为其他数据类型时可以利用数据类型转换的函数来完成,如下:
| 函数 | 说明 |
|---|---|
| int(x [,base]) | 将x转换为一个整数, 取整数部分 |
| float(x) | 将x转换为一个浮点数 |
| str(x) | 将对象x转换为字符串 |
| eval(str) | 用来计算在字符串中的有效python表达式,并返回一个对象,原本的类型 |
| tuple(s) | 将序列s转换为一个元组 |
| list(s) | 将序列s转换为一个列表 |
| complex(real [,imag]) | 创建一个复数,real为实部,imag为虚部 |
| repr(x) | 将对象x转换为表达式字符串 |
| chr(x) | 将一个整数转换为一个unicode字符 |
| ord(x) | 将一个字符转换为它的ascii整数值 |
| round() | 元整,四舍六入取偶 |
| floor() | 向下取整 |
| ceil() | 向上取整 |
| // | 整除且向下取整 |
| isinstance(x,(str,float, str,bool,int)) | 判断字符x是否属于元组(str,float, str,bool,int) 中的任意一个类型,是则返回True,否则False |
isinstance()判断字符类型:
示例:
>>> isinstance(11,int)
True
>>> isinstance(11,(float, str, bool))
False
>>> isinstance(11,(float, str, bool, int))
True
示例1:
passwd = input("输入密码")
#此时数据类型是字符型
print(type(passwd))
#此时转换后的数据类型是整数型
print(type(int(passwd)))
示例2:
#将字符串或者整数转换为符点类型
num1 = 1
str1 = '10'
print(type(float(num1)))
print(float(num1))
print(float(str1))
# str() 将数字转换为字符类型
print(str(num1))
# tuple() 将序列转换为元组
list1 = [10, 20, 30]
print(tuple(list1))
# list() 将一个序列转换为列表
t1 = (100, 200, 300)
print(list(t1))
# eval() 判断变量的字符中有效的python表达式,返回一个对象,及值对应的类型
# 自动判断变量的字符类型,并且输出字符的对应的类型
str2 = '1'
str3 = '1.1'
str4 = '(1000, 2000, 3000)'
str5 = '[1000, 2000, 3000]'
print(type(eval(str2)))
print(type(eval(str3)))
print(type(eval(str4)))
print(type(eval(str5)))
3. 运算符的分类
算数运算符, 赋值运算符, 复合赋值运算符, 比较运算符, 逻辑运算符
注:
3.1 算数运算符:+ - * / // % **
加,减,乘,除,取整数除,取余,指数
算数混合运算时,优先级顺序为() > ** > * > / > // > % > + , -
优先级a +b > c and (加减 > 比较 > 逻辑);先算等号右边再算左边
// 取整输除时遵循向下取整,就是取比结果小的整数数,特别注意负数取整时-2.5取整是-3
异或运算;相异出1
magedu补充知识--二进制运算:
原码,反码,补码,负数表示法:
原码(是给人看的):5(原码) => 0b101(编译后的机器表示), 1 => 0b1, -1 => -0b1, bin(-1)
反码:正数的反码与原码相同;负数的反码符号位不变其余按位取反
补码(机器能读的编码叫做补码):正数的补码与原码相同;负数的补码符号位不变其余按位取反后+1
负数表示法:
早期数字电路的cpu中的运算器实现了加法器,但是没有减法器,减法要转换成加法
负数在计算机中使用补码存储,-1的补码为 1111 1111
5-1 => 5+(-1)直觉上是 0b101-0b1, 其实计算机中是 0b101+0b11111111,溢出位舍弃
~12(把12按位取反)为什么是 -13
0000 1100 <- 12 (十进制)
1111 0011 <- (按位取反)最高位是1,表示负数,认为是负数的补码
1000 1101 —> -0x0d -0xd 得到结果 -13
10^9(异或运算)等于?10^-9等于?为什么
1010 = 10
1001 = 9
-----
0011 = 3 (10异或 9为 3) 10^9是-3?
3.2 赋值运算符:
二进制转换到十进制:
1001
8421。是 十进制的 9
= 等号赋值,将等号右边的值赋值给左边的变量中
= 等号用于变量赋值,注意在多变量赋值时,左边的变量名用逗号隔开,右边的值也用逗号隔开,达到一一对应的赋值效果,用法如下:
a, b, c = "tom" , 1, 3.24
print(a)
print(b)
print(c)
#多变量赋相同的值,作用时化简代码量
e = f = 100
print(e)
print(f)
3.3 复合赋值运算符
| 运算符 | 描述 | 示例 |
|---|---|---|
| += | 加法赋值运算 | c+=a 等价于 c = c + a |
| -= | 减法赋值运算 | c-=a 等价于 c=c-a |
| *= | 乘法赋值运算 | c*=a 等价于 c=c*a |
| /= | 除法赋值运算 | c/=a 等价于 c=c/a |
| //= | 整除赋值运算 | c//=a 等价于 c=c//a |
| %= | 取余赋值运算 | c%=a 等价于 c=c%a |
| **= | 幂赋值运算 | c**=a 等价于 c=c**a |
| ~x | 按位取反 | bit位 1001 取反是 0110 |
| x is y, x is not y | 等同测试 | |
| x in y, x not in y | 成员判断 |
s = 10
s += 1
#输出时的表达式为 s = s + 1
print(s)
# 下例中先做等号右边的运算,再来赋值运算,下述结果为30
d = 10
d *= 1 +2
print(d)
注:在运算时等号右边的值赋值给等号左边的值 ,如果等号右边的值存在运算时,则先做右边值的运算再赋值
3.4 比较运算符
== 等于
!= 不等于
< 小于
> 大于
<= 小于等于
>= 大于等于
3.5 逻辑运算符
and 与(并且);都真才真(短路运算符)
or 或 ;只要一个为真就为真,都假才假 (短路运算符)
not 非 ;取反
成员运算符:in , not in
身份运算符:is(是),is not(不是)
示例:
a = 1
b = 2
c = 3
print((a<b) and (b<c))
注:在使用逻辑运算符时,最好加上小括号避免歧义,并且有助于阅读代码
拓展:
# and 运算符在做数值逻辑运算时,只要有一个值为0,则结果为0,否则结果为最后一个非0数字
# or 运算符在做数值逻辑运算时,只要所有值为0结果才为0,否则结果为第一个非0数字
4. 条件语句
4.1 if 循环语句
语法:
if 条件:
执行代码
...
else:
执行代码
...
注:在上述的【执行代码】块必须是缩进状态的,不是缩进状态的【执行代码】不会在 if 中执行
示例:
num = input("请输入学号:") #或者将字符转换成整数在做下面的比较 num = int(input("请输入学号")),下面在比较时就可以不带冒号
if num == "10":
print('恭喜你的学号中奖了')
else:
print('不好意思,未中奖!')
4.2 多重条件判断 if...else...
条件成立则执行代码1,条件不成立则执行代码2
多重判断也可以和else配合使用,一般else放到整个if语句的最后,表示以上条件都不成立的时候执行的代码
语法:
if 条件1:
条件1成立执行的代码1
条件1成立执行的代码2
...
elif (条件2) and (条件3):
条件2和条件3都成立执行的代码1
条件2和条件3都成立执行的代码2
...
...
else:
以上条件都不成立执行的代码
拓展:如果条件判断年龄是 age >= 18 and age <= 60 可以简化为 18 <= age <= 60
4.3 if 嵌套
语法:
if 条件1:
条件1成立执行的代码1
条件1成立执行的代码2
if 条件1:
条件1成立执行的代码1
条件1成立执行的代码2
注意:条件2的if也是出于条件1的缩进关系内部
示例:
# 坐公交:如果有钱可以上车,否则不能上车,如果上车,判断是否能坐下
men = 1
seat = 1
if men == 1 :
print("请上车")
if seat == 1:
print("请坐下")
else:
print("请站着")
else:
print("不能上车")
5. 三目运算符/三元表达式
注:但是这种简化的表达式有局限性,只能表达一行代码判断,不能多行判断
说明:要读带有三目运算符的表达式时要从if开始读,代码1 if (条件) else 代码2 左边是条件成立执行的代码1,条件不成立执行代码2 。这样书写的目的是简化程序
示例:
a = 1
b = 2
# 将运行结果赋值给c,并且打印c
c = a if a>b else b
print(c)
#例2 执行判断若aa大于bb,则用aa-bb,反之亦然,将结果赋值给cc
aa = 10
bb = 20
cc = aa -bb if aa > bb else bb - aa
print(cc)
5.1 While 循环
语法:
while 条件判断:
循环执行语句
...
示例:
#例1:执行代码10次
num = 1
while num <= 10:
print("i am sorry %d " % num)
num += 1
# 例2 求1 加到 100 的和
步骤分析:
1。定义初始值,做加法
2。定义总和变量来赋值加法结果
3。打印总和变量
i = 1
result = 0
num3 = 9999
while i <= num3:
result = result + i
i = i + 1
print("1+2+3+4+...+%d =%d" % (num3,result))
# 求100以内的偶数相加
i = 1
num4 = 0
cyc = 100
while i <= cyc:
if i % 2 == 0:
num4 = num4 + i
i = i +1
print("2+4+6+8+...+%d=%d" %(cyc,num4))
# 求100以内的奇数相加
i = 1
num6 = 0
while i <= cyc:
if i % 2 != 0:
num6 = num6 + i
i = i +1
print("1+3+5+7+...+%d=%d" %(cyc,num6))
5.2 while嵌套
先写外层循环,再写内层循环,然后将内层循环移动到外层循环中
# 通过循环嵌套打印一个正方形
# 先写外部循环体,循环5次,其中pass代表还没有替换的内循环体
while i < 5:
pass
print()
i += 1
# 内循环体,打印5个连续的*
while j < i + 1:
print("*", end=" ")
j += 1
结果为:
while i < 5:
while j < i + 1:
print("*", end=" ")
j += 1
print()
i += 1
#示例2
# 外循环体
i = 0
while i < 5:
print("已经跑了第%d圈了" % (i + 1))
pass
i += 1
# 内循环
j = 0
while j < 10:
print("做了第%d个俯卧撑了" % (j + 1))
j += 1
结果为:
i = 0
while i < 5:
print("已经跑了第%d圈了" % (i + 1))
j = 0
while j < 10:
print("做了第%d个俯卧撑了" % (j + 1))
j += 1
i += 1
5.3 Break 和 continue和 else
break 的作用是跳出循环,后续不再执行
continue 的作用是跳过指定循环,下一循环继续执行
while ...循环体... else... 语句:
当循环体中没有遇到 break 语句时while 条件执行完后继续执行else 分支中的条件
注:与if ... else... 中不同,if 中只会执行其中的一个分支
而while 语句中只要没有break就会都执行
5.4 字符串(下标,切片)
补充:
一个个字符组成的有序的序列,是字符的集合;'有序序列都可以使用索引'
使用单引号,双引号,三引号引住的字符序列
字符串是'不可变'对象,不能再被修改
python3起,字符串就是Unicode类型
(1). 书写字符串时,可以用单引号,双引号和三引号;其中三引号的特点时可以换行输出需要打印的内容
(2). 下标 又叫做 索引 ,即位置编号
(3). 下标查找示例:
namm = 'hello world'
print(namm[0])
(4). 切片是指对操作的对象截取其中一部分的操作,字符串,列表,元组都支持切片操作
语法:序列[开始位置下标:结束位置下标:步长]
左闭右开的选择区间
注意:不包括结束位置的下标所包含的数据,正负整数均可,步长是选取间隔,正负整数均可,默认步长为1
a = '123456789'
print(a[-1:-5:-1])
b = 'abcdefg'
print(b[-1:-5:-2])
6. 字符串常用操作方法
字符串的常用操作方法有查找,修改和判断三大类
注:字符串是不可被修改的,是不可变元素,所有修改操作返回的一定是一个新值或者新变量
6.1 查找
# find(): 字符串序列.find(子串,开始位置下标,结束位置下标)
查找某个字串是否包含在这个字符串中,如果在则返回这个字串开始的位置下标,否则则返回-1
注:开始和结束位置下标可以省略,表示在整个字符串序列中查找
# index():index(sub[,start[,end]]) -> int
在指定的区间[start, end],从左至右,查找子串sub。找到返回索引,没找到抛出异常ValueFrror;同为查找功能,与find() 语法相同但不同点是在查找不存在的子串时会直接报错
# count():count(sub[,start[,end]]) -> int
在指定的区间,从左至右,统计子串sub出现的次数;统计某子串在整个字符串中出现的次数,语法与上述相同
# rfind(): 和find()功能相同,但查找方向为右侧开始
# rindex(): rindes(sub[,start[,end]]) -> int
在指定的区间[start, end],从左至右,查找子串sub,找到返回索引,没找到抛出异常ValueError ;和index()功能相同,但查找方向为右侧开始
6.2 修改: replace(),split(),join()
replace() 修改,替换可以修改字符串,指的就是通过函数的形式修改字符串中的数据,并没有更改变原始字符串,返回的是一个新字符串
replace(old, new[,count]) -> str
字符串中找到匹配替换为新子串,返回新字符串
count表示替换几次,不指定就是全部替换
语法:字符串序列.replace(旧子串,新子串,替换次数)
示例:
mystr = ("hello world and hello python")
print(mystr.replace('hello', 'haha'))
split(): 按照指定字符分割字符串
split() --分割, 返回一个列表,会丢失分割字符
rsplit() -- 反向分割
'默认使用空白字符和换行符分割,立即返回一个列表'
按照行来切分字符串
keepends 指的是是否保留分隔符
行分隔符包括\n, \r\n, \r等
语法:
字符串序列.split(分割字符, num)
注意:num表示的是分割字符出现的次数,即将来返回数据个数为num+1个
示例:
mystr = ("hello world and hello python")
list = mystr.split('and')
print(list)
##返回 ['hello world ', ' hello python']
>>> s1 = 'a,b,c,d,e,f'
>>> s1.split("d")
['a,b,c,', ',e,f']
--------------------------------------------------------------------
partition() -- 分割,必须要有分割符,只切一刀,结果为元组,并且是一个三元组
rpartition() -- 反向分割
>>> s1 = 'a,b,c,d,e,f'
>>> s1.partition("d")
('a,b,c,', 'd', ',e,f')
join(): 用一个字符或子串合并字符串,即是将多个字符串合并为一个新的字符串。
join() ---合并列表里面的字符串数据为一个大字符串
语法:
字符或子串.jion(多字符串组成的序列)
示例1:
mylist = ['aa', 'bb', 'cc']
new_list = '...'.join(mylist)
print(new_list)
## 返回 aa...bb...cc
示例2:
>>> ",,,,".join(['1','3','9','9'])
'1,,,,3,,,,9,,,,9'
示例3:每个int类型的数字都给str转换为字符后进行连接
>>> ":".join(map(str, range(9)))
'0:1:2:3:4:5:6:7:8'
6.3 其他非重点的修改函数:
zfill(): 填充0,给字符串按位填充,不够的用0代替
>>> str(10).zfill(10)
'0000000010'
字母大小写转换:
capitalize(): 将字符串第一个字符转换成大写
注:capitalize()函数转换后,只整个字符串的第一个字符大写,其他字 符全部小写。
title():将字符串每个单词首字母转换成大写
lower(): 将字符串中大写转小写
upper(): 将字符串中小写转大写
lstrip(): 删除字符串左侧空白字符
删除空白字符:
lstrip(): 删除字符串左侧空白字符
rstrip(): 删除字符串右侧空白字符
strip(): 删除字符串两侧空白字符
字符串对齐:
ljust(): 返回一个原字符串左对齐,并使用指定字符(默认空格)填充至对应长度的新字符串。
语法:
字符串序列.ljust(长度,填充字符)
示例:
_mystr = ('hello')
new_mystr = _mystr.ljust(10, '.')
print(new_mystr)
## 返回 hello.....
rjust(): 返回一个原字符右对齐,并使用指定字符填充对应长度的新字符串,语法与上述相同
center(): 返回一个原字符的中间对齐状态,并使用指定字符填充对应长度的新字符串,语法与上述相同
6.4 判断
所谓判断既是判断真假,返回的结果是布尔型数据类型:True 或 False
# startswith():字符串序列.startswith(子串,开始位置下标,结束位置下标)
检查字符串是否是以指定子串开头,是则返回 True ,否则返回 False。 如果同时设置了开始和结束位置下标,则在指定范围内检查。
# endswith():字符串序列.endswith(子串,开始位置下标,结束位置下标)
检查字符串是否以指定子串结尾,是则返回 True ,否则返回 False 。如果设置了开始和结束位置下标,则在指定范围内检查。
# 其他字符串判断函数:
isalpha(): 如果字符串至少有一个字符并且所有字符都是字母则返回 True,否则返回 False
isdigit(): 如果字符串只包含数字则返回 True 否则返回 False
isalnum(): 如果字符串至少有一个字符并且所有字符都是字母或数字则返回 True,否则返回 False
isspace(): 如果字符串中包含空白,则返回 True, 否则 返回 False
7. 列表
7.1 列表可以一次性存储多个数据(连续排列的)
列表:列表中可以存储多个数据类型,但一般只存储一种数据类型,一个连续的内存空间,是一个队列,一个排列整齐的队伍,python中有垃圾回收机制,可以整理碎片化的内存空间,开辟一个连续的内存空间出来;但是列表的坏处也在与此,当列表中添加或者删除其中一个数据时,整个内存空间就会移动,保持住连续性,使列表的性能很受影响。列表因为是一个连续的有序的内存空间,所以在查找,索引列表中的某一个数据时会很高效。
链表:区别于列表,是散落排列在内存空间,优势在于增删,劣势在于查询;链表中的数据是前后相连的,前一个数据只会知道后一个数据的位置,若要知道某一个数据的位置就需要从头开始询问查找
queue:队列,数据遵循先进先出策略,如排队,队列前面的先进去,后面的后进去
stack:栈,数据遵循先进后出策略,如子弹上弹夹,第一个子弹最后一个打出
列表,链,队列,栈都是有序的数据类型,都有顺序的
列表的索引访问:
索引,也叫做下标
正索引:从左往右,从0开始,为列表中的每一个元素编号
负索引:从右往左,从-1开始
正负索引不可以超界,否则引发异常IndexError
为了方便理解,可以认为列表是从左至右排列的,左边是头部,右边是尾部,左边是下界,右边是上界
列表是可变的额数据类型
列表中查找的函数:
index(): 返回指定数据所在位置的下标
语法:列表序列.index(数据,开始位置下标,结束位置下标)
count(): 统计指定数据在当前列表中出现的次数
语法与上相同
len(): 访问列表长度,即得到列表中的数据的个数
列表list在存放数据时,自带有长度属性,加元素+1,减元素-1,因此获取列表的长度信息会很快
时间复杂度:
index和count方法都是O(n)
随着列表数据规模的增大,而效率下降,这两个函数尽量少用
7.2 列表中判断是否存在的函数:
in: 判断指定数据是否在某个列表序列,如果在返回 True,否则返回 False
not in : 判断指定数据不在某个列表序列,如果不在返回 True,否则返回 False
示例1:
mystr = ("hello world and hello python")
print('hello' in mystr)
示例2 需求:注册邮箱时,用户输入一个用户名,判断此用户名是否已经存在,存在则告知不能注册,不存在则可以注册
name_list = ['tom', 'ian', 'sam']
name = input("请输入你的用户名:")
if name in name_list:
print("该用户名已存在,不能注册!")
else:
print(f"用户名{name}可以注册!")
7.3 增加
作用:增加指定数据到列表中
'append(): 列表结尾追加数据,一次添加一个'
语法:列表序列.append(数据)
示例:
name_list = ['tom', 'ian', 'sam']
name = input("请输入你的用户名:")
if name in name_list:
print("该用户名已存在,不能注册!")
else:
print(f"用户名{name}可以注册!")
name_list.append(name) ##添加用户输入到列表
print(name_list)
注:append()追加的数据是一个序列,则追加整个序列到列表中
extend(): 列表结尾追加数据,如果数据是一个列表,则将这个列表的数据逐一添加到目标列表中,若要追加的数据是个字符串,则将字符串逐个拆开进行追加
语法:列表序列.extend(数据)
a = ['aa', 'bb', 'cc']
a.append(['a','b','c'])
print(a) #结果是:['aa', 'bb', 'cc', ['a', 'b', 'c']]
a.extend(['a','bbbbb'])
print(a) #结果是:['aa', 'bb', 'cc', ['a', 'b', 'c'], 'a', 'bbbbb']
'insert(): 指定位置新增数据,插队(插队在列表中效率最低,因为会影响数据在内存中的整体位置
语法:列表序列.insert(位置下标,数据)
超越上界,尾部追加,超越下界,头部追加
示例:
a = ['aa', 'bb', 'cc']
a.insert(1,'sorry')
print(a)
结果:['aa', 'sorry', 'bb', 'cc']
7.4 删除
del
语法: del 目标
# 删除整个列表
a = ['aa', 'bb', 'cc']
del a
print(a)
#删除列表中指定数据
a = ['aa', 'bb', 'cc']
del a[0] #删除下标为0的数据
print(a)
pop()
'根据索引查找数据,也会挪动数据,除非是最后一个数据,也是一个低效的操作,找数据很快'
删除指定下标的数据,如果不指定下标,默认删除最后一个数据,无论是按照下标还是删除最后一个,pop函数都会返回这个被删除后的数据
示例:
a = ['aa', 'bb', 'cc']
new_a = a.pop()
print(a)
# 结果为 ['aa', 'bb']
remove(数据)
'删除列表中的某个数据的第一个匹配项,时间复杂度O(n),较低效,会引起数据的挪动,除非是最后一个数据'
语法:列表序列.remove(数据)
示例:
a = ['aa', 'bb', 'cc']
a.remove('aa')
print(a)
clear() -- 效率较高
'引用计数为0时,内存的垃圾回收机制会自动清除,clear()只是会将引用计数减1,剩下一个空列表,而清除数据的操作只会是垃圾回收机制来做,而非这个函数来做'
清空数据,返回空列表
7.5 修改
修改指定下标的数据
a = ['aa', 'bb', 'cc']
a[0] = 'ff'
print(a)
逆置排序(反转):reverset()
>>> s3 = [1, 100, 3]
>>> s3.reverse()
>>> s3
[3, 100, 1]
升序/降序 排序: sort() -- 低效操作
语法: 列表序列.sort( key=None, reverse=False)
注:reverse 表示排序规则,reverse = True 降序,reverse = False 升序(默认), key 是设置在排序时按照什么数据类型来排,只用于排序过程中,不改变数据本质类型
如:
>>> s5 = [1, 2, 3, 4, 8]
>>> s5.append('10')
>>> s5
[1, 2, 3, 4, 8, '10'] # 添加字符串10
>>> s5.sort() # 列表中数据类型不同无法排序
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'str' and 'int'
>>> s5.sort(key=int, reverse=True) # key=int 设置用整数类型做降序
>>> s5
['10', 8, 4, 3, 2, 1]
>>> s5.sort(key=str, reverse=True) # 转换成字符串类型后降序排列,默认是按 ASCII 码比较
>>> s5
[8, 4, 3, 2, '10', 1]
7.6 复制
函数:copy()
复制列表中的数据到另一个变量进行赋值存储
经常在删除数据前先备份一份数据
语法:变量2 = 变量1.copy()
# copy()直接拷贝是浅拷贝,既只拷贝了原数据的引用指针,数据在内存中还是只有一份,如果改变了原数据,拷贝后的变量所指的数据也会改变
"""
影子拷贝:也叫做浅拷贝,遇到引用类型,只是复制了一个引用而已
* 星号,和 copy 会出现浅拷贝的问题
深拷贝:借助 copy 模块 提供的 deepcopy
"""
例如:
'列表进行拷贝时要注意浅拷贝和深拷贝的问题,因为列表中存的是内存地址(指向)而非数据本身'
'浅拷贝-指向同一个内存地址'
>>> s1 = [1,2,3,[4,5],6]
>>> s2 = s1.copy()
>>> s2
[1, 2, 3, [4, 5], 6]
>>> s1[3][0] = 100 #改变原数据
>>> s1
[1, 2, 3, [100, 5], 6] # s1变量改变
>>> s2
[1, 2, 3, [100, 5], 6] # s2 变量也跟着改变
>>> s4 = [100,200]
>>> s5 = [s4] * 3
>>> s5
[[100, 300], [100, 300], [100, 300]]
>>> s4[0] = 500
>>> s5
[[500, 300], [500, 300], [500, 300]]
'深拷贝-将内存中的数据全部拷贝一份并且赋值给新变量'
需要引用:copy模块中的deepcopy
>>> s3 = [1, 2, 3]
>>> import copy
>>> s6 = copy.deepcopy(s3)
>>> s3[1] = 100
>>> s3
[1, 100, 3]
>>> s6
[1, 2, 3]
"""
在列表中使用copy时,要考虑列表中的数据类型是简单数据类型还是复杂数据类型。
简单 :list1=[1,2,3]
复杂:list2=[1,2,3,[4,5,6]]
只有list2复杂数据类型在做copy时会有浅拷贝的影响,而list1简单数据类型在做拷贝时不受影响
"""
例:
>>> f3 = [1,2,3,[00,11],5]
>>> f4 = f3.copy()
>>> f4
[1, 2, 3, [0, 11], 5]
>>> f4[2] = 99
>>> f4
[1, 2, 99, [0, 11], 5]
>>> f3
[1, 2, 3, [0, 11], 5] # 修改f3中的简单数据类型原数据不改变
>>> f4[3][0] = 88 # 修改f4中的复杂数据类型就有浅拷贝的影响
>>> f4
[1, 2, 99, [88, 11], 5] #f3列表中的[00,11]拷贝到f4是浅拷贝,增加了一次引用,实际上只是一份数据
>>> f3
[1, 2, 3, [88, 11], 5]
7.7 列表的循环遍历
需求:依次打印列表中的数据
示例:
a = ['aa', 'bb', 'cc', 'dd']
i = 0
while i < len(a):
print(a[i])
i += 1
for 循环实现循环遍历
if...in... : 判断某个元素是否在列表中,如果在则返回 True
for...in... : 从头到尾依次从列表中取出每一个元素
示例:
a = ['zhangsan','lisi','wangwu']
for i in a:
print(i)
7.8 列表嵌套
所谓嵌套就是指在一个列表中包含了其他的子列表。
示例:
a = [['zhangsan', 'lisi', 'wangwu'], ['a', 'b', 'c'], ['1', '2', '3']]
print(a[0][0])
## 结果为:zhangsan
7.9 列表排序-冒泡法排序
# 排序算法-冒泡法
冒泡法属于交换排序,就地排序,直接改变列表
两两比较大小,交换位置。如同水泡一个一个往上冒
结果分为升序和降序
升序:
n个数从左至右,编号从0开始到n-1,索引0和1的值比较,如果索引0大,则交换两者的位置,如果索引1大,则不交换。继续比较索引1和2的值,将大值放在右侧。直到n-2和n-1比较完,第一轮比较完成。第二轮从索引0比较到n-2,因为最右侧n-1位置上已经是最大值了,依次类推,每一轮都会减少最右侧的不参与比较,直至剩下最后2个数比较
降序:
和升序相反
# 冒泡法代码:
nums_list = [[1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 9, 8, 5, 6, 7, 4, 3, 2], [9, 8, 7, 6, 5, 4, 3, 2, 1]]
nums = nums_list[2] #多个列表测试
lenges = len(nums)
count = 0
swap_count = 0
# print(lenges)
for i in range(lenges): # 最外层循环,一共多少个数循环比较多少次
for j in range(lenges -1 - i): #内层移动比较的数n-1个循环
count += 1 # 列表循环的次数
if nums[j+0] > nums[j+1]: # 升序排列
temp = nums[j] # 临时变量,做数据交换
nums[j] = nums[j+1]
nums[j+1] = temp
swap_count += 1 #数据交换的次数
print(nums)
print(count) #列表循环次数
print(swap_count) #列表中数据交换次数
---------------------------------------------------------------
# 冒泡法代码优化,加入检查机制,减少列表循环次数;即只有当前一个数大于后一个数,并且进入数据位置交换程序后改变了标计为Flase;如果前一个数就是小于后一个数不用进入位置交换程序,也就不用进行列表循环了
nums_list = [[1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 9, 8, 5, 6, 7, 4, 3, 2], [9, 8, 7, 6, 5, 4, 3, 2, 1]]
nums = nums_list[2]
lenges = len(nums)
count = 0
swap_count = 0
for i in range(lenges):
flag = True #初始检查标志
for j in range(lenges -1 - i):
count += 1
if nums[j+0] < nums[j+1]:#如果第一个数据小于第二个数据就进入数据交换程序
temp = nums[j]
nums[j] = nums[j+1]
nums[j+1] = temp
swap_count += 1
flag =False #发生了一次数据交换就改变标志
if flag: #flag为True就跳出,表示上一轮数据比较没有进行数据交换
break
print(nums)
print(count)
print(swap_count)
----------------------------------------------------------------
冒泡法总结:
(1. 冒泡法需要数据一轮轮比较
(2. 可以设定一个标记判断此轮是否有数据交换发生,如果没有发生交换,可以结束排序,如果发生交换,继续下一轮排序
(3. 最差的排序情况是,初始顺序与目标顺序完全相反,遍历次数1,...,n-1之和n(n-1)/2
(4. 最好的排序情况是,初始顺序与目标顺序完全相同,遍历次数n-1
(5. 时间复杂度O(n2)(n的平方)(两层循环,都随着n的增加而增加)
'写代码时,最多2个循环,要考虑时间复杂度,大于2个循环的代码非常低效,循环越少越高效'
8. 元组 -- 小括号定义
一个元组可以存储多个数据,元组内的数据是不能修改的;例如存储身份证号这种不能被修改的数据时,可以用元组来存储
元组中可以存放多种类型的数据,但尽量只存放一种类型的数据,避免后期取数时繁琐
元组特点:定义元组使用小括号,且逗号隔开各个数据,数据可以是不同的数据类型
# 元 组 中 的 数 据 不 能 被 修 改 #
#注意:一个元组中只有一个数据时一定要带上逗号,否则该单个数据是什么类型元组就会成为什么类型
元组的常见操作
#元组数据不支持修改,只支持查找
(1) 按下标查找数据
(2) index():查找某个数据,如果数据存在返回对应的下标,否则报错,语法和列表,字符串的 index 方法相同
(3) count():统计某个数据在当前元组出线的次数
(4) len(): 统计元组中数据的个数;在元组定义时就已经有个数信息了,直接查询,性能很快
时间复杂度:
index和count方法都是O(n)
随着列表数据规模的增大,而效率下降
示例:
# 如果元组中的数据包括列表,那就可以修改元组中列表的值,但谨慎修改
a = ('a', 'b', 'c', ['11', '22'])
print(a[3][1]) #元组中的列表可以更改,元组本身没有改变
a[3][1] = 'tom'
print(a)
补充:
命名元组 namedtuple 是个类
namedtuple(typename,field_names,verbose=False,rename=False)
命名元组,返回一个元组的子类,并定义了字段
field_names可以是空白符或逗号分割的字段的字符串,可以是字段的列表
9. 函数
9.1 函数的基本使用
#定义函数:
def 函数名():
函数体中的代码块
9.2 函数执行过程,文档注释
# 函数体内第一行多行注释即为函数的文档注释,主要写函数的描述信息
# 使用 连续的三引号编写帮助信息
def fun():
'''
帮助信息:计算1+1的和的函数
'''
a = 1 + 1
print(a)
# 调用函数
fun()
# 使用光标移动到函数名上,使用 ctrl+q 可以查看自己写的函数帮助信息
ctrl+q
9.3 函数参数1: 普通参数
作用域:变量起作用的范围
形参的作用域:只在定义函数的代码块中
函数参数的作用:可以传递数据给函数内部,增加函数的通用性
# 定义格式
def 函数名(形参1,形参2,...)
函数体代码块
# 带参数的函数调用,带上参数
函数名(实参1,实参2,...)
例:
def func(a,b):
"""
求和函数
"""
c = a + b
print(c)
# 函数调用并传参
func(2987,45348)
# 形参的作用域 只在定义函数的代码块中 ,形同的形参名在不同的函数中不影响
9.4 函数返回值
return 可以设置返回值并且中断函数,中断函数后返回一个结果,return后的代码不会执行
# 通过return给函数设置返回值
# return
1. 函数内部没有任何return语句,默认返回None,表示没有任何数据
2. return不设置返回值,默认有返回None
9.5 4种函数类型
# 无参数无返回
定义格式:
def 函数名():
函数体
函数调用:
函数名()
# 无参数有返回
定义格式:
def 函数名(形参1,从参2,...):
函数体
return 返回结果
函数调用:
返回值变量 = 函数名字(实参1,实参2)
# 有参数无返回
定义格式:
def 函数名(形参1,形参2,...):
函数体
函数调用:
函数名(实参1,实参2,....)
# 有参数有返回
定义格式:
def 函数名(形参1,形参2,...)
函数体
return 返回结果
函数调用:
返回变量 = 函数名(实参1,实参2,...)
# 例:有参有返回
def func_sum(n):
'''
实现1-n的累加
:param n: 累加数据的范围
:return: 返回累加结果
'''
i = 1
sum =0
while i <= n:
sum = sum + i
i += 1
return sum
ret = func_sum(100)
print(ret)
9.6 函数的嵌套调用
'''
函数的嵌套调用:函数里面调用其他函数
'''
# 定义func01函数
def func01():
print("函数开始调用")
# 定义func02函数,在代码中调用func01
def func02():
print("函数2开始调用")
func01()
# 调用函数
func02
示例:求3个数的平均值
# 定义函数1,求三个数的和
def number(a, b, c):
return a + b + c
# 定义函数2,调用函数1的值,求平均
def avg_number(a,b,c):
sum = number(a,b,c)
avg = sum /3
return avg
ret = avg_number(11,22,33)
print(ret)
9.7 局部变量和全局变量
# 局部变量:
1. 定义在函数内部的变量,仅在函数内调用
2. 局部变量中函数定义的形参,是一个局部变量,函数内部定义的变量也是局部变量
3. 局部变量的作用域只在函数内部
# 全局变量
1. 在函数外部定义的变量叫做 全局变量
2. 全局变量能够在所有的函数中进行访问(不修改)
3. 全局变量在第一个函数中被修改后,其他函数再去调用该变量时返回的都是修改后的结果
# 通过 global 声明修改全局变量
函数内修改全局变量:先 global 声明全局变量,再修改
# 关 键 字: global
例:
# 定义一个全局变量 num
num = 10
def doo():
"""
修改一个全局变量
:return: 无返回值
"""
global num
# ⬆️ 先声明要修改全局变量,关键字 global
num = 250
# ⬆️ 修改了全局变量
print(num)
# 函数调用
doo()
# 打印这个全局变量,发现已经在函数中修改了
print(num)
9.8函数参数2: 参数详解
'''
位置参数:按形参的位置,从左往右,一一匹配传递参数
关键字参数: 通过 形参=值 方式为函数形参传值,无需和形参位置意义对应
'''
#1. 位置参数:实参的位置顺序必须和形参 位 置 一一对应
#2. 位置参数:必须保证形参和实参的 个 数 保持一致
#3. 位置参数:在以位置给函数传参时 字符串类型 一定要和函数中定义的类型相同
例:
def foo(name, age, sex):
print("姓名:%s 年龄: %d 性别: %s" % (name, age, sex))
foo("小六",12,"男")
# 关键字参数:
#1. 函数调用时,通过形参=值方式为函数的形参传值
#2. 不用按照位置为函数形参传值,这用方式叫做关键字传参
# 注:形参不能重复赋值,关键字参数必须在位置参数的右边
关键字参数:
关键字=值
形参=值
例:
def foo(name, age, sex):
print("姓名:%s 年龄: %d 性别: %s" % (name, age, sex))
# 关键字传参
foo(age=20, name="小李", sex="女")
# 位置和关键字混合传参,位置参数在关键自左边
foo("小王", sex="男", age=21)
# 混合传参的错误示范
foo(name="小肖",18 ,sex="男")
SyntaxError: positional argument follows keyword argument
9.9 高阶函数:map reduce filter
1. map 用法
map() 会根据提供的函数对指定序列做映射
第一个参数 function 以参数序列中的每一个元素调用 function 函数,返回包含每次 function 返回值的一个新列表
示例:
# map 函数,对列表中的数做幂次方
my_list = [1,2,3,4]
def f(x):
return x ** 2
ret = map(f, my_list)
print(ret, list(ret))
# 对列表中首字母大写转换
m_list = ["rose", "tom", "mike", "jody"]
def foo(x):
return x[0].upper() + x[1:]
res = map(foo, m_list)
print(res, list(res))
2.reduce 用法
reduce() 函数会对参数序列中元素进行累计
函数将一个数据集中的所有数据进行下列操作:
1). 用传给 reduce 中的函数 function (有两个参数)先对集合中的第1,2个元素进行操作
2). 得到的结果再与第三个数据用 function 函数运算,最后得到一个结果
示例:
# 对集合进行累计操作
import functools
mm_list = [1,2,3,4,5]
def fo(x1, x2):
return x1 + x2
resu = functools.reduce(fo, mm_list)
# 匿名函数方式
resu = reduce(lambda x1, x2 : x1 + x2 , mm_list)
3. filter用法
filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回一个 filter 对象,如果要转换为列表,可以使用 list() 来转换
该接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判断,然后返回True或False,最后将返回 True 的元素放到新的列表中
示例:
ms_list = [1,2,3,4,5,6,7,8]
def ff(x):
return x % 2 == 0
result = filter(ff, ms_list)
print(list(result))
10.【重点】默认形参(缺省形参)
# 默认参数(缺省形参)
' 形参设置默认值则为缺省参数,也叫做默认参数
' 调用函数时,如果没有传入默认参数对应的实参,则使用默认值
' 默认参数必须在普通参数的后边,就是定义形参时,第一个形参必须是普通参数,不能给值
例: def fun_(a,b=10,c=20) #固定格式
print(a, b, c)
###「重点」 ###
# 在函数调用传实参时,实参都是从第一个形参开始赋值
11.【重点】*agrs 元组型不定长参数
形参变量名前面加上一个 * (星号),这个参数则为元 组型不定长 位置 参数
【 重 点 】
1.# 函数可以定义 不定长参数 , 用于接收 任 意 数 量 的 位 置 实 参
2.# 不定长参数本质时,将传递的 参 数 包 装 成 元 组
3.# 函数定义 形参 前加上一个 * ,这个参数就是不定长参数
例:
def foo(*agrs):
print(args,type(args))
# 函数调用
foo()
foo(1,2,3) # (1,2,3) <class 'tuple'>
# 类型是元组类型
12.【重点】**agrs 字典型不定长参数
定义参数时需要在形参名前添加** (两个星号),则为字典型不定长 关键字 参数
字典型可变形参必须在形参列表的后面
# 函数形参变量,前面有2个 * ,字典型不定长参数,也叫做 关 键 字 型 不定长参数
# 函数内部使用,无需加 *
# **kwargs,这个参数一定是放在最右边(一般这种形参名起名为: kwargs)
# 不定长参数,就是将传递的参数 包装成 字 典
例:
def fooo(name, **gel):
print(gel)
print(name)
print(type(gel))
# 实参传递是要用字典格式传参,使用的是关键字传参,与位置无关
fooo(name='xiaoming',age=18, city="beijing", sex="男")
返回结果:
{'age': 18, 'city': 'beijing', 'sex': '男'}
xiaoming
<class 'dict'>
13. 强化和进阶
13.1 组包和拆包
1. 组包 -- 只能组包成 元组
"组包,多变一"
" 等号 = 右边有多个数据时,会自动包装为元组
示例:
a = 1, 2.2, "猪"
print(a, type(a))
#返回结果:(1, 2.2, '猪') <class 'tuple'>
2. 拆包 --- 列表,字符串,字典,元组都支持拆包,字典拆包结果是key
# 如果 变量数量 = 容器长度 ,容器中的元素会一一对应赋值给变量
## 拆包时注意,需要拆的数据的个数要与变量的个数相同,否则程序会异常
### 除了对元组拆包之外,还可以对列表,字典等拆包
"拆包,一变多"
# 交换变量
# 函数同时返回多个数
例:
def foo():
return 1, 2, 3 # 1,2,3组包赋值给函数foo()
# 返回值直接做拆包
s1, s2, s3 = foo() # 这里的foo()是一个组包后的元组
print(s1, s2, s3) # 1 2 3
3. 字典元素拆包
# 注:取的值是字典的key
_codes = {'a': 1, 'b': 2, 'c': 3}
for name in _codes.items(): #先依次取出字典的key赋值给变量name
print(name)
key, value = name #将变量中的元组拆包,再分别赋值给变量
print(key)
print(value)
# 元素交换
a=10
b=20
a,b = b, a # 第一步:b,a组包成(b,a)
# 第二步:然后将(b,a)拆包分别赋值个a,b;此时b赋值给了a,a赋值给了b
print(a,b)
13.2 引用
# 引用:是一个变量或者值的另一个名字,又称别名
赋值本质:给右边的变量或值,起一个别名
# 可以使用 id函数 查看变量的引用地址,引用地址相等,说明指向同一个内存空间
每一次运行程序,每次地址都可能不一样
例如:
a = 10
print(id(a), id(10)) # 地址id一样,指向同一个内存空间
# 返回 4359929360 4359929360
说明:a是10的别名,10是a的引用,操作a就是操作10
13.3 可变和不可变类型
可变和不可变,指的是数据的内存地址的改变
1. 可变类型:列表,字典,集合
特性:在地址不变的情况下,可以修改内容 #(修改内容,内存指向不变)
2. 不可变类型:数字类型(int, bool, float), 字符串,元组
特性:在地址不变的情况下,不可修改内容; #(修改内容,内存地址指向改变)
' 不 可 变 类 型'
'修改数据,内存地址不变'
# 示例:集合和列表,集合
my_list = {1, 2, 3, 4}
print("my_list: ", id(my_list))
print(my_list)
my_list.add(5) # 集合添加数据
#my_list.append(5) # 列表添加数据
print("my_list: ", id(my_list))
print(my_list)
# 结果输出,修改数据内存地址不变
my_list: 4509703072 # 修改前数据的内存地址
{1, 2, 3, 4}
my_list: 4509703072 # 修改数据后的内存地址
{1, 2, 3, 4, 5}
'可 变 类 型'
'修改数据,内存地址会改变'
# 示例:字符串,元组,数据类型(int,bool,float)
a = 10
print("a:", id(a))
a = 20
print("a:", id(a))
# 输出,地址改变
a: 4484587024
a: 4484587344
a = (1, 2, 3)
print(a, id(a))
a = (4, 5, 6)
print(a, id(a))
# 输出,地址改变
(1, 2, 3) 4379709184
(4, 5, 6) 4379709376
13.4 range 函数
# range 函数 左闭右开区间
1. range 语法: for 变量 in range(开始位置,结束位置,步长)
2. range(起始,结束,步长):起始默认是0,步长默认是1
3. range() 方法可以生成一段左闭右开的 '整数' 范围
4. 特点1: 它接受的参数必须是整数,可以是负数,但不能是浮点数和其他类型
特点2:它是不可变的序列类型,可以进行判断元素,查找元素,切片等操作,但不能修改元素
特点3: 它是可迭代对象,却不是迭代器
# 示例1:
for 变量 in range(5): , range(5)序列范围,使用和切片一样,但是以逗号,隔开,打印结果为 0,1,2,3,4 (前闭后开区间的值)
# 示例2:
_sum = 0
for i in range(0,101):
_sum = _sum + i
print("_sum: ", _sum)
13.5 列表推导式
一行代码,快速生成列表的作用
能够使用列表推导式创建包含 1- 100 之间元素的列表
格式:[计算公式 for 循环体 if 判断]
# 通过列表推导式,实现上面的效果 [计算公式 for循环体]
1. for i in range(1, 101), 取出1,放在i变量中,i追加到列表
2. 循环下一步,取出2, 放在i变量中, i追加到列表
# 重复,直到退出循环
# 列表推导式:
list = [i for i in range(1,101)]
print(list)
示例2:取出100内的偶数添加到列表中
list = [ i for i in range(101) if i % 2 == 0 ]
print(list)
# 注:中括号里第一个 i 会接受 i % 2 == 0 的结果,是一个赋值过程
list = [i * 2 for i in range(11) if i % 2 == 0]
print(list)
# 结果:[0, 4, 8, 12, 16, 20]
示例:
# 一个for循环
my_ls = [x for x in range(4)]
# 两个for循环
my_ls = [(x,y) for x in range(1,3) for y in range(2,4)]
# 三个for循环
my_ls = [(x,y,z) for x in range(1,2) for y in range(1,3) for z in range(1,4)]
面试题:
将100以内的数字列表每3个数放进一个小列表中,小列表组成一个大列表输出
a = [x for x in range(1,101)]
b = [a[x:x+3] for x in range(0,len(a),3)]
打印 b
13.6 匿名函数
通过匿名函数编写简单的函数
# 关 键 字 : lambda
##. 格式:
'lambda 形参1,形参2,... : 单行表达式 或者 函数调用'
'⬆️只能写一行表达式'
特点
1. 匿名函数是简单普通函数的简洁写法
2. 匿名函数没有函数名字
3. 匿名函数是有返回值的,默认带了return做返回,函数名()就是调用函数
4. 一种用法是,给这个匿名函数设置一个接受值 ret ,ret = (lambda: 1+1)()
5. 另一种用法是给这个接受值赋值给一个变量,func = ret() , print(func)
匿名函数的优点:
1. 不用取名称,因为给函数取名是比较头疼的一件事,特别是函数比较多的时候
2. 可以直接在使用的地方定义,如果需要修改,直接找到修改即可,方便以后代码的维护工作
3. 语法结构简单,不用使用def 函数名(参数名):这种方式定义,直接使用lambda 参数:返回值 定义即可
# 无参无返回值
ret = (lambda: 1 + 2)()
print(ret)
'有参有返回 示例'
ret = (lambda a, b : a + b )(20, 30)
# (关键字 形参1,形参2 : 函数内代码块)(实参1,实参2)
print(ret())
# 等价于下面的写法
def ret(a,b):
return a + b
foo = ret(20, 30)
print(foo)
# 示例1:
1.# 给匿名函数起一个变量名,变量名() 就是调用函数
func = lambda: 1 + 1 # 给匿名函数起一个函数名字叫做 func
# 函数体就是返回值的内容,无需return
ret = func() # 返回值变量名 = 变量名()
# 匿名函数调用,变量名() 就是调用函数
# 示例2:
def foo(fn):
ret = fn() # 函数里面的形参是一个匿名函数
print(ret)
foo(lambda: 1 + 1) # 在调用foo()函数是,实参传的是一个匿名函数
13.7 递归函数
# 递归函数的特点
'函数自己调用自己'
'一定要有出口'
1. 函数递归:函数调用自己,了解即可,尽量通过画图,理解流程
2. 递归函数一般会在特定情况下, 不再调用函数本身
'示例 用递归函数实现阶乘:
5! = 5*4*3*2*1
n! = n*(n-1)*(n-2)...1
def foo(n):
if n == 1: # 递归函数的出口
return 1
else:
ret = n * foo(n - 1) # 函数嵌套,嵌套的函数是自己本身
return ret
_ret = foo(5)
print(_ret)
'注:若没有递归函数的出口,就会超过python解释器的递归最大深度996,解释器就会终止递归'
13.8 enumerate 函数和 del 函数
1. 通过 for 配合 enumerate 遍历容器同时获取元素索引位置和元素
语法: for i, value in enumerate(容器):
'总结:相当于遍历查询列表中元素的 索引位置,和元素内容 '
例如:
my_list = [{'name': "xiaoming", 'age': 20, 'sex': "男"},
{'name': "xiaozhang", 'age': 30, 'sex': "男"}]
for i, item_dict in enumerate(my_list):
print(i, item_dict)
# 返回结果
# 元素的索引位置 元素内容
0 {'name': 'xiaoming', 'age': 20, 'sex': '男'}
1 {'name': 'xiaozhang', 'age': 30, 'sex': '男'}
2. 通过del删除列表元素:
语法: del 列表[索引]
' 总结: 通过索引删除列表中对应的元素'
del my_list[0]
或者写法:
del(my_list[0])
13.9 学生名片管理系统
14. 文件操作
14.1 文件介绍
文件的作用:把一些长期存放起来,可以让程序下一次执行的时候直接使用,而不必重新制作一份
'数据持久化存储'
14.2 文件的打开和关闭
# 文件的操作流程
1. 打开或者新建一个文件
2. 读/写 数据
3. 关闭文件
# 打开文件
在python中,使用open函数,可以打开一个已经存在的文件,或者创建一个不存在的新文件
格式:
文件变量 = open(路径+文件名称,访问模式) # 文件名称和访问模式都是字符串格式,不写路径就是当前目录
'''
访问模式:
r 只读模式,文件不存在会报错,默认打开方式
w 只写模式,文件存在会先清空,不存在会创建新文件
'''
文件中包含中文时,打开文件时设置文件编码,windows的python默认打开文件的编码是GBK:
f = open('xxx.txt'.'r',encoding = "utf-8")
'关键字 encoding'
# 关闭文件 写完文件一定要关闭,否则内容不会保存
注: 如果程序结束也会自动关闭文件的,但一定要程序执行结束,否则不会保存
1.手动关闭
关闭文件的作用是,为了释放资源
'语法格式: 文件变量.close()
2.自动关闭
with open("文件名", "文件操作") as 文件别名(文件变量):
# 文件操作
pass
示例:
with open("test.txt", "w") as f: # 打开的结果赋值给f变量,是文件变量的别名
pass # 文件操作完后会自动关闭
执行完缩进代码,会自动关闭文件
14.3 文件的读写
# 写数据
使用 write() 可以完成向文件写入数据
'语法格式:文件变量.write(编写文件内容)
注:写文件前要先用open打开文件
示例:
f = open("sss.txt", 'w')
f.write('hello python')
...
f.close()
示例:
a = open("a.txt", "w") # 创建一个新文件
a.write("hello ljjjjj") # 向新文件中写内容
a.close()
a = open("a.txt", 'r') # 只读方式打开新文件
b = a.read() # 读新文件并赋值给b
a.close()
print(b) # 打印变量b
# 读数据
使用read() 可以读取文件
'语法格式:内容变量 = 文件.read(n)
注:n 为读取多少个字符数,不设置则全部读取
## readlines 文件中如果有回车行也会计算为行,换行符作分隔符
readlines() 函数可以一次全部读出,读取所有的行,'按行作分隔条件',返回列表(每行内容是一个元素)
'语法格式:内容列表变量 = 文件变量.readlines()
# 缺点总结:在使用readlines 读取一个多行文件时,返回的结果会是一个组包后的一个列表 ['我\n', '在\n', '学\n', 'ptthon\n', '啊'] ,而不是原格式的多行内容
with open("xxx.txt", "w") as f:
f.write("我\n在\n学\nptthon\n啊")
# for 循环遍历读取多行内容
f = open("123.txt",'r')
b = f.readlines()
print(b)
for row in b :
# print(row)
print(row, end="")
f.close()
## readline 文件中有回车会包含,换行符作分隔符,结果是一个列表
readline() 每次读取一行数据,如果文件是多行内容,需要重复执行此函数
'语法格式:内容变量 = 文件变量.readline()
# 缺点总结:在使用readline读取文件时,如果文件中的内容是多行的,readline却只会读取第一行,所以就要用下述 while 循环来读取整个文件的内容,并且按照换行输出
示例:循环逐行读取文件内容
f = open("123.txt",'r')
while True:
ret = f.readline()
# if ret == "": # 读到空时执行break
if not ret: # true 取反判断,读到文件为空时跳出
break
print(ret)
f.close()
14.4 访问文件 r, w, a 的区别
# 文件访问模式
r 只读,默认模式,文件不存在会报错
w 只写,文件存在先清空,不存在先创建
a 追加写,文件存在则追加内容,不存在会创建新文件
# 绝对路径和相对路径
open 第一个参数说明:
open 第一个参数,实际上是 (路径 + 文件名)
路径分为:绝对路径和相对路径
'绝对路径:是只文件在硬盘上真实存在的路径,是电脑完整的路径
注意:写代码的时候,windows下路径的 \ 需要改为 \\ 或者改为 /
一般而言,写程序很少使用到绝对路径,因为程序在本机运行可以找到这个绝对路径,但是,把程序拷贝给别人运行,别人电脑不一定有这个路径
'相对路径:相对于自己的目标文件位置
通常使用相对路径
注意:
# ../1.txt: 上一级路径下的1.txt
# 1.txt:等价于./1.txt,当前路径下的1.txt
14.5 应用:用python实现文件备份案例
版本一:
版本二:读取用户输入的文件名对以存在的文件进行备份
# 对文件名进行切片拼接
old_file_name = input("输入需要拷贝的文件名: ")
pos = old_file_name.rfind(".")
l_file_name = old_file_name[:pos]
r_file_name = old_file_name[pos:]
new_file_name = l_file_name + "[备份]" + r_file_name
# 开始备份文件
old_file = open(old_file_name, 'r')
new_file = open(new_file_name, 'w')
while True:
ret = old_file.read(1024) # 一次读1024个字节
if ret:
new_file.write(ret)
else:
break
old_file.close()
new_file.close()
版本三:利用python备份二进制格式文件
示例 对 .mp4结尾的文件进行备份
# 对文件名进行切片拼接
old_file_name = input("输入需要拷贝的文件名: ")
pos = old_file_name.rfind(".")
l_file_name = old_file_name[:pos]
r_file_name = old_file_name[pos:]
new_file_name = l_file_name + "[备份]" + r_file_name
# 开始备份文件,为了处理任何格式文件,
# 就以二进制格式读文件 rw ,和 二进制格式写 wb 文件
old_file = open(old_file_name, 'rb')
new_file = open(new_file_name, 'wb')
while True:
ret = old_file.read(1024) # 一次读1024个字节
new_file.write(ret)
if not ret:
break
old_file.close()
new_file.close()
14.6 文件相关操作
文件的重命名,删除等一系列操作,是使用python中 os 模块中的功能
1)导入模块,只需要导入一次即可
import os
2)使用os中的方法,完成功能
# 功能
1. 文件重命名
os模块中的 rename() 进行重命名操作
语法格式: os.rename(旧的文件名,新的文件名)
2. 删除文件
os模块中的 remove() 进行文件的删除,但不能删除文件夹
语法格式: os.remove(待删除的文件名)
3. 创建空文件
创建文件夹,只能创建文件夹,不能创建普通文件
语法格式:os.mkdir(文件夹的名字)
4. 删除空文件夹
删除文件夹,只能删除空的文件夹
语法格式: os.redir(待删除文件夹的名字)
5. 获取当前目录
获取当前工作的路径
语法格式: 路径变量 = os.getcwd
6. 改变默认目录
改变默认目录,切换指定的路径
语法格式:os.chdir(改变的路径)
7. 获取目录列表
获取某个目录的文件信息,获取文件夹或文件的名字
语法格式: 目录列表变量 = os.listdir(指定某个目录)
如果不指定目录,默认当前路径
8. 判断文件是否存在
语法格式:os.path.exists(需要判断的文件)
如果文件存在返回 True , 如果文件不存在返回 False
14.7 文件版学生名片管理系统
### 字符转换 ###
# str(容器变量) 将容器变量转换成字符串类型'
user_list = [{'name': 'tom', 'age': 20, 'tel': '139'}]
my_list = str(user_list)
# eval(字符串内容) 类型转换,括号中看着像什么数据类型就转换成什么数据类型
user_list = "[{'name': 'tom', 'age': 20, 'tel': '139'}]"
new_list = eval(user_list)
print(new_list, type(new_list), new_list[0], type(new_list[0]))
输出结果:
[{'name': 'tom', 'age': 20, 'tel': '139'}] <class 'list'> {'name': 'tom', 'age': 20, 'tel': '139'} <class 'dict'>
# 将学生列表写入文件
with open("stu_info.txt", "w") as file:
file.write(str(user_list))
14.8 字符串,容器类型相互转换
str(容器变量) 将容器变量转换为一个字符串
eval(字符串内容) 返回传入字符串内容的结果,字符串里面是什么类型就转换成什么类型
os 模块的一般操作
文件重命名:os.rename(旧的文件名,新的文件名)
改变默认目录:os.chdir(改变的路径)
获取目录列表:目录列表变量 = os.listdir(指定某个目录)
判断文件是否存在:os.path.exists(需要判断的文件)
15. 面向对象1 : 类和对象,魔法方法
15.1 理解面向对象
python 中一切皆对象,是一门面向对象的语言 !
# 面向对象和面向过程的区别
面向过程:把编程的任务划分为一个一个的步骤,然后按照步骤分别去执行
面向过程的思想:需要实现一个功能时,都需要开发按步骤和过程来进行开发,没一个步骤都亲力亲为
# 理解面向对象
总结:面向对象就是将编程当成是一个事物,对外界来说,事物是直接使用的,不用去管他内部的情况。而编程就是设置事物能够做什么事
化简代码的作用,面向对象是一种抽象化的编程思想,很多编程语言中都有的一种思想
15.2 类和对象
类和对象的关系:
在面向对象编程过程中,有两个重要组成部分:'类' 和 '对象'
'类和对象的关系: 用类去创建一个(实例化)对象'
面向对象就要先创建类,再用类去创建对象,对象就可以实现一些功能
15.2.1 类 -- 类是由方法(函数)和属性(变量)构成
类是对一系列具有共同'特征'和'行为'的事物的统称,是一个'抽象的概念',不是真实存在的事物。
'特征'即是属性--变量
'行为'既是方法--函数
对象的公共属性定义为类,对象的抽象化是类
某些事物的抽象化特征,就是类,例如所有的汽车,而自己每天开的车就是类,因为是具体的'实物'
15.2.1 对象
对象是'类创建出来的真实存在的事物',例如:洗衣机
注意:开发中,先有类,再有对象
用类来创建对象
# 具体某个东西就是对象,而对象的抽象就是类
例如:'所有电脑,所有联想电脑,所有苹果电脑都是类',而具体到我自己手上的电脑实物就是对象,'实例化的东西就是对象'
类是对象的模板(不占内存空间),对象是类的实例(占内存空间)
类相当于图纸,对象相当于根据图纸制造的实物
15.3 面向对象的实现方法
15.3.1 定义类
python2 中类分为:经典类和新式类
''' 语法
class 类名(object):
def 方法名(self):
pass
.....
'''
# 注意: 类名要满足标识符命名规则,同时遵循 大 驼 峰 命 名 习 惯
# object 是所有类的祖先
新式类语法:
class 类名(object):
def 方法名(self):
pass
经典类语法:
class 类名:
代码
....
15.3.2 创建对象
对象又名 实例
# 语法:
对象名 = 类名()
15.3.3 self
# self 指的是调用该函数的对象;虽然定义方法时设置第一个参数 self,但是 调用方法时不要传递对应self的参数,解释器自动处理
# 在python类中规定,实现方法的第一个参数就是实例对象本身,并且约定俗成,把其名字写为self
# 某个对象调用其方法时,Python解释器会自动把这个对象作为第一个参数传递给方法
-
通俗理解:哪个对象调用该方法,该方法中self就是这个对象
【self 的作用】:
在方法中使用
self,可以获取到调用当前方法的对象,进而获取到该对象的属性和方法 self作用:为了区分不同对象的属性(变量)和方法(函数)
'self是什么:哪个对象调用方法,方法中的self就是这个对象本身
'self作用:区分不同对象的属性和方法
# 示例:
class Washer():
def wash(self): # 定义Washer类中的方法,self是默认属性
print('洗衣服')
print(id(self)
a = Washer() # 将类赋值给对象a
a.wash() # 调用对象中的属性和方法
b = Washer() # 将类再一次赋值给对象b
b.wash() # 调用对象中的属性和方法
返回结果中,两个对象调用一个类的内存地址是不同的
说明:一个类可以创建多个对象,多个对象调用函数时,self地址是不同的
print(id(a))
# 此时会发现,id(self)的返回地址和id(a)返回的地址相同,说明是一个东西
类作为对象的模具,根据类可以创建多个对象
15.4 添加和获取对象属性
属性即是特征,比如:洗衣机的长,宽,高
对象属性既可以在类外面添加和获取,也能在类里面添加和获取
15.4.1 类外面添加对象属性
# 语法
对象名.属性名 = 值 # 第一次赋值是定义,第二次赋值就是修改
例如:
a.height = 800
a.width = 500
15.4.2 类的外面获取对象属性
# 语法
对象名.属性名
例如:调用对象属性
print(f'a洗衣机的宽度是{a.width}')
print(f'a洗衣机的高度是{a.height}')
15.4.3 类里面获取对象属性
# 语法: self.属性名
示例:
class Washer(): # 创建类
def print_info(self): # 创建类的属性和方法
print(f'a 洗衣机的宽度是{self.width}') #类里面获取对象的属性
print(f'a 洗衣机的高度是{self.height}')
a = Washer() # 创建对象(调用类)
a.width = 500 # 添加属性
a.height = 800
a.print_info() # 对象调用方法
15.5 魔法方法
'在Python中,所有以 __ 双下划线包起来的方法,都统称为 Magic Method ,中文称 魔法方法
'魔法方法是系统提供好的方法名字,用户需重新实现它
'魔法方法一般情况下无需手动调用,在合适的时候自动会调用
在Python中,__xx__() 的函数叫做魔法方法,指的是具有特殊功能的函数。
# 魔法方法就是 特 殊 的 函 数, 名字一般是固定的,不被对象调用,而被系统自己调用
15.5.1 魔法方法: _init_()
__init__() 方法的作用:初始化对象;对象创建时被系统自动调用,是给对象添加属性用的
class Washer():
def __init__(self): # 添加初始化属性
self.width = 500
self.height = 800
print('init方法调用了')
def print_info(self): # 添加方法
print(f'a 洗衣机的宽度是{self.width}') #调用初始属性
print(f'a 洗衣机的高度是{self.height}')
a = Washer()
a.print_info()
# 使用类创建对象会经历两个步骤:是由系统自动完成的
'1. 开辟空间:系统会自动调用__new__魔法方法(另一种魔法方法)
'2. 对象初始化:系统会自动调用__init__魔法方法
'注意:'
__init__() 方法,在创建一个对象时默认被调用,不需要手动调用
__init__(self) 中的 self 参数,不需要开发者传递,python解释器会自动把当前的对象引用传递过去
15.5.2 带参数的 _init_()
作用是:一个类可以创建多个对象,针对多个对象设置不同的初始化属性就用带参数的 init 定义。
__init__(self)除了默认参数self,还可以设置任意个数的自定义参数,例如:__init__(self,x,y,z)- init方法 设置的自定义参数必须和创建对象时传递的参数保持一致,例如:
对象变量名 = 类名(x,y,z) - 开发者可以 设置自定义参数,为对象的默认属性提供 不同的初始值
class Washer(): # ⬇️带参数的init
def __init__(self, width, height): # 添加自定义对象形参,来接收数据
# 初始化属性,添加实例属性⬇️
self.width = width # 形参赋值给变量
self.height = height
def print_info(self): # 定义方法
print(f'a 洗衣机的宽度是{self.width}') # 调用变量
print(f'a 洗衣机的高度是{self.height}')
# 1 个类创建多个对象并且属性值不同,再给对象传值
# 创建对象1
a = Washer(500, 800) # 给类的形参传值
a.print_info()
步骤分解:系统使用对象a调用__init__()方法,并且传递参数a给self,500参数给width,800给height
# 创建对象2
b = Washer(300,400)
b.print_info()
15.5.3 魔法方法 : _str_()
当使用print输出对象的数据时,默认打印对象的内存地址。如果类定义了_str_ 方法,那么就会打印在这个方法中 return 的数据
_str_( ) 方法的返回值必须是字符串类型
_str_( ) 方法作用主要返回对象属性信息,print(对象变量名) 输出对象时,直接输出_str_( )方法返回的描述信息
"""
__str__方法:
1. 返回值必须是字符串类型
2. print(对象变量名) 对象变量名的位置替换为__str__()方法返回值的内容
3. __str__方法大部分情况下是不需要定义除了self之外的形参
"""
'出发点:类创建对象后,打印该对象一般返回系统的内存地址信息,而设置了__str__()这个特殊函数后,打印对象就返回的是特殊函数中的return设定值'
# 一般 str中存放的是一些解释说明的文字,并且返回这些文字,而不是返回内存地址信息
class Washer():
def __init__(self, width, height):
# 添加实例属性
self.width = width
self.height = height
def __str__(self): # 定义str魔法方法
return '这是a 洗衣机的说明书,高%d,宽%d' % (self.width, self.height)
return f"高{self.height},宽{self.width}"
a = Washer(300, 400)
print(a) # 这是a 洗衣机的说明书
15.5.4 魔法方法:_del_()
当删除对象(生命周期结束)时,python解释器也会自动调用 _del_() 函数方法,并执行该函数内的代码块,做一些清理工作
# 删除对象时的返回值
class Washer():
def __init__(self, width, height):
# 添加实例属性
self.width = width
self.height = height
def __del__(self):
#print(f'{self}对象已经删除')
print('对象已经删除')
a = Washer(300, 400) # 调用类创建对象 a
del a # 删除对象 a
# 返回: 对象已经删除
# 或者下种方式:在函数里面定义对象调用类
当下列函数调用完毕后,里面创建的对象会销毁,生命周期结束,会自动调用__del__()方法
def foo():
dog = Washer()
# 以下三种情况会调用此魔法方法
1. 函数调用结束,函数中调用的对象会被销毁
2. del 对象,销毁对象
3. 程序结束后,所有对象会被销毁
总结:
__init__: 创建对象后自动调用方法,实现给对象添加属性和属性初始化
__str__ : print(对象)时,提交替换对象__str__的返回值
__del__ :销毁对象时,自动调用,做清理动作
主逻辑:创建对象,调用方法
需求分析 ---> 找对象 ———> 抽象类属性和类方法 --—>代码实现类
15.5.5 魔法方法:_call_()
- 包含此方法的类创建出来的实例化对象可以当做函数来调用
15.5.5.6 魔法方法:_new_()
- new 方法负责将类实例化为对象,init方法负责将实例化出来的对象进行初始化
16 . 面向对象2: 继承,属性,方法
16.1 私有权限
封装就是定义类的属性和方法
面向对象的三大特性:封装,继承,多态
# 面向对象的三大特性:封装,继承,多态
'面向对象的封装特性:
1. 将属性和方法放到一起封装成一个整体(就是类),然后通过实例化对象来处理(访问类中的方法)
2. 对类的属性和方法增加访问权限控制
# 私有属性:只能在 类 内 部 访 问,类的外部无法访问
# 私有属性的定义方法:在属性(变量)名前面加2个下划线'__',则表明该属性是私有属性,否则是共有属性
# 私有方法:只能在本类的内部访问,在类外面无法直接访问
# 私有方法(函数)的定义:跟私有属性类似,在方法名前面加上2个下划线'__',则表明该方法是私有方法
# 在类内部调用实例方法的语法格式:self.方法名()
示例:
class Dog(object):
def __init__(self):
self.__baby_count = 0 # 私有属性(变量)
self.age = 1 # 共有属性(变量)
def __level(self): # 私有方法
print("ViP")
def prin_info(self):
print(self.__baby_count) # 共有方法中调用私有属性
self.__level() # 共有方法中调用私有方法
dog = Dog()
print(dog.__baby_count) # 私有属性不能方法,会报错找不到变量
print(dog.age) # 共有属性可以访问
dog.prin_info() # 通过共有方法调用私有属性,和私有方法
16.2 继承 (py特点继承、封装、多态)
16.2.1 继承介绍
# 概念:
在程序中指的是'类与类'之间的关系
# 形容:
站在'父类'的角度来看,'父类'派生出'子类'
站在'子类'的角度来看,'子类'继承于'父类'
'父类'也叫做'基类','子类'也叫做'派生类'
# 继承的作用:
继承:子类直接具有父类的属性和方法
作用:解决代码重用问题,提高开发效率
# 继承的语法:
class 子类名(父类名):
pass
# 注意:
子类对象调用方法有一个就近原则:
如果本类能找到方法,直接调用本类的方法即可
如果本类找不到,则调用父类继承过来的方法
16.2.2 单继承和多层继承
# 单继承:子类只继承一个父类(子类中指调用了一个父类)
# 多层继承:继承关系为多层传递 (孙子类中调用子类,子类调用父类,每层只传递一个类,层层传递)
示例:多层继承
class Animal(object):
def eat(self):
print('吃')
class Dog(Animal):
def drink(self):
print("喝")
class Bosi_dog(Dog):
def bandian(self):
print("wangwnagwang")
a = Bosi_dog()
a.bandian()
a.eat()
a.drink()
16.2.3 多继承
# 多继承:所谓多继承,即一个子类继承有多个父类,并且具有相同的特征
# 多继承的语法格式:
class 子类名(父类1,父类2,....)
pass
# 类的继承顺序:自己不用定义此方法,解释器自己定义的,可以在调用时直接调用
查看类的继承顺序:类名.__mro__
示例:查看继承顺序
class SmallDog(object):
def eat(self):
print("小狗吃小东西")
class BigDog(object):
def eat(self):
print("大狗吃肉")
class SuperDog(SmallDog, BigDog):
pass
print(SuperDog.__mro__)
#返回一个列表,即继承顺序
(<class '__main__.SuperDog'>, <class '__main__.SmallDog'>, <class '__main__.BigDog'>, <class 'object'>)
# 子调用父类同名方法
1. 默认调用情况:如果继承过来的2个父类的方法同名,默认调用先继承父类的同名方法'(先继承先调用)'
2. 子类调用父类同名方法
子类调用父类同名方法:
1) 父类名.同名方法(self, 形参1, ...) : 调用指定父类
2) super(类名,self).同名方法(形参1,形参2,...) :调用继承顺序中类名的下一个类的同名方法
3) super().同名方法(形参1,...) : 调用先继承父类的同名方法
' 当一个子类中有多个继承父类时,调用同名方法时,就需要考虑继承顺序来调用 '
用__mro__获得继承循序,按照继承进行调用同名方法
16.2.4 私有和继承
# 私有和继承
父类中的私有方法(2个下划线),属性不能直接继承使用,只能在父类中被调用,如果这个调用私有属性或方法的是共有方法,就可以被外界调用私有属性
可以通过调用继承的父类的共有方法,'间接'的访问父类的私有方法和属性
# 私有属性和方法的继承访问示例:
class SmallDog(object):
def __init__(self):
self.__la = "小狗拉" # 父类的私有属性
def __eat(self): # 父类的私有方法
print("小狗吃小东西")
def drink(self): # 父类的公有方法
self.__eat() # 调用了父类的私有方法和属性
print(self.__la)
class SuperDog(SmallDog): # 子类继承父类
pass
dog1 = SuperDog()
print(dog1)
dog1.drink() # 子类对象访问父类的公有方法,调用父类的私有属性和方法
16.3 重写父类方法
# 父类的方法不能满足子类的需要,可以对父类的方法进行重写,重写父类的目的是为了给他扩展功能
# 在子类中定义了一个和父类同名的方法(参数也一样),即为对父类的方法重写
# 子类调用同名方法,默认只会调用子类的
# 子类调用父类的同名方法 ,在类内部
子类中调用父类同名方法:(三种方法各有优缺点)
1. 父类名.同名方法(self,形参1,...)
2. super (子类名,self).同名方法(形参1,...)
3. super().同名方法(形参1,...): 是方法2的简写,'推荐写法‘
# 重写示例:子类重写父类的属性和子类的同名方法中调用父类的方法
# 父类
class Animal(object):
def __init__(self):
self.type = "动物" # 父类的属性
print("父类的init") # 父类的打印
def eat(self): # 父类的eat方法1
print("父类的逛吃逛吃逛吃")
def print_info(self): # 父类的方法2
print("父类的方法")
# 子类
class Dog(Animal):
def __init__(self):
self.type = "狗" # 可以在子类重写了父类同名的属性
print("子类的调用")
def eat(self): # 与父类方法同名的子类方法
print('子类小狗吃吃吃')
super().eat() # 在同名子类中调用父类的方法
def print_info(self): # 与父类方法同名的子类方法
super().__init__() # 在同名方法中调用父类的方法
super().eat() # 在同名方法中调用父类的方法
a = Dog()
a.eat()
a.print_info() # 子类中调用子类的方法
# 结果:
子类的调用
子类小狗吃吃吃
父类的逛吃逛吃逛吃 # 父类方法结果
父类的init # 父类方法结果
父类的逛吃逛吃逛吃
16.4 多态:同一个 函数 的不同表现,
python多态是伪多态
# 多态;python的多态是伪多态
多态:多种形态,调用同一个'函数',传递不同参数实现不同表现
因为python是动态语言,站在用户的角度,本身就是多态,不存在非多态的情况
实现多态的步骤:'先继承,后重写父类的方法,再调用'
1)实现继承关系
2)子类'重写父类'方法
3)通过对象调用该方法
# 多态:同一个函数,根据传参不同实现不同的结果,就是多态
class Animal(object): # 定义父类
def eat(self):
print("吃东西")
class Dog(Animal): # 定义子类1,继承父类
def eat(self):
print("吃骨头")
class Cat(Animal): # 定义子类2,继承父类
def eat(self):
print("吃鱼")
def func(temp): # 定义一个函数,并自定义形参
temp.eat()
a = Dog()
b = Cat()
func(a) # 给函数传入新参,子类1
func(b) # 给函数传入新参,子类2
# 同一个函数根据传入的实参(实参是2个不同的对象),而结果不同,这就叫做多态
16.5 实例属性,类属性
python 是一门纯面向对象的语言,一切皆对象
# 1. 专业名词说明
在python中"万物皆对象"
通过类创建的对象 又称为'实例对象,对象属性 又称为 实例属性'
类本身也是一个对象,执行class语句是会被创建,称为 '类对象' 为了和实例对象区分开来,我们习惯叫类
# 2. 实例属性
通过 __init__ 方法里面给实例对象添加的属性
在类的外面,直接通过实例对象添加的属性
'实例属性'必须通过'实例对象' 才能访问
# 3. 类属性
类属性就是 '类对象' 所拥有的属性,它被 '该类的所有实例对象共同所有'(相当于这个类里面的全局变量)
定义在'类里面,类方法外面'的变量就是'类属性'
类属性可以使用'类名' 或'实例对象' 访问,'推荐使用类名访问'
# 4 类属性和实例属性的区别
类属性就是'类对象'所拥有的属性,它被 '该类的所有实例对象所共有'
'实例属性'要求'每个对象'为其'单独开辟一份内存空间',只属于某个实例对象的
# 5 *注意点:修改类属性时
类属性 只 能 通 过 类 对 象 修 改,不 能 通 过 实 例 对 象 修 改;
例:dog1 = Dog() # 对象变量名 = 类名
Dog.name = “狗” # 通过类名修改
dog1.name = “小狗” # (不能)通过实例对象名修改
'因为对象名dog1来修改name属性,其实不是修改,而是添加了一个同名的属性,跟类里面同名的name属性,其实不是同一个'
# 6 类属性和实例属性同名
如果类属性和实例属性同名,实例对象名只能操作实例属性
结论:'操作类属性建议使用类名',避免不必要的麻烦
'类名操作类属性,实例名操作实例属性'
示例:
class Dog(object):
# 定义类属性: 统计使用Dog类创建类多少对象,是所有对象共同的
count = 666
def __init__(self):
# 实例属性
self.count = 250
d1 = Dog()
print(d1.count, Dog.count) #(实例属性,类属性)各不同
结果:250 666
# 7 私有类属性
类属性也可以设置为'私有',前面添加两个下划线__
# 类属性和实例属性的示例
class Dog(object):
count = 0 # 定义类属性: 统计使用Dog类创建类多少对象,是所有对象共同的
def __init__(self, _name):
self.name = _name # 定义示例属性:每个实例对象特有的
Dog.count += 1 # 每次调用__init__时,count计数加1
print(Dog.count)
dog1 = Dog("狗1")
print(dog1.name, Dog.count)
dog2 = Dog("狗2")
print(dog1.name, Dog.count)
dog3 = Dog("狗3")
print(dog1.name, Dog.count)
# 结果
0
狗1 1
狗1 2
狗1 3
# 总结:狗1,狗2,狗3 是实例属性,属于每个实例(对象)的,count是类属性是所有实例(对象)共有的
16.6 类方法,静态方法
类方法目的:在不定义对象实例的情况下,调用类里面的方法
# 1.类方法
'类对象所拥有的方法',主要为了在没有创建实例对象前提下,处理类属性
需要用装饰器 @classmethod 来标识其为类方法
对于类方法,'第一个参数必须是类对象(代表类)',一般以 cls 作为第一个参数,这个参数不用人为传参,解释器会自动处理
'''
类方法:为了方便处理类属性
1. 用装饰器 @classmethod 来标识其为类方法
2. 一般以 cls 作为第一个参数,代表当前这个类,这个参数不用人为传参,解释器自动执行
3. 类方法调用:
3.1 类名.类方法() 推荐用法
3.2 实例对象名.类方法()
'''
示例:在创建对象的情况下,调用类里面的方法
方法一:推荐用法
class Dog(object):
count = 1 # 类属性
@classmethod
def print_count(cls): # 实例方法:创建实例对象后才能调用的方法
print(Dog.count)
Dog.print_count()
方法二:不推荐用法
定义了对象d1来调用类中的方法,其中cls会把d1对象替换
class Dog(object):
count = 1 # 类属性
@classmethod
def print_count(cls): # 实例方法:创建实例对象后才能调用的方法
print(cls.count)
d1 = Dog()
d1.print_count()
# 2 静态方法
#类中定义函数时,不设置任何形参,就是静态方法#
需要通过解释器 @staticmthod 来进行修饰,'静态方法默认情况下,既不传递类对象也不传递实例对象(形参没有self/cls)
当方法中 既不需要使用实例对象,也不需要使用类对象 时,定义静态方法
取消不需要的参数传递,有利于 '减少不必要的内存占用和性能消耗'
静态方法 也能够通过 实例对象 和类对象(类名)去访问
'''
静态方法:
1. 需要通过解释器@staticmethod 来进行修饰默认情况下
2. 既不传递类对象也不传递实例对象(形参没有self/cls)
3. 静态方法调用
3.1 类名.静态方法() 推荐用法
3.2 实例对象名.静态方法()
'''
示例:
class Dog(object):
# 需要通过装饰器进行修饰
@staticmethod
def foo(): # 函数括号内没有任何形参
# 实例属性:self.属性
# 类属性: cls.属性
print("一个与示例属性和类属性无关的函数")
Dog.foo()
16.7 总结
类方法,示例方法,静态方法的区别
(就是类中的函数定义方法的区别,根据是否需要形参或者被对象还是类调用而做区别)
#定义区别
class 类名(object):
def 实例方法名(self): #定义实例方法,可以通过对象所执行
pass
@classmethod # 装饰器
def 类方法名(cls): #类方法,不定义对象,在类外面直接用类名来调用
pass
@staticmethod # 装饰器
def 静态方法名(): #静态方法,没有形参,也不需要形参时的定义方法,简化
pass
17. 异常 模块
17.1 异常介绍
# 异常的定义:
程序在运行期间,当Python检测到一个错误时,解释器就无法执行(俗成:程序崩溃)了,反而出现了一些错误的提示,这就是所谓的"异常"
# 注意:
异常不是语法错误,语法错误,是程序写错了,异常是指程序已经运行后的非语法错误
17.2 异常处理
处理异常的目的:
1. 只要解释器检查到异常错误,默认执行的动作是终止程序
2. 处理异常目的:防止程序退出,保证程序正常执行
捕获异常:防止程序结束
# 1. 语法:
try...except
格式:
try:
可能发生异常的代码
except:
# 处理异常的代码
1. 如果try里面发生异常
2. 自动跳转到 except 里面
"""
把可能出现问题的代码,放在try中
把处理异常的代码,放在 except 中
except 后面没有指定异常类型,可以捕获任意类型的异常
"""
# 2. 捕获指定异常类型
语法格式:
try:
可能发生异常的代码
except 异常类型:
处理异常的代码
# 3. except 捕获多个异常
语法格式:
try:
可能发生异常的代码
except(异常类型1,异常类型2):
处理异常的代码
# 4. 获取异常的信息描述
语法格式:
"""
try:
可能发生异常的代码
except 异常类型 as 异常对象名:
print(异常对象名) 即可获取异常的信息描述
"""
# 4. 捕获任意类型的异常
语法格式:
"""
try:
可能发生异常的代码
except Exception as 异常对象名:
Exception 为异常类的父类
"""
捕获异常示例:
# 捕获多个异常示例:
try:
f = open("ssss.txt", 'r')
print(10/0)
except (FileNotFoundError, ZeroDivisionError):
print("捕获到文件不存字异常,和被除数为0的异常")
# 捕获异常并且获取异常报错信息
try:
f = open("ssss.txt", 'r')
# print(10/0)
except FileNotFoundError as e:
print("捕获到文件不存字异常", e)
返回:捕获到文件不存字异常 [Errno 2] No such file or directory: 'ssss.txt'
# 捕获异常时,获取任意类型的异常
这里的任意异常就是在捕获异常时不用再设置异常的类型,而任意类型都可以被捕获
其实是一种多态,捕获的任意异常存在一个变量 Exception 中并且 给别名 e 存储起来
try:
f = open("ssss.txt", 'r')
# print(10/0)
except Exception as e:
print("捕获到文件不存字异常", e)
异常中 else
在if中,它的作用是当条件不满足是执行的实行
同样在 try...except... 中也是如此,即如果没有捕获到异常,那么就执行else中的事情
# 语法格式:
"""
try:
可能发生异常的代码
except:
处理异常的代码
else:
没有发生异常,except不满足执行else
"""
try...finally... (完整格式)
语法格式:
'''
try:
可能发生异常的代码
except:
处理异常的代码
else:
没有发生异常,except 不满足执行else
finally:
不管有没有异常,最终都要执行
'''
示例:
try:
print('=' * 20)
# num = 333
# print(num)
open("a", 'r')
except Exception as e:
print("捕获到异常:" , e )
else:
print("没有异常")
finally:
print("不管有没有异常,都打印")
异常小结:
1. 处理异常的目的:
只要解释器检查到异常错误,默认执行的动作是终止程序,为了防止程序退出,保证程序正常执行,需要认为处理异常
2. 捕获处理异常
'''
try:
可能发生异常的代码
except:
处理异常的代码
else:
没有发生异常,except 不满足执行else
finally:
不管有没有异常,最终都要执行
'''
17.3 异常传递
1. 异常传递特点
如果异常在内部产生,如果内部不捕获处理,这个异常会向外部传递
2. 异常嵌套
try 嵌套时,如果内存try没有捕获处理该异常,就会向外层try进行传递
3. 函数嵌套
函数嵌套时,如果内层函数没有捕获处理该异常,就会向外层函数进行传递
17.4 自定义异常
1. 抛出自定义的异常
用户用 raise 语句 来人为抛出一个异常
异常/错误对象必须有一个名字,且他们应是 Exception 类的子类
# 语法格式:先写类,再继承该类,再调用报错
1). 自定义异常类
class 自定义异常类名字(Exception):
1.1 重写 __init__(self, 形参1,形参2,...)
# 建议调用父类的init,先做父类的初始化工作
super().__init__()
自己写的代码
1.2 重新写 __str__(), 返回提示信息
2)抛出异常类
raise 自定义异常类名字(实参1,实参2,...)
示例:
class NumberError(Exception): # 定义自定义异常的类,父类一定是Exception
def __init__(self, _user_len, _match_len):
super().__init__() # 调用父类的同名初始化函数
self.user_len = _user_len
self.match_len = _match_len
def __str__(self): # 重写定义异常信息
return f"用户输入的手机号长度:{self.user_len},但要求的长度是{self.match_len}"
try: # 调用上述类
iphone_num = input("请输入手机号码:")
if len(iphone_num) != 11:
raise NumberError(len(iphone_num), 11) # 调用自定义异常判断类
except NumberError as e:
print("异常信息为:", e)
17.5 模块介绍
介绍:
1. 模块是一个由Python代码组成的文件,就是一个以.py 结尾的文件
2. 模块包含函数,类和变量,还可以包括可运行的代码
3. 模块的主要作用:
提高了代码的可维护性
一个模块编写完毕后,其他模块直接调用,不用再从零开始写代码了
避免名字冲突
# 模块导入 import
import 导入模块,把整个模块都加载进来
语法格式:
"""
导入格式: import 模块名
使用格式: 模块名.函数 模块名.类名 模块名.变量名
"""
# 模块导入 from...import导入模块中需要的内容
语法格式:
"""
导入格式:from 模块名 import 需使用的函数,类,变量
使用格式:函数,类,变量 无需通过模块名引用
"""
缺点是:有可能引起变量名冲突(就是导入的模块下的变量名与本身代码中的变量冲突)
# 模块导入 from...import * 导入模块所有的内容(也不是所有,而是__all__变量所包含的才会被导入)
语法格式:
"""
导入格式:from 模块名 import *
使用格式:函数,类,变量 无需通过模块名引用
"""
# import 和 from...import...导入模块的区别
import 导入模块,把整个模块都加载进来
from...import... 导入模块时,是把需要的模块中的函数,类,变量导入进来,但容易造成名字冲突
# import...as...给导入的模块取别名
把复杂名字改写简单些
把已经同名的名字改一个不同的名字
语法格式:
"""
模块起别名
导入格式: import 模块 as 模块别名
使用格式: 模块别名.工具(工具指函数,类,变量)
模块工具起别名
导入格式:from 模块 import 工具 as 工具别名
使用格式:工具别名 (无需通过模块名引用)
"""
# 模块的搜索路径
当导入一个模块,Python解析器对模块位置的搜索顺序是:
1. 当前路径
2. 如果不在当前目录,python则搜索系统路径
3. 模块搜索路径存储在system模块的sys.path变量中
示例:
import sys
print(sys.path)
17.6 模块制作
# 定义自己的模块
在python中,每个python文件都可以作为一个模块,模块的名字就是文件的名字。
# 调用自己定义的模块
import 导入模块
# 测试模块
在实际开发中,当一个开发人员编写完一个模块后,为了让模块能够在项目中达到想要的效果,这个开发人员会自行在模块文件中添加一些测试信息
注意:
模块文件,应该是单独执行时,才执行其测试代码
导入模块文件时,不应该执行测试代码
python 中变量 __name__ 能解决上述问题
# 模块中的 __name__
直接运行此文件,__name__的结果为 __main__
此文件被当作模块文件导入时, __name__ 的结果不为 __main__
如果不想导包把模块的测试代码也运行,把模块的测试代码放在 if __name__ == '__main__': 条件语句里面
# 模块中的 __all__
模块中 __all__ 变量,只对 from xxx import * 这种导入方式有效
模块中 __all__ 变量包含的元素,才能会被 from xxx import * 导入
__all__ 格式:是一个列表
__all__ = ['变量名', '类名', '函数名',...]
'在模块中,定义名子为 __all__ 的一个列表,这个列表中包含的内容,才会调用模块时被调用,模块中这个变量所包含的内容会在被调用此模块时被导入,其他的不会被导入'
17.7 总结
18. 包
# 1. 创建包
有两个模块功能有些联系,可以将其放在同一个文件夹里
要组成包,还需要在该文件夹中创建 __init__.py 文件
总结:
把有联系的多个模块文件,放在同一个文件夹下,并且在这个文件夹创建一个名字为 __init__.py 文件,那么这个文件就称之为包
包的本质就是一个文件夹,包的作用是将模块文件组织起来
包能有效的避免模块名称冲突问题,提高程序的结构性和可维护性
# 2. 导入包中模块
使用 import 包名.模块名 能够导入包中的模块
使用 from 包名.模块名 import...能够导入模块中的符号
"""
方式1:
导入格式:import 包名.模块名
包名就是文件名 模块名就是文件名字
使用格式:包名.模块名.工具 (类名,函数,变量)
"""
"""
方式2:
导入格式: from 包名.模块名 import 所需的工具
使用格式: 工具 (类名,函数,变量)
"""
# 3. __init__.py 文件的作用
包被导入时,会执行 __init__.py 文件的内容
__init__.py 的作用:控制包的导入行为,管理模块文件
包被导入时,会执行 __init__.py 文件的内容
# 方式4 init使用
__init__.py 文件
'''
print("init要执行了")
from msg import recvmsg
from msg import sendmsg
'''
# 直接导入包名,就执行了包中的init直接导入了包中的模块
import msg
#包名.文件名.函数名
msg.sendmsg.send_msg()
msg.recvmsg.recv_msg()

浙公网安备 33010602011771号