文章目录
- 前言
- 1 Python程序基本结构
- 2 输入和输出
- 3 python变量
- 4 数据类型
- 5. 各种控制语句
- 6. 函数
- 7.文件
- 8. 异常
- 9. 面向对象
前言
本文讲述python的基础知识,内容过多,建议收藏阅读。
如果你能把本文看完,并且把每个代码运行都自己敲打执行一遍,必能入门python!!!
1 Python程序基本结构
Python 程序结构涉及代码块、注释、语句分隔、语句续行、关键字与大小写等内容
1.1 代码缩进
在Java 、C/C++等语言中,用花括号表示代码块,例如:
if(x > 100){
y = x *5 -1;
}else{
y = 0;
}
Python使用缩进(空格)来表示代码块。通常,语句末尾的冒号表示代码块的开始。在if、for、while、函数、类等定义中都会使用到代码块。例如:
if x > 100:
y = x *5 -1
else:
y = 0
在包含代码嵌套是,冒号代表语句块的开始,应注意同级代码块的缩进量应保持相同:
x = 5
if x < 0:
print("参数X的值不合法")
print('X < 0')
else:
for i in [1,2,3,4,5]:
print(i)
print('X > 0')
print(x)
编码规范:使用空格来缩进,tab在不同环境下对应空格长度可能发生变化,在某些ide上tab键可能是8个空格长度
1. 2 代码注释
注释用于为程序添加说明文字,当Python运行程序时,会忽略掉被注释的内容。
1.2.1 单行注释
单行注释用“#”表示注释开始,“#”之后的语句不会被编译器执行。单行注释可以单独占一行,也可以放在语句末尾。
# 注释单独占一行,定义一个变量n
n = 0
---------------------------------------
b = 100 # 注释放在语句末尾,定义的变量b
1.2.2 多行注释
多行注释使用三个英文的单引号“ ‘’’ ”或者双引号“ “”" ”作为注释的开始和结束符号。
'''多行注释,这个是开头的三个单引号,
这是一段内容
这是结束的三个单引号'''
----------------------------------
"""多行注释,这个是开头的三个双引号,
这是一段内容
这是结束的三个双引号"""
1.3 语句分隔
使用分号分隔语句,从而将多条语句写在一行。
print(100);print(2+3)
如果冒号之后的语句块只有一条语句,Python允许将语句写在冒号之后,冒号之后可以是分号分隔的多条语句。
if x > 1:y = x **2
else:y=1;print('x <= 1') #冒号(:)后的每一个语句缩进量相同
1.4 语句续行
通常,Python中的一条语句占一行,没有类似于Java中的分号等语句结束符号。在遇到较长的语句时,可使用语句续行符号,将长语句写在多行中。
使用“\”符号续行
if x < 100\
and z > 10:
y = x*5-1
else:
y = 0
使用括号续行(包括使用圆括号()、方括号[]和大括号{}等),在使用括号时,括号内的内容可分多行书写,括号中的空白和换行符都会被忽略。
if x < 100\
and z > 1:
y = x*5-1
else:
y = 100
1.5 关键字与大小写
Python中也有各种标识符,如if、for、while等,可称为关键字。
Python对大小写敏感,关键字和各种自定义的标识符(如变量名、函数名等)在使用时区分大小写。
# if 不能写成If或IF,否则在编译时python提示语法出错
>>>If 2<9:
File "<stdin>",line I
If 2<9:
^
SyntaxError: invalid syntax
ab与Ab是两个不同的变量
>>>ab = 10
>>>Ab = 20
>>>print(ab,Ab)
10 20 # 结果输出为10 20 则证明ab与Ab是两个不同的变量
好了,我们已经完成了第一章的学习,下面我们进入第二章。
2 输入和输出
2.1 基本输入
如果我们想要获取用户输入的信息,我们可以使用input语句。input函数用于获得用户输入数据,基本格式如下:
变量 = input (提示字符串)
其中,变量和字符串是可以省略的。用户按【Enter】键完成输入,【Enter】键之前的全部字符均作为输入内容。
>>>a = input('请输入数据:')
请输入数据:'shiting
>>>a
'shiting
其中input函数是将用户输入以字符串返回,这个特点需要我们特别注意,例如:我们在python中打出”“a = input(请输入一个整数)”,此时变量a的类型仍然是为字符串。
如果需要输入整数或小数,则需要使用int或float函数进行相应的数据类型转换
>>>a = input('请输入一个整数:') # 实际a的类型为字符串
请输入一个整数:2
>>>a + 1 # 运行会报错,因为a是一个字符串,试图执行加法运算
# 可以改为:
a = input('请输入一个整数:')
int(a)+1 #将字符串转为整数再执行相关加法运算,ok
# 或者改为:
a = int(input('请输入一个整数:'))
另外,在使用input输入数据时,我们可以使用【Ctrl+Z】组合键中断输入;但是假如没有输入任何数据,按下【Ctrl+Z】组合键,则会产生EOFError异常
2.2 基本输出
Python3.x中使用print函数完成基本输出操作,print函数基本格式:
print(value, ..., sep=' ', end='\n', file=sys.stdout , flush=False)
参数的具体含义如下:
value–表示输出的对象。输出多个对象时,需要用 , (逗号)分隔。
sep – 用来间隔多个对象。
end – 用来设定以什么结尾。默认值是换行符 \n,我们可以换成其他字符。
file – 要写入的文件对象。
flush – 是否刷新缓冲区
2.2.1 省略所有参数(输出空白行)
print函数所有参数均可以省略。当无参数时,print函数输出一个空白行
>>>print() # 无参数时,输出空白行
>>>
2.2.2 输出一个或多个对象
print函数可同时输出一个或多个对象,无论什么类型的数据都可以直接输出。
#输出一个对象
>>>print(123456) # 123456
>>>num = 19
>>>print(num) #19 输出数值型变量
>>>str = 'Duan Yixuan'
>>>print(str) #Duan Yixuan 输出字符串变量
>>>list = [1,2,'a']
>>>print(list) #[1, 2, 'a'] 输出列表变量
>>>tuple = (1,2,'a')
>>>print(tuple) #(1, 2, 'a') 输出元组变量
>>>dict = {'a':1, 'b':2}
>>>print(dict) # {'a': 1, 'b': 2} 输出字典变量
----------------------------------------------------------------
# 同时输出多个对象
>>>print(123,'abc','com') #123 abc com 对象之间默认用空格分隔
2.2.3 指定输出分隔符
print函数默认分隔符为空格,可用sep参数指定输出对象的分隔符号
>>>print(123,'abc','com',sep='#') # 指定使用'#'作为输出分隔符
123#abc#com
>>>print("www", "4399", "com", sep=".") # 指定使用'.'作为输出分隔符
www.4399.com
2.2.4 指定输出结尾符号
print函数默认以回车换行符号( \n)作为输出结尾符号,即在输出后会换行,后面的print函数的输出会在新的一行开始。可用end参数指定输出结尾符号
>>>print("abc");print(000) # 一行写完多条输出print语句,仍会每一条都换行
abc
000
>>>print('abc',end='+');print(000) #指定使用”+“作为结尾符号,输出在一行
abc+000
2.2.5 指定输出到文件
print 函数默认输出到标准流(即sys.stdout)。可用 file参数 指定输出到特定文件。
>>>file1 = open('data.txt','w') # 打开文件
>>>print(123,'abc',45,'book',file = file1) # 用file参数指定输出到文件
>>>file1.close() # 关闭文件
>>>print(open('data.txt').read()) # 输出从该文件中读出的内容
123 abc 45 book
2.3 高级输出
2.3.1 使用%格式化输出
在C语言中,我们可以使用printf(“%-0.3d”,num)之类的形式,实现数据的的格式化输出
在python中,我们也可以实现类似的操作: %是一个占位符
%格式符 | 相应作用 |
---|---|
c | 字符及其ASCII码 |
s | 字符串 |
d | 有符号整数(十进制) |
f | 浮点数字(用小数点符号) |
u | 无符号整数(十进制) |
o | 无符号整数(八进制) |
x | 无符号整数(十六进制) |
X | 无符号整数(十六进制大写字符) |
e | 浮点数字(科学计数法) |
E | 浮点数字(科学计数法,用E代替e) |
g | 浮点数字(根据值的大小采用%e或%f) |
G | 浮点数字(类似于%g) |
p | 指针(用十六进制打印值的内存地址) |
n | 存储输出字符的数量放进参数列表的下一个变量中 |
常用格式化输出:%d %f %s ,
num = 156 # 整数
print('%d' %num) # 156
print('%f' %num) # 156.000000 默认精度为6
print('%s' %num) # 156
num = 156.1415 # 浮点数
print('%d' %num) # 156
print('%f' %num) # 156.141500 默认精度为6
print('%s' %num) # 156.1415
num = '156' # 字符串
print('%d' %num) # TypeError: %d format: a number is required, not str
print('%f' %num) # TypeError: must be real number, not str
print('%s' %num) # 156
nun = [1,4] # 列表
print('%s' %num) # [1,4]
#同时输出多个变量用()
num = 156.1415
n = 456
print('%d,%f' %(n,num)) # 456,156.141500
2.3.2 使用format格式化输出
format()–该函数把字符串当成一个模板,通过传入的参数进行格式化,并且使用大括号‘{}’作为特殊字符代替‘%’,且不用相应的格式字符
num = 156 # 整数
print('{}'.format(num)) # 156
num = 156.1415 # 浮点数
print('{}'.format(num)) # 156.1415
num = '156' # 字符串
print('{}'.format(num)) # 156
#同时输出多个变量
n = 456
num = 156.1415
print('{},{}'.format(n,num)) # 456,156.1415
相对基本格式化输出采用‘%’的方法,format()功能更强大,
优点:
1.带数字编号,可调换顺序,即“{1}”、“{2}”
2.带关键字,即“{a}”、“{tom}”
num = 156.1415
n = 456
print('{1},{0}'.format(n,num)) # 通过索引控制
156.1415,456 # 即format(n,num)中{0}=n,{1}=num
print('{a},{b}'.format(a=n,b=num)) # 通过关键字控制
156.1415,456 # 即format(n,num)中{0}=n,{1}=num
456,156.1415 # 即format{a=n,b=num}中指定{a}=n,{b}=num
2.3.3 使用f格式化输出
f-string用大括{ }表示被替换字段,其中直接填入替换内容即可。
num = 156 # 整数
print(f'{num}') # 156
num = 156.1415 # 浮点数
print(f'{num}') # 156.1415
num = '156' # 字符串
print(f'{num}') # 156
#同时输出多个变量
n = 456
num = 156.1415
print(f'{n},{num}') # 456,156.1415
2.3.4 最小字段宽度和精度
最小字段宽度:转换后的字符串至少应该具有该值指定的宽度。
如果是*(星号),则宽度会从值元组中读出。
精度:点(.)后跟精度,表示输出值的精度大小
如果需要输出实数,精度值表示出现在小数点后的位数。
如果需要输出字符串,那么该数字就表示最大字段宽度。
如果是*,那么精度将从元组中读出。
用%格式化输出
num = 156.1415 #最小字段宽度和精度支持各种格式化输出
print('**********') # 用来判断宽度
print('%10f' %num) # 字段宽10
print('%.3f' %num) # 精度3,四舍五入
print('%10.3f' %num) # 字段宽10,精度3
print('%010.3f'%num) #010表示字宽10,不够用0填充
print('%+010.3f'%num) # 添加+ 表示显示正负号
print('%-10.3f'%num) # 添加负号表示左对齐
#执行结果为:
**********
156.141500
156.142
156.142
000156.142
+00156.142
156.142
用format()格式化输出,同样需要跟%输出一样,只是多了{:}
num = 156.1415 #最小字段宽度和精度支持各种格式化输出
print('**********') # 用来判断宽度
print('{:10f}'.format(num)); # 字段宽度10
print('{:.3f}'.format(num)) # 精度3
print('{:10.3f}'.format(num)) # 字段宽10,精度3
print('{:010.3f}'.format(num)) #010表示字宽10,不够用0填充
print('{:+010.3f}'.format(num)) # 添加+ 表示显示正负号
print('{:<10.3f}'.format(num)) # 添加<表示左对齐
#执行结果为:
**********
156.141500
156.142
156.142
000156.142
+00156.142
156.142
制表符\t
,可以占用四个字节。 第二章节内容完结啦。
3 python变量
C、C++和Java等都是属于静态数据类型语言,即要求变量在使用前必须声明其数据类型。例如,我们需要定义一个变量为x,其数据类型为int
int x;
但对于python属于动态数据类型语言,他不需要对变量进行声明即可使用。
3.1 变量与对象
x = 5
对于上面的赋值语句,python在执行时总共执行了如下几个步骤:
第一步:创建表示整数5的对象。(注意:在python中所有的数据都是以对象的方式存在,学会了python,你就学会了创建对象)
第二步:检查变量x是否存在,若不存在则创建它。
第三步:建立变量x到对象5之间的引用
上面说的可能有些复杂,可以看完全文的内容后,再回来了解。
3.2 变量理解
在python中使用变量,需要理解下面几点:
1、变量在第一次赋值时被创建,再次出现时可以直接使用
2、严格来说变量没有数据类型的概念。数据类型属于对象,类型决定了对象在内存中的存储方式
3、变量引用了对象。当在表达式中使用变量时,变量立即被其引用的对象替代。
4、所以变量在使用前必须对其进行赋值
x = 5 # 创建对象5,第一次使用,创建变量x, 引用对象5
print(x + 3)
#执行结果为:
8
对同一个变量赋值不同的数据类型,则会改变变量的类型
x = 5
x = 'yyds'
print(x)
#执行结果为:
yyds
3.3 变量命名
3.3.1 命名规则
变量的命名规则应包含如下:
- 必须以下划线或字母开头,不能以数字开头
- 变量名称只能包含下划线、字母或者数字
- 变量名区分大小写
- 禁止使用python保留字
myname1 = "须佐"
my_name_2 = "须佐"
_myname3 = "须佐"
避免使用如下样式:
- 前后有下划线的变量名通常为系统变量,例如:name、doc
- 以一个下划线开头的变量(如_name)不能被from…import*语句从模块导入
- 以两个下划线开头、末尾无下划线的变量(如__abc)是类的私有变量
3.3.2 命名法则
驼峰式命名法,混合使用大小写字母来构成变量和函数的名字。包含:骆驼法则和帕斯卡法则
骆驼法则(Camel):又名小驼峰命名(lowerCamelCase),除第一个单词之外,其他单词首字母大写。
TaskRepository taskRepository #成员变量
androidStudio = "安卓开发工具" #局部变量
帕斯卡法则(Pascal):又名大驼峰命名(CamelCase),每个单词的首字母都大写。
class TaskDate() #类名
Login #命名空间
蛇形法则:每个单词都以下划线字符分隔。
get_uername()
test_login01 = 'z' #测试方法名
PI = 3.1415926 #常量
LAST_DATA #枚举名称
命名规范
1、模块名和包名尽量短小,并且全部使用小写字母,可以使用下划线,如:mymodule.py,module_name.py,common包名
2、类采用单词首字母大写形式(即Pascal)。
3、函数、类的属性和方法的命名规则同模块的类似,也是全部小写字母,多个字母间用下划线“”分隔。
4、常量命名时采用全部大写字母,可以使用下划线。
5、使用单下划线“”开头的模块变量或者函数是受保护的,在使用from xxx import * 语句从模块中导入时这些变量或函数不能被导入。
3.4 变量赋值
赋值语句用于创建变量、建立变量到对象的引用。python中有多种赋值语句:简单赋值、序列赋值、多目标赋值、增强赋值等。
(1)简单赋值
x =100 #为一个变量建立对象引用
(2)序列赋值
序列赋值可以一次性为多个变量赋值。python会顺序匹配变量名和值。
n, m = 1, 2 # 使用省略圆括号的元组赋值
print(n, m) # 输出:1,2
(a, b) = (3, 4) # 使用元组赋值
print(a, b) # 输出:3,4
x, y, z="kpo" # 用字符串赋值
print(x, y, z) # 输出:'k','p','o'
注意可以在变量名之前使用“*”,为变量创建列表对象引用。此时,不带星号的变量匹配一个值,剩余的值会作为带星号变量的列表对象
x, *y = 'cctest' # x匹配第一个字符,剩余字符作为列表匹配y
print(x, y) # 输出:'c','ctest'
(3)多目标赋值
可以使用“=”,为连续多个变量赋同一个值
n=m=p= 520 # 将520赋值给变量n、m、p
print(n, m, p) # 输出:520,520,520
前面几章节的内容都比较简单,接下来开始进入重点
4 数据类型
数据类型决定了程序如何存储和处理数据。Python中完善的数据类型系统,使得在Python程序中可以轻松完成各种数据处理,python中的内置数据类型如下
数字类型: int, float, complex
序列类型: str, list, tuple, range
映射类型: dict
套装类型: set, frozenset
布尔类型: bool
二进制类型: bytes, bytearray, memoryview
标准数据类型如下:
不可变类型(3 个):Number(数字)、String(字符串)、Tuple(元组);
可变类型(3 个):List(列表)、Dictionary(字典)、Set(集合)。
可变、不可变区分依据是保存数据的源内存空间是否允许修改
1、不可变类型:数字、字符串、元组
源内存空间中数据不允许修改, 如果想要修改, 只能开辟新内存空间,让变量引用指向新内存空间数据的地址
2、可变类型:字典、列表、集合
源内存空间中的数据可以修改,不需要开辟新内存空间,只要在源内存基础上修改数据
4.1 Number 数字类型
数字 是程序处理的一种基本数据,Python 数字数据类型用于存储数值。
数据类型是不允许改变的,这就意味着如果改变数字数据类型的值,将重新分配内存空间。
在变量赋值时 Number 对象将被创建:
var1 = 1
print(id(var1)) # 2621878588880
var1 = 10
print(id(var1)) # 2621878592144
id()函数可以查询变量的内存地址,我们可以看到在对var1变量进行再次赋值后,其内存地址改变了,说明数字类型是一个不可变类型
可以通过使用del语句删除单个或多个对象的引用
del var1,var2
Python中核心对象包含的数字类型包括: 整型(int) 、浮点型(float)、复数(complex)、布尔类型(boole)
4.1.1 int 整型
整型就是不带小数点的数,一般的整型都是十进制。在Python 3里,只有一种整数类型 int,表示为长整型,没有 python2 中的 Long。理论上来说,只要计算机的内存空间足够,整数可以无穷大。
>>>2**100 # 输出2的100次方
1267650600228229401496703205376
>>>3**100 # 输出3的100次方
515377520732011331036461129765621272702107522001
Python还允许将整型表示为二进制、八进制、十六进制。
- 二进制:以0b或0B开头,后面跟二进制数字(0、1)如:0b1001、0B1003
- 八进制:以0o或0O开头,后面跟八进制数字(0~7)如:0o1002、0O1003
- 十六进制:以0x或0X开头,后面跟十六进制数字(0~9、A ~ F)字母大写或者小写都可以。如:0x12AB、0X231BD
不同进制只是整数的不同书写形式,程序运行时都会处理为十进制数。这个念书变量在程序中使用时,都会生成一个整数对象
可以使用int函数将一个字符串按指定进制转换为整数。int函数基本格式如下:
int(x,基数= 10 ) # 如果X不是数字或如果基数给出,则X必须是一个字符串
>>>int('110') # 111 # 默认按十进制转换
>>>int('110',2) # 6 # 按二进制转换
>>>int('110',8) # 72 # 按八进制转换
>>>int('110',10) # 110 # 按十进制转换
>>>int('110',16) # 272 # 按十六进制转换
>>>int(110,2) # 基数给出,X必须为字符串 错误
int函数中第一个字符可以是正负号,其他字符不能包含小数及其他符号。
>>>int(+12)
>>>int('+12') # +12
>>>int('-12') # -12
>>>int('+-123') # 字符串中同时包含了正负号 错误
>>>int('12.3') # 字符串中包含了小数 错误
>>>int('123asd') # 字符串中包含了字母 错误
python中提供了内置函数bin(x)、oct(x)和bex(x)用于将整数转换为对应进制的字符串。
>>>bin(21) # '0b10101' #转换为二进制字符串
>>>oct(21) # '0o25' #转换为八进制字符串
>>>hex(21) # '0x15' #转换为十六进制字符串
4.1.2 float 浮点数类型
12.5、2.、3.0、12.3e+10等都是合法的浮点数常量。float()函数来创建浮点数
float()函数基本格式如下:
float(X) # X为数字或者字符串构成的浮点数
>>>float(12.0) # 12.0
>>>float(+12.0) # +12.0
>>>float(-12.0) #-12.0
>>>float('12.2333') #12.2333
>>>float('wqhfaf') #字符串包含字母, 错误
4.1.3 complex 复数类型
复数常量表示为“实部+虚部”形式,虚部以j或J结尾。如2+3j、2+3J。可用complex()函数来创建复数
complex (实部,虚部)
>>>complex(2,3) # 2+3j
>>>complex(2) # 2+0j
4.1.4 boole 布尔类型
bool类型标识 True 和 False,常用语表达式是否为True or False
4.1.4.1 计算表达式
比较两个值,将计算表达式结果并返回布尔值答案:
print(1 > 2) # False
if 1 == 2: # True
print("aaaa")
else:
print('bbbb')
4.1.4.2 True 布尔真值
True表示真值,几乎任何值都会被评估,任何非空的字符串、列表、元组、集合、字典都是True
bool("abc") # True
bool(123) # True
bool(["yys", "age"]) # True
4.1.4.3 False 布尔假值
False表示空值(例如()、[]、{}、‘’、数字0)及计算为None的值
bool(0) # False
bool("") # False
bool(()) # False
bool([]) # False
bool({}) # False
bool(None) # False
一般我们使用内置的 type() 函数可以用来查询变量所指的对象类型。
a = True
print(type(a)) #输出 <class 'bool'>
此外还可以用 isinstance 来判断:
a = 111
isinstance(a, int) # 使用isinstance函数判断变量 a 是否是整型
# 输出结果为布尔值,True or False
注意:
Python3 中,boole 是 int 的子类,True 和 False 可以和数字相加, True==1、False==0
会返回 True.
在 Python2 中是没有布尔型的,它用数字 0 表示 False,用 1 表示 True
4.1.5 运算符
Python 语言支持以下类型的运算符:
- 算术运算符
- 比较运算符
- 赋值运算符
- 位运算符
- 成员运算符
- 身份运算符
4.1.5.1 运算符
python支持基本的算术运算:加法(+)、减法(-)、乘法(*)、除法(/)、取整除(//)、取余(%)、幂(**)
a, b=8, 5
c = a + b # 13 两个数相加
d = a - b # 3 两个数相减
e = a * b # 40 两个数相乘
f = a / b # 1.6 a除以b
g = a // b # 1 取整除 往小的方向取整数,返回向下取整后的结果
h = a % b # 6 返回除法的余数
j = a ** b # 32768 返回a的b次方
注意:在整数除法中,除法 / 总是返回一个浮点数
4.1.5.2 比较运算符
python中的比较运算符有:大于(>)、小于(<)、等于(==)、大于等于(>=)、小于等于(<=)、不等于(!=)
a,b=10,20
print(a > b) # False
print(a < b) # True
print(a == b) # False
print(a >= b) # False
print(a <= b) # True
print(a != b) # True
4.1.5.3 赋值运算符
python中的赋值运算符有:简单赋值(=)、加法赋值(+=)、减法赋值(-=)、乘法赋值(*=)、除法赋值(/=)、取整除赋值(//=)、取模赋值(%=)、幂赋值(**=)
a,b=21,10
c = a + b # 31
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
4.1.5.4 位运算符
按位运算符是把数字看作二进制来进行计算的,python中的位运算符有:按位与(&)、按位或(|)、按位异或(^)、按位取反(~)、左移动(<<)、右移(>>)
a = 60 # 60 = 0011 1100
b = 13 # 13 = 0000 1101
c = a & b # 如果两个相应位都为1, 则该位的结果为1, 否则为0 # 12 = 0000 1100
c = a | b # 如果两个相应位都为0, 则该位的结果为0, 否则为1 # 61 = 0011 1101
c = a ^ b # 如果两个相应位相异, 则该位的结果为1, 否则为0 # 49 = 0011 0001
c = a ~ b # 把每个二进制位取反, 即把1变为0, 把0变为1 # -61 = 1100 0011
c = a << 2 # 把每个二进制位左移若干位,高位丢弃,低位补0 # 240 = 1111 0000
c = a >> 2 # 把每个二进制位右移若干位,高位补0,低位丢弃 # 15 = 0000 1111
4.1.5.5 逻辑运算符
python支持的逻辑运算符有:与(and)、或(or)、非(not)
a , b= 10 , 20
print(a and b) # 如果 a 为 False,返回 a 的值,否则返回 b 的计算 # True
print(a or b) # 如果 a 是 True,返回 a 的值,否则返回 b 的计算值 # True
print(not a) # 如果 a 为 True,返回 False 。如果 a 为 False,它返回 True。 # False
4.1.5.6 成员运算符
除了以上的一些运算符之外,Python还支持成员运算符:在(in) 和 不在(not in )
a , b= 10 , 20
list = [1, 2, 3, 4, 5 ]
print(a in list) # 如果在序列中找到值返回 True,否则返回 False # False
print(b not in list) # 如果在序列中没有找到值返回 True,否则返回 False # True
4.1.5.7 身份运算符
身份运算符用于比较两个对象的存储单元,python有身份运算符:是(is) 和 不是(not is)
a , b= 10 , 20
print(a is b) # 判断是不是引用自一个对象,相同返回 True,否则返回 False
print(a not is b) # 判断是不是引用自不同对象, 不同返回 True,否则返回 False
注意:is 和 == 两者不一致
is的作用是用来检查对象的标识符是否一致,即比较两个对象在内存中是否拥有同一块内存空间。即 a is b 是 id(a) == id(b)
4.1.5.8 运算符优先级
运算符优先级一览表,列出了从最高到最低优先级的所有运算符,同一行表示优先级相同。在同一个表达式中,按照优先级从高到低依次计算。优先级相同则按照从左到右的顺序计算
运算符 | 描述 | |
---|---|---|
** | 乘方(指数) | |
+x, -x, ~x | 正,负,按位非 NOT | |
+*, @, /, //, % | 乘,矩阵乘,除,整除,取余 | |
+, - | 加,减 | |
<<, >> | 左移,右移 | |
& | 按位与 | |
^ | 按位异或 | |
按位或 | ||
in,not in, is,is not, <, <=, >, >=, !=, == | 身份、成员、比较运算符 | |
not | 非 | |
and | 与 | |
or | 或 | |
=,-=,+=,*=,/=,//=,**= | 赋值系列 |
简单来说,运算符优先级按类别排序:算术 > 位 > 身份 >成员 > 比较 > 逻辑 > 赋值
4.2 String 字符串类型
字符串是字符的容器,一个字符串可以存储任意数量的字符。除了数字,字符串是 Python 中最常用的数据类型,
4.2.1 初识字符串
4.2.1.1 定义字符串
字符串在python中有多种定义形式:
- 单引号定义:
name = '阴阳师'
- 双引号定义:
name = "阴阳师"
- 三引号定义:
name = '''阴阳师'''
三引号定义方式与多行注释写法一样,同样支持换行操作
4.2.1.2 创建字符串
我们可以使用单引号(‘’)或双引号(“”),也可用str()函数来创建字符串
创建字符串很简单,只要为变量分配一个值即可。例如:
x1 = '阴阳师'
x2 = "yys"
x = '''阴阳师'''
x = str('阴阳师')
创建空字符串可以使用:x=''
或者x=str('')
4.2.1.3 引号嵌套
当需要定义的字符串本身是包含:单引号、双引号自身时?该怎么写
- 单引号定义方式,可以内含双引号
- 双引号定义方式,可以内含单引号
- 可以使用转义字符(\)将引号解除效用,转变成普通字符串
x1 = '我的阴阳师名称"名字"'
x2 = "I'm yys"
x3 = '阴阳师中我的名字:\'yys\''
4.2.2 访问字符串中的值
4.2.2.1 字符串是序列
字符串str是文本序列类型,可以支持序列类型的各种操作。
字符串的截取格式:变量[头下标:尾下标:步长]
# 如一个字符串: 'z i u v c'
# 从前面索引: 0 1 2 3 4
# 从后面索引: -5 -4 -3 -2 -1
注意:计算机中索引值以 0 为开始值,-1 为从末尾的开始位置。
4.2.2.2 获取特定位置单个字符
可以根据索引,来获取特定位置的的字符,如下:
s = 'yys'
print(s[0]) # 获取头位置字符,即:y
print(s[-1]) # 获取末尾位置字符,即:s
print(s[1]) # 获取索引为1处的字符, 即:y
python中没有字符数据类型,单个字符就是长度为1的字符串
4.2.2.3 切片
可以使用切片语法返回一系列字符串,格式s[start:end:length]
,
start指定开始索引,end指定结束索引,length指定步长,返回s[start]至s[end]之间的所有字符,不包括s[end]
正步长
当步长为正时,end必须在start的右侧,且不能等于start。步长默认为1
正索引切片
# 如一个字符串:s= 'z i u v c'
# 从前面索引: 0 1 2 3 4
print(s[3:1]) # 正步长,end在start右侧,切片为空
print(s[1:1]) # end等于start,则切片为空
print(s[1:3]) # 输出索引 1 到 4 位置之间的字符,不包含索引4,即:iuv
负索引切片
# 如一个字符串:s= 'z i u v c'
# 从后面索引: -5 -4 -3 -2 -1
print(s[-2:-4]) # 正步长,end在start右侧,切片为空
print(s[-3:-3]) # end等于start,切片为空
print(s[-4:-1]) # 输出索引 -4 到 -1 位置之间的字符,不包含索引-1,即:iuv
负步长
当步长为负时,end必须在start的左侧,且不能等于start
正索引切片
# 如一个字符串:s= 'z i u v c'
# 从前面索引: 0 1 2 3 4
print(s[1:3:-1]) # 负步长,end不在start的左侧,切片为空
print(s[1:1:-1]) # end等于start,切片为空
print(s[3:1:-1]) # 输出索引 3 到 1 位置之间的字符,不包含索引1,即:vu
负索引切片
# 如一个字符串:s= 'z i u v c'
# 从后面索引: -5 -4 -3 -2 -1
print(s[-4:-2:-1]) # 负步长,end不在start的左侧,切片为空
print(s[-4:-4:-1]) # end等于start,切片为空
print(s[-2:-4:-1]) # 输出索引 -2 到 -4 位置之间的字符,不包含索引1,即:vu
省略开始或结束索引,切片
# 如一个字符串:s= 'z i u v c'
# 从前面索引: 0 1 2 3 4
# 从后面索引: -5 -4 -3 -2 -1
print(s[:3:1]) # 省略开始索引,输出 开始 到 3 位置的字符,不包含索引3,即:ziu
print(s[2::1]) # 省略结束索引,输出 2 到 结束 位置的字符,不包含索引3,即:ziu
print(s[::-1]) # 表示字符串逆序输出:cvuiz
4.2.3 字符串基本操作
4.2.3.1 字符串拼接
可以使用 ‘+’ 运算符,来连接或组合多个字符串:
a = "Hello"
b = "World"
c = a + b
print(c) # 返回 "Hello" 和 "World" 字符串拼接结果,即:HelloWorld
d = a + ' ' + b
print(d) # 在"Hello" 和 "World" 字符串之间拼接个空格,即:Hello World
4.2.3.2 字符串遍历
range()函数遍历
4.2.3.3 成员运算符
in:判断字符串中是否包含指定字符
txt = "yys tins is shijian zui 好玩"
print("yys" in txt) # 如果字符串中包含给定的字符,返回True
print("lol" in txt) # 如果字符串中不包含给定的字符,返回False
not in:判断字符串中是否不包含指定字符
txt = "yys tins is shijian zui 好玩"
print("lol" not in txt) # 如果字符串中不包含给定的字符,返回True
print("yys" in txt) # 如果字符串中包含给定的字符,返回False
4.2.3.3 字符串格式化
字符串格式化常用三种操作:%s、format()、f-string
最基本的用法是将一个值插入到一个有字符串格式符 %s 的字符串中,如下:
print("我叫 %s 今年 %d 岁!" % ('小羽', 1)) 用 %s
字符串格式化操作符辅助指令:
符号 | 功能 |
---|---|
* | 定义宽度或者小数点精度 |
- | 用做左对齐 |
+ | 在正数前面显示加号( + ) |
<sp> | 在正数前面显示空格 |
% | ‘%%‘输出一个单一的’%’ |
* | 定义宽度或者小数点精度 |
详细可查看第2章的高级输出
4.2.4 常用内置函数和方法
len() – 统计字符串长度
语法格式:len(str)
参数:str–字符串
s = "this is CCTV!"
print(len(s)) # 返回字符串长度,包含空格,即:13
count() – 统计字符串里某个字符出现的次数
语法格式:str.count(sub, start=0,end=len(string))
sub – 搜索的子字符串
start – 开始索引,默认为0
end – 结束索引,默认为字符串的长度
s = "this is CCTV!"
print(s.count('i')) # 返回整个字符串中,字符 'i' 出现次数 ,即 2
print(s.count('i',0,5)) # 返回索引 0 到 5 字符串中,字符 'i' 出现次数,即 1
find() – 检测字符串中是否包含指定字符串
语法格式:str.find(str, beg=0, end=len(string))
str – 指定检索的字符串
beg – 开始索引,默认为0。
end – 结束索引,默认为字符串的长度
s = "this is CCTV!"
print(s.find('s')) # 找到了,返回开始的索引值,即:3
print(s.find('is', 1)) # 找到了,返回开始的索引值,即:2
print(s.find('sss', 0 , 5)) # -1 ,没找到返回-1
类似的有:rfind() --从右开始匹配,检测字符串中是否包含指定字符串
index() – 检测字符串中是否包含指定字符串
语法格式:str.index(sub, beg=0, end=len(string))
sub – 检测的字符串
beg – 开始索引,默认为0。
end – 结束索引,默认为字符串的长度
s = "this is CCTV!"
print(s.index('s')) # 找到了,返回开始的索引值,即:3
print(s.index('is', 1)) # 找到了,返回开始的索引值,即:2
print(s.index('sss', 0 , 5)) # -1 ,没找到否则抛出异常,即报错
注意:index() 方法与 find() 方法一样,只不过如果 sub 不在 string中会报一个异常。
类似的有:rindex() --从右开始匹配,检测字符串中是否包含指定字符串
join() – 将序列中的元素以指定的字符连接生成一个新的字符串
语法格式:sub.join(seq)
参数:
seq – 要连接的元素序列,元组或者列表
sub – 为指定字符
seq = ("y", "y", "s") # 字符串序列
print('.'.join(seq)) # 将序列 seq 中的字符一个个以'.'相连得到字符串,即:'y.y.s'
replace() – 替换字符串
语法格式:str.replace(old, new, max)
old – 原字符串
new – 新字符串
max – 可选参数,最大替换次数
s = "this is CCTV!"
print(s.replace('is','ws')) # 用'ws'字符串替换原字符中的'is'字符串,即:'thws ws CCTV!'
split() – 通过指定分隔符对字符串进行切片,
语法格式:str.split(sub="", num=string.count(str))
sub – 分隔符,默认为所有的空字符,包括空格、换行(\n)、制表符(\t)等
num – 分割次数。默认为 -1, 即分隔所有
s = "this is CCTV!"
print(s.split()) # 返回分割后的字符串列表,即:['this','is','CCTV!']
endswith() – 检测字符串是否以指定后缀结尾
语法格式:str.endswith(suffix, start=0 end=len(string))
suffix – 该参数可以是一个字符串或者是一个元素。
start – 开始索引,默认为0
end – 结束索引,默认为字符串的长度
s = "this is CCTV!"
print(s.endswith('i')) # False
print(s.endswith('CCTV!')) # True
类似的有:startwith()–检测字符串是否以指定前缀开始
isdigit():检测字符串是否只由数字组成,只对正整数有效,负数及小数均返回不正确。
max():返回字符串中最大的字母
min():返回字符串中最小的字母
capitalize():将字符串的首字母变成大写,其他字母变小写
swapcase():将字符串的大小写字母进行相互转换,将大写变为小写,小写变为大写
lower():转换 string 中所有大写字符为小写
Python 使用反斜杠 \ 来转义特殊字符或在字符串前面添加一个 r,表示原始字符串:
print(r'this is CCTV!')
与 C 语言字符串不同的是,Python 中字符串不能被改变。所以向一个索引位置赋值,比如 word[0] = ‘m’ 会导致错误。
注意:字符串不能被改变
4.3 List 列表类型
4.3.1 初识列表
4.3.1.1 列表定义
List(列表) 是写在方括号 [] 之间、用逗号分隔开的元素列表,是 Python 中使用最频繁的数据类型。
列表可以完成大多数集合类的数据结构实现。它支持数字,字符串甚至别的数据类型组合
list1 = list(['yys', '游戏' , 18])
lsit2 = ['yys', '游戏' , 18]
4.3.1.2 创建列表
创建一个列表,只要把逗号分隔的不同的数据项使用方括号括起来即可。或者使用 list 函数
list1 = ['yys', '游戏' , 18, Ture]
lsit2 = [1, 2, 3, 4]
list1 = list(('yys', '游戏' , 18))
创建空列表可以使用:x=[]
或者 x = list()
4.3.1.3 列表嵌套
列表可以包含各种数据类型,包括列表,如下
list1 = [['yys', '游戏' , 18], [1, 2, 3], []]
4.3.2 访问列表中的值
4.2.2.1 列表是序列
和字符串一样,列表同样可以被索引和截取,列表被截取后返回一个包含所需元素的新列表
列表的截取格式:mylist[start:end]
# 如一个列表 : ['yys', 'zz', '1', '3', 'x']
# 从前面索引: 0 1 2 3 4
# 从后面索引: -5 -4 -3 -2 -1
4.2.2.2 获取特定位置列表元素
可以根据索引,来获取特定位置的的列表元素,如下:
list1 = list(['yys', '游戏' , 18])
print(list[0]) # 获取头位置列表元素,即:'yys'
print(list[-1]) # 获取尾位置列表元素,即:'18'
print(list[1]) # 获取索引为 1 处列表元素,即:'游戏'
4.2.2.3 切片
同样,可以使用切片语法返回一系列列表,格式mylist[start:end:length]
,
start指定开始索引,end指定结束索引,返回s[start]至s[end]之间的所有元素,不包括s[end],length指定步长
正步长
当步长为正时,end必须在start的右侧,且不能等于start。步长默认为1
# 如: mylist = ['yys', 'zz', '1', '3', 'x']
# 从前面索引: 0 1 2 3 4
print(mylist[3:1]) # 正步长,end在start右侧,切片为空
print(mylist[1:1]) # end等于start,切片为空
print(mylist[1:3]) # 输出索引 1 到 4 位置之间的元素,不包含索引4,即:['zz', '1', '3']
负索引切片
# 如: mylist = ['yys', 'zz', '1', '3', 'x']
# 从后面索引: -5 -4 -3 -2 -1
print(mylist[-1:-4]) # 正步长,end在start右侧,切片为空
print(mylist[-1:-1]) # end等于start,切片为空
print(mylist[-4:-1]) # 输出索引 -4 到 -1 位置之间的列表元素,不包含索引-1,即:['zz', '1', '3']
负步长
当步长为负时,end必须在start的左侧,且不能等于start
正索引切片
# 如: mylist = ['yys', 'zz', '1', '3', 'x']
# 从前面索引: 0 1 2 3 4
print(mylist[1:3:-1]) # end不在start的左侧
print(mylist[1:1:-1]) # end等于start,切片为空
print(mylist[3:1:-1]) # 输出索引 3 到 1 位置之间的字符,不包含索引1,需要逆序:['3','1']
负索引切片
# 如: mylist = ['yys', 'zz', '1', '3', 'x']
# 从后面索引: -5 -4 -3 -2 -1
print(s[-4:-2:-1]) # end不在start的左侧
print(s[-4:-4:-1]) # end等于start,切片为空
print(s[-2:-4:-1]) # 输出索引 -2 到 -4 位置之间的列表元素,不包含索引1,需要逆序即:['3','1','zz']
省略开始或结束索引,切片
# 如: mylist = ['yys', 'zz', '1', '3', 'x']
# 从前面索引: 0 1 2 3 4
# 从后面索引: -5 -4 -3 -2 -1
print(mylist[:3]) # 省略开始索引,输出 开始 到 3 位置的字符,不包含索引3,即:['yys', 'zz', '1']
print(mylist[2:]) # 省略结束索引,输出 2 到 结束 位置的字符,不包含索引3,即:['1', '3', 'x']
print(mylist[::-1]) # 逆序输出列表,即['x','3','1','zz','yys']
4.3.3 列表基本操作
4.3.3.1 列表拼接
可以使用 ‘+’ 运算符,来连接或组合多个列表:
list1 = [1997, 2000, 2]
list2 = [4, 5, 6]
print(list1 + list2) # 返回列表拼接的结果,即:[1997, 2000, 2, 4, 5, 6]
4.3.3.2 添加列表元素
1.末尾添加
将元素添加到列表的末尾,可以使用 append()
list1 = [1,2,3,4,5]
list1.append(88) # 在末尾添加元素
print(list1) # 即:[1,2,3,4,5,88]
2.指定位置添加
将元素添加到列表中的指定位置前面,可以使用 insert()
list1 = [1,2,3,4,5]
list1.insert(1, 88) # 在索引1位置前面添加元素
print(list1) # 即:[1,88,2,3,4,5]
3.合并两个列表
将一个列表中的元素附加到当前列表,可以使用 extend()
list1 = [1,2,3,4,5]
list2 = [88,77,66]
list1.extend(list2) # 在list1末尾附加列表2元素
print(list1) # 即:[1,2,3,4,5,88,77,66]
4.3.3.3 更新列表元素
1.更改单个元素值
更改特定位置元素值,需要使用索引
list1 = [1,2,3,4,5]
list1[1] = 88 # 将索引1位置元素值更改为88
print(list1) ,即:[1,88,3,4,5]
2.更改多个元素值
更改特定位置元素值,需要使用索引范围
list1 = [1,2,3,4,5]
list1[1:3] = [88,77] # 将索引1到索引3位置,不包括索引3,元素值更改
print(list1) # 即:[1,88,77,4,5]
4.3.3.4 查找列表元素
1.成员运算符
可以判断某个元素是否属于这个列表,可以用 in 或 not in
list1 = [1,2,3,4,5]
print(1 in list1) # 1属于这个列表,即:True
print(3 not in list1) # 3属于这个列表,即:False
2.查找列表元素索引
查找列表中某个值得索引位置,可以使用index()
list1 = [1,2,3,4,5]
print(list.index(3)) # 查找元素值3的索引,即:2
4.3.3.5 删除列表元素
1.删除指定值
将列表中特定值删除,可以使用remove()
list1 = [1,2,3,4,5]
list1.remove(5) # 将列表中删除值为5的元素
print(list1) # 即:[1,88,3,4]
2.删除指定索引
将列表中指定索引值删除,可以使用pop()
list1 = [1,2,3,4,5]
list1.pop(2) # 将列表中删除索引为2的元素
print(list1) # 即:[1,2,4,5]
3.删除列表
删除列表可以使用del关键字
list1 = [1,2,3,4,5]
del list1[0] # 将列表中删除索引为0的元素
print(list1) # 即:[1,2,4,5]
del list # 将列表删除
4.清空列表
清空列表可以使用 clear()
list1 = [1,2,3,4,5]
list1.clear() # 将列表中的元素清空
print(list1) # 即:[]
4.3.4 列表常用函数和方法
函数/方法 | 描述 | 返回值 |
---|---|---|
len(list) | 统计列表元素个数 | 返回个数 |
max(list) | 统计列表元素最大值 | 返回最大值 |
min(list) | 统计列表元素最小值 | 返回最小值 |
list.count(obj) | 统计某个元素在列表中出现的次数 | 返回次数 |
list.index(obj) | 从列表中找出某个值第一个匹配项的索引位置 | 返回索引位置 |
list.pop([index=-1]) | 移除列表中的一个元素(默认最后一个元素) | 返回该元素的值 |
list.append(obj) | 在列表末尾添加新的对象 | 无返回值,会修改原列表 |
list.extend(seq) | 在列表末尾追加另一个序列中的多个值 | 无返回值,会修改原列表 |
list.insert(index, obj) | 将对象按指定索引插入列表 | 无返回值,会修改原列表 |
list.remove(obj) | 移除列表中某个值的第一个匹配项 | 无返回值,会修改原列表 |
list.sort( key=None, reverse=False) | 对原列表进行排序 | 无返回值,会修改原列表排序 |
list.reverse() | 反向排序列表中元素 | 无返回值,会修改原列表排序 |
list.clear() | 清空列表 | 无返回值,会修改原列表 |
list.copy() | 复制列表 | 返回一个列表的浅复制 |
列表是一个有序且可变的集合,允许成员重复
注意:List中的元素是可以改变的
4.4 Tuple 元组类型
4.4.1 初始元祖
4.4.1.1 元祖定义
Python 的元组与列表类似,不同之处在于元组的元素不能修改,元祖用小括号(),列表使用方括号[]
tup1 = ('yys', '游戏', 18, True)
tup2 = (1, 2, 3, 4, 5 )
4.4.1.2 创建元祖
元组创建很简单,只需要在小括号()中添加元素,并使用逗号隔开即可,也可以使用tuple函数
tup1 = ('yys', '游戏', 18, True)
tup2 = tuple((1, 2, 3, 4, 5))
创建空元祖可以使用:tup = ()
或者 tup = tuple()
元组中只包含一个元素时,需要在元素后面添加逗号 , ,否则括号会被当作运算符使用:
tup1 = (50)
type(tup1) # 不加逗号,类型为整型 <class 'int'>
tup1 = (50,)
type(tup1) # 加上逗号,类型为元组 <class 'tuple'>
注意:元祖元素的数据类型可以是任何数据类型:字符串、整型、布尔数据类型或者嵌套元祖
4.4.2 元组跟列表区别
元组的各种操作及函数跟列表基本一致
关键区别在于:元组中的元素不可被修改,即元组是不可变的
所谓元组的不可变指的是元组所指向的内存中的内容不可变,但是其内容中的内容是可变的
tup = ('r', 'u', 'n', 'o', 'o', 'b')
tup[0] = 'g' # 对元组元素修改会报错
tup = (1,2,[122,[2]],5,8)
tup[2] = 'g' # 当对元组中的元素对象操作时,一样会报错
tup[2][1] = 'g' # 当对元组中的元素对象里面的元素对象操作时,则不会报错
tuple1 = tuple(('yys', '游戏' , 18))
print(tuple) # 输出完整元组
print(tuple[0]) # 输出元组的第一个元素
注意:元组的元素不能修改
4.5 Set 集合类型
4.5.1 初识集合
4.5.1.1 集合定义
集合(set)是由一个或数个形态各异的大小整体组成的,构成集合的事物或对象称作元素或是成员,集合写在大括号 { } 里面,元素之间用逗号隔开。
myset = {'yys','玉藻前','10086'}
myset = {'yys'}
4.5.1.2 创建集合
可以使用大括号 { } 或者 set() 函数创建集合
myset = {'yys','玉藻前','10086'}
thisset = set(('yys','玉藻前','10086'))
创建空集合可以使用:myset = set()
,不可以使用myset={}
是用来创建空集合,因为这是创建空字典的方法
4.5.2 基本操作
4.5.2.1 访问集合值
集合是一个无序的不重复的元素序列,不能通过索引访问
4.5.2.2 添加元素
s.add()
将元素 x 添加到集合中,如果元素已存在,则不进行任何操作
myset = {'yys','玉藻前','10086'}
myset.add(110) # 将元素 110 添加到集合中
myset.add('yys') # 如果元素已存在,不进行任何操作
s.update()
将元素 x 添加到集合中,如果元素已存在,则不进行任何操作,x可以是列表,元组,字典
myset = {'yys','玉藻前','10086'}
myset.update([1,4]) # 将元素 1,4 一个个添加到集合中
print(myset) # {1, 4 'yys','玉藻前','10086'}
4.5.2.2 移除元素
s.remove()
元素 x 从集合 s 中移除,如果元素不存在,则会发生错误
myset = {'yys','玉藻前','10086'}
myset.remove('yys') # 移除'yys'
print(myset) # {'玉藻前','10086'}
myset.remove('yys') # 元素不存在,报错
s.discard()
元素 x 从集合 s 中移除,如果元素不存在,不会发生错误
myset = {'yys','玉藻前','10086'}
myset.remove('1111') # 移除'111',元素不存在,不报错
print(myset) # {'yys','玉藻前','10086'}
**s.pop() **
随机删除集合中的一个元素
myset = {'yys','玉藻前','10086'}
myset.pop() # set 集合的 pop 方法会对集合进行无序的排列,然后删除这个无序排列集合的第一个元素
4.5.2.3 计算集合元素个数
len(s)
计算集合中元素个数。
myset = {'yys','玉藻前','10086'}
len(myset) # 3
4.5.2.4 清空集合
s.clear()
myset = {'yys','玉藻前','10086'}
myset.clear() # 清空集合
4.5.2.5 成员运算符
in:判断元素是否在集合中
myset = {'yys','玉藻前','10086'}
print("yys" in myset) # 元素在集合中,返回True
print("lol" in myset) # 元素不在集合中,返回False
not in:判断元素是否不在集合中
myset = {'yys','玉藻前','10086'}
print("lol" not in myset) # 元素不在集合中,返回True
print("yys" in myset) # 元素在集合中,返回False
4.5.3 集合常用函数
方法 | 描述 | 返回值 |
---|---|---|
add() | 添加元素 | 无返回值,直接修改原集合 |
update() | 给集合添加元素 | 无返回值,直接修改原集合 |
pop() | 随机移除元素 | 无返回值,直接修改原集合 |
remove() | 移除指定元素 | 返回移除的元素,且修改原集合 |
discard() | 移除指定元素 | 无返回值,直接修改原集合 |
copy() | 拷贝一个集合 | 返回原集合 |
clear() | 移除集合中的所有元素 | 无返回值,直接修改原集合 |
union() | 返回两个集合的并集,重复的元素只会出现一次 | 返回两个集合的并集 |
注意:集合中的元素是可以改变的
4.6 DICtionary 字典类型
4.6.1 初识字典
4.6.1.1 字典定义
字典(dictionary)是Python中另一个非常有用的内置数据类型。
字典是一种映射类型,字典用 { } 标识,它是一个无序的 键(key) : 值(value) 的集合,可以提供基于 key 检索 value 的场景
生活中的字典是: python中的字典是:
[字]:[含义] [key]:[value]
字典的每个键值对
key=>value 用冒号 : 分割,每个对之间用逗号(,)分割,整个字典包括在花括号 {} 中 ,如下:
d = {key1 : value1, key2 : value2, key3 : value3 }
tinydict = {'name': 'yys', 'age': 18, 'cop': 'wy'}
字典中,键(key)必须使用不可变类型,值可以取任何数据类型。key 必须唯一,否则会遗漏部分键值对,如下:
tinydict = {'name': 'yys', 'age': 18, 'age':19 }
print(tinydict) # 输出tinydict = {'name': 'yys','age':19 } ,重复的key只会保留最后一个的键值对
注意:字典中的 key 不允许重复,重复添加等同于覆盖原有的数据
4.6.1.2 创建字典
可以使用花括号 {} 函数创建字典
hashmap = {'name': 'yys', 'age': 18, 'cop': 'wy'}
创建空字典可以使用:hashmap = {}
或者 hashmap = dict()
注意:dict 作为 Python 的关键字和内置函数,变量名不建议命名为 dict。
4.6.1.3 嵌套字典
字典的 value 可以是任意数据类型,所以 value 也可以是一个字典
stu_score_dict = {'周杰伦': {'语文': 100, '数学': 90}, '林俊杰': {'语文': 90, '数学': 88}}
# 写成另一种通俗易懂的排列
stu_score_dict = {
'周杰伦': {'语文': 100, '数学': 90},
'林俊杰': {'语文': 90, '数学': 88}
}
4.6.2 基本操作
4.6.2.1 访问字典值
字典是无序的对象集合,字典同集合一样,不可以使用下标索引(偏移存取),字典当中的元素是通过键来存取的。
访问字典中的值,只需要把相应的键放入到方括号中:
my_dict = {'name': 'zzz','code':1, 'age': 18}
print (my_dict['one']) # 输出键为 'one' 的值
print (my_dict) # 输出完整的字典
如果用字典里没有的键访问数据,会输出错误:
my_dict = {'name': 'zzz','code':1, 'age': 18}
print (my_dict['yys']) # 程序报错,KeyError: 'yys'
4.6.2.2 添加字典元素
语法:dict[key]=value , 结果:字典被修改,新增一个键值对
my_dict = {'name': 'zzz', 'age': 18}
my_dict['sex']= 'boy' # 新增一个键值对
print(my_dict) # 输出添加后的结果,{'name': 'zzz', 'age': 18, 'sex':'boy'}
4.6.2.3 更新字典元素
语法:dict[key]=value , 结果:字典被修改,键值对被更新
原因:因为字典中 key 不可以重复,所以对已存在的key 进行上述操作,就是更新 value 值
my_dict = {'name': 'zzz', 'age': 18, 'sex':'boy'}
my_dict['sex']= 'girl' # 修改键sex 的值
print(my_dict) # 输出修改后的结果,{'name': 'zzz', 'age': '18', 'sex':'girl'}
4.6.2.4 删除字典元素
语法1:dict.pop(key) , 结果:获取指定key 的值,同时字典被修改,字典中指定key 的数据被删除
my_dict = {'name': 'zzz', 'age': 18, 'sex':'boy'}
print(my_dict.pop('sex')) # 删除键 sex ,并返回结果: 'boy'
print(my_dict) # 输出删除后的结果,{'name': 'zzz', 'age': 18}
语法2:del dict[key] , 结果:删除 字典中指定key 的数据
my_dict = {'name': 'zzz', 'age': 18, 'sex':'boy'}
del my_dict[sex] # 删除键 sex
print(my_dict) # 输出删除后的结果,{'name': 'zzz', 'age': 18}
同时,del 还可以删除整个字典,语法:del dict
my_dict = {'name': 'zzz', 'age': 18, 'sex':'boy'}
del my_dict # 删除字典
print(my_dict) # 程序报错,NameError: name 'my_dict' is not defined
4.6.2.5 清空字典
语法:dict.clear() ,结果:清空元素
my_dict = {'name': 'zzz', 'age': 18, 'sex':'boy'}
my_dict.clear() # 删除字典
print(my_dict) # 输出清空后的结果,{}
4.6.2.6 获取字典全部的key
语法:dict.keys()
my_dict = {'name': 'zzz', 'age': 18, 'sex':'boy'}
print(my_dict.keys()) # 输出结果,dict_keys(['name', 'age', 'sex'])
4.6.2.7 成员运算符
in:判断键是否在字典中
my_dict = {'name': 'zzz', 'age': 18, 'sex':'boy'}
print("name" in my_dict) # 元素在集合中,返回True
print("lol" in my_dict) # 元素不在集合中,返回False
not in:判断键是否不在字典中
my_dict = {'name': 'zzz', 'age': 18, 'sex':'boy'}
print("lol" not in my_dict) # 元素不在集合中,返回True
print("name" in my_dict) # 元素在集合中,返回False
4.6.3 字典常用方法和内置函数
函数/方法 | 描述 | 返回值 |
---|---|---|
len(dict) | 计算字典元素个数 | 返回键的总数 |
max(dict) | 求的是 key 的最大值 | 返回键的最大值 |
min(dict) | 求的是 key 的最小值 | 返回键的最小值 |
dict.fromkeys() | 创建一个新字典,以序列seq中元素做字典的键,val为字典所有键对应的初始值 | 返回新字典 |
dict.get(key, default=None) | 返回指定键的值,如果键不在字典中返回 default 设置的默认值 | 返回指定键的值 |
dict.setdefault(key, default=None) | 和get()类似, 但如果键不存在于字典中,将会添加键并将值设为default | 返回键的值 |
dict.update(dict2) | 把字典参数 dict2 的 key/value(键/值) 对更新到字典 dict 里 | 无返回值,会修改原字典 |
dict.items() | 以列表返回视图对象,是一个可遍历的key/value 对 | 返回key/value 对的列表 |
dict.keys() | 以列表返回视图对象,是一个可遍历的key 集 | 返回 key 视图对象 |
dict.values() | 以列表返回视图对象,是一个可遍历的value 集 | 返回 value 视图对象 |
pop(key[,default]) | 删除字典 key(键)所对应的值,返回被删除的值。 | 返回被删除的值 |
popitem() | 反向排序列表中元素 | 返回并删除字典中的最后一对键和值 |
dict.clear() | 删除字典内所有元素 | 无返回值,会修改原列表 |
dict.copy() | 复制字典 | 返回一个字典的浅复制 |
注意:视图对象不是列表,不支持索引,可以使用 list() 来转换为列表。
4.7 数据类型通用特性
不同的数据类型尽管各自有各自特点,但是他们也有一些相通的操作
4.7.1 for循环遍历
在遍历上,5类数据类型都支持for循环遍历
注意:列表、元组、字符串支持while循环,字典、集合不支持(无法下标索引)
4.7.2 len() 统计元素个数
语法:len(容器)
my_list = [1, 2, 3, 4, 5]
my_tuple = (1, 2, 3, 4, 5)
my_str = 'abcde'
my_set = {1, 2, 3, 4, 5}
my_dict = {'key1': 1, 'key2': 2, 'key3': 3, 'key4': 4, 'key5': 5}
print((len(my_list))) # 输出:5
print((len(my_tuple))) # 输出:5
print((len(my_str))) # 输出:5
print((len(my_set))) # 输出:5
print((len(my_dict))) # 输出:5
4.7.3 max() 统计最大元素
语法:max(容器)
my_list = [1, 2, 3, 4, 5]
my_tuple = (1, 2, 3, 4, 5)
my_str = 'abcde'
my_set = {1, 2, 3, 4, 5,}
my_dict = {'key1': 1, 'key2': 2, 'key3': 3, 'key4': 4, 'key5': 5}
print((max(my_list))) # 输出:5
print((max(my_tuple))) # 输出:5
print((max(my_str))) # 输出:'e'
print((max(my_set))) # 输出:5
print((max(my_dict))) # 输出:'key5' 得到的是最大的键
字符串使用ascii 码比较大小,字典返回最大的键
4.7.4 min() 统计最小元素
my_list = [1, 2, 3, 4, 5]
my_tuple = (1, 2, 3, 4, 5)
my_str = 'abcde'
my_set = {1, 2, 3, 4, 5,}
my_dict = {'key1': 1, 'key2': 2, 'key3': 3, 'key4': 4, 'key5': 5}
print((min(my_list))) # 输出:1
print((min(my_tuple))) # 输出:1
print((min(my_str))) # 输出:'a'
print((min(my_set))) # 输出:1
print((min(my_dict))) # 输出:'key1' 得到的是最小的键
4.7.5 sorted() 排序
sorted() 函数会返回一个排序列表,不改变原有序列
sorted(容器,reverse=False),reverse = False表示升序排列
my_list = [2, 3, 4, 1, 5]
my_tuple = (2, 3, 1, 4, 5)
my_str = 'deabc'
my_set = {2, 1, 3, 4, 5,}
my_dict = {'key2': 2, 'key3': 3, 'key1': 1, 'key4': 4, 'key5': 5}
print((sorted(my_list))) # 输出:[1, 2, 3, 4, 5]
print((sorted(my_tuple))) # 输出:[1, 2, 3, 4, 5]
print((sorted(my_str))) # 输出:['a', 'b', 'c', 'd', 'e']
print((sorted(my_set))) # 输出:[1, 2, 3, 4, 5]
print((sorted(my_dict))) # 输出:['key1', 'key2', 'key3', 'key4', 'key5']
注意:排序后,全部的数据类型都会变为列表
4.7.6 数据类型转换
python 是弱类型语言,跟 C语言强类型语言不一样,它并不需要声明变量的数据类型
Python 数据类型转换可以分为两种:隐式类型转换 和 显式类型转换
4.7.6.1 隐式类型转换
在隐式类型转换中,Python会自动将一种数据类型转换为另一种数据类型,不需要我们去干预。例如:
num_int = 123
num_flo = 1.23
num_new = num_int + num_flo
在运算时,python会自动将较低数据类型(整数)就会转换为较高数据类型(浮点数)以避免数据丢失
4.7.6.2 显式类型转换
转列表:list(容器)
print((list(my_tuple))) # 输出:[1,2,3,4,5]
print((list(my_str))) # 输出:['a','b','c','d','e']
print((list(my_set))) # 输出:[1,2,3,4,5]
print((list(my_dict))) # 输出:['key1', 'key2', 'key3', 'key4', 'key5']
转元组:tuple(容器)
print((list(my_list))) # 输出:(1,2,3,4,5)
print((list(my_str))) # 输出:('a','b','c','d','e')
print((list(my_set))) # 输出:(1,2,3,4,5)
print((list(my_dict))) # 输出:('key1', 'key2', 'key3', 'key4', 'key5')
转字符串:str(容器)
print((str(my_list))) # 输出:[1,2,3,4,5]
print((str(my_tuple))) # 输出:(1,2,3,4,5)
print((str(my_set))) # 输出:{1,2,3,4,5}
print((str(my_dict))) # 输出:{'key1': 1, 'key2': 2, 'key3': 3, 'key4': 4, 'key5': 5},字典转为字符串才会保留value
只有字典转为字符串才会保留value
转集合:set(容器)
print((set(my_list))) # 输出:{1, 2, 3, 4, 5}
print((set(my_tuple))) # 输出:{1, 2, 3, 4, 5}
print((set(my_str))) # 输出:{'d', 'a', 'e', 'c', 'b'}
print((set(my_dict))) # 输出:{'key2', 'key5', 'key4', 'key1', 'key3'}, 仅保留键,保持无序
数据类型转为集合,重复元素的会被去重
转字典:dict(容器)
转字典需要满足一定的条件,因为一定要满足键值对这个概念,所以一般的列表、元组、字符串、集合都无法转为字典
在显式类型转换中,使用预定义函数将用户对象的数据类型转换为所需的数据类型。
常用的预定义函数如下:
函数 | 描述 |
---|---|
int(x [,base]) | 将x转换为一个整数 |
float(x) | 将x转换到一个浮点数 |
complex(real [,imag]) | 创建一个复数 |
str(x) | 将对象 x 转换为字符串 |
tuple(s) | 将序列 s 转换为一个元组 |
set(s) | 转换为可变集合 |
dict(d) | 创建一个字典。d 必须是一个 (key, value)元组序列。 |
frozenset(s) | 转换为不可变集合 |
chr(x) | 将一个AScii码转换为字符 |
ord(x) | 将一个字符转换为对应AScii码 |
oct(x) | 将一个整数转换为一个2进制字符串 |
oct(x) | 将一个整数转换为一个八进制字符串 |
hex(x) | 将一个整数转换为一个十六进制字符串 |
这些函数返回一个新的对象,表示转换的值,用法举例:
var = int(200) # 强调var为整型
var = dict(name="yys", age=188) # 强调var为字典
# 进制转换
i = 23
bin(i) # 转换成二进制0b10111
4.8 推导式
Python 推导式是一种独特的数据处理方式,可以从一个数据序列构建另一个新的数据序列的结构体。
Python 支持各种数据结构的推导式:
- 列表(list)推导式
- 字典(dict)推导式
- 集合(set)推导式
- 元组(tuple)推导式
4.8.1 列表推导式
列表推导式格式为:
[表达式 for 变量 in 列表]
[out_exp_res for out_exp in input_list]
或者
[表达式 for 变量 in 列表 if 条件]
[out_exp_res for out_exp in input_list if condition]
- out_exp_res:列表生成元素表达式,可以是有返回值的函数
- for out_exp in input_list:迭代 input_list 将 out_exp 传入到 out_exp_res 表达式中
- if condition:条件语句,可以过滤列表中不符合条件的值
例子:过滤掉长度小于或等于3的字符串列表
在没有学习列表推导式前,我们的正常写法是这样的:
names = ['yys','timlin','bobo','wen','wendi']
new_names = []
for name in names:
if len(name)>3:
new_names.append(name)
print(new_names)
使用列表推导式,仅仅只需一句话就可以将for循环的内容写完
names = ['yys','timlin','bobo','wen','wendi']
new_names = [name for name in names if len(name)>3]
print(new_names) # ['timlin','bobo','wendi']
列表推导式写法是将for语句简化,但其提升的效率极高
又如例子:计算 30 以内可以被 3 整除的整数:
multiples = [i for i in range(30) if i % 3 == 0]
扩展:
语句格式也可以为:结果值1 if 判断条件 else 结果2 for 变量名 in 原列表
如:[i if i % 3 else [] for i in range(30)]
但是 else不可以省略,原理的话应该是使用了三目运算符
即 i if i % 3 else []
可以看做是一个表达式
python 的列表推导比 for 循环、while 循环要快很多。分析了下原因:列表推导内的迭代在解释器内是以 C 语言的速度运行的 (一般是 for 循环的两倍,对大型文件操作而言,用列表推导效果尤其明显), while 语句是纯粹用 Python 代码写成,所以速度最慢。
4.8.2 字典推导式
字典推导基本格式:
{key_expr: value_expr for value in collection}
或
{key_expr: value_expr for value in collection if condition}
例子:
使用字符串及其长度创建字典:
listdemo = ['Google','Runoob', 'Taobao']
# 将列表中各字符串值为键,各字符串的长度为值,组成键值对
newdict = {key:len(key) for key in listdemo}
print(newdict) # {'Google': 6, 'Runoob': 6, 'Taobao': 6}
字典推导式基本结构与列表推导式一致,区别是
- 字典推导式中的表达式一定要键值对,
- 外层要使用{}
4.8.3 集合推导式
集合推导式基本格式:
{expression for item in Sequence}
或
{expression for item in Sequence if conditional}
例子:
判断不是 abc 的字母并输出:
a = {x for x in 'abracadabra' if x not in 'abc'}
print(a) # {'d', 'r'}
字典推导式基本结构与列表推导式一致,区别是:
- 外层要使用{}
4.8.4 元祖推导式
元组推导式可以利用 range 区间、元组、列表、字典和集合等数据类型,快速生成一个满足指定需求的元组。
元组推导式基本格式:
(expression for item in Sequence)
或
(expression for item in Sequence if conditional)
例子:
生成一个包含数字 1~9 的元组:
tpl = (x for x in range(1,10))
print(tuple(tpl)) # (1, 2, 3, 4, 5, 6, 7, 8, 9)
元组推导式和列表推导式的用法区别:
- 元组推导式是用()圆括号
- 另外元组推导式返回的结果是一个生成器对象,需要使用tuple()转换成元祖
这就开始学晕了吗?加油后面继续!
5. 各种控制语句
5.1 条件控制语句if-else
5.1.1 if语句的基本格式
程序中的判断语句:
if 判断的条件:
条件成立时,要做的事
如下:
age = 18
if age >= 18: 当条件为真时,执行if里面代码块的内容
print('我已经成年了') # 输出:我已经成年了
注意:
- 判断语句的结果,必须是布尔类型:True或False
- 归属if判断的代码语句块,需要填充4个空格缩进
5.1.2 if-else语句的基本格式
if 判断的条件:
条件成立时,要做的事
else:
不满足条件时,要做的事
如下:
age = int(input("请输入你的年龄:"))
if age >= 18: # 当条件为真时,执行if里面代码块的内容
print('你已经成年了') # 输出:我已经成年了
else: # 当条件为假时,执行else里面代码块的内容
print('你未成年了') # 输出:我未成年了
注意:else和if同级,else里面的内容也要使用缩进
5.1.3 if-elif-else语句的基本格式
if 条件1:
条件1成立时,要做的事
elif 条件2:
条件2成立时,要做的事
else:
以上条件都不满足时,要做的事
如下案例:
age = int(input("请输入你的年龄:"))
if age >= 18: # 当条件为真时,执行if里面代码块的内容
print('你已经成年了') # 输出:你已经成年了
elif age <4: # 当条件为真时,执行if里面代码块的内容
print('你不可以游玩') # 输出:你不可以游玩
else: # 当条件为假时,执行else里面代码块的内容
print('虽然未成年,但是也可也玩') # 输出:虽然未成年,但是也可也了
5.1.4 if嵌套
可以通过多个if来控制不同的条件判断
if 条件1:
条件1成立时,要做的事
if 条件2:
条件2成立时,要做的事
else:
条件2不满足时,要做的事
else:
条件1不满足时,要做的事
- 嵌套语句可以用于多条件和多层次的逻辑判断中
- 嵌套语句可以根据需求,自由组合if -elif-else
- 嵌套语句一定要注意空格缩进,py中通过空格缩进来决定层次关系
5.2 循环控制语句
5.2.1 while控制语句
while循环语法格式:
while 条件:
条件满足时,要做的事
1、while的条件需要布尔类型
2、需要规划循环终止条件,否则将无限循环
3、while需要跟if一样,需要空格缩进
5.2.2 while 循环使用 else 语句
while…else…
如果 while 后面的条件语句为 false 时,则执行 else 的语句块。
语法格式如下:
while <expr>:
<statement(s)>
else:
<additional_statement(s)>
# expr 条件语句为 true 则执行 statement(s) 语句块,如果为 false,则执行 additional_statement(s)。
如下:
count = 0
while count < 5:
print (count, " 小于 5")
count = count + 1
else:
print (count, " 大于或等于 5")
注意:while循环的循环条件时自定义的,自行控制循环条件
5.2.3 for循环控制语句
for基础语法格式:
for 临时变量 in 待处理数据集:
循环满足条件时执行的代码
例如遍历一个字符串:
# 定义一个字符串str_t
str_t = 'name'
# 使用for循环处理字符串
for x in str_t:
print(x)
for循环只能从被处理的数据集中,依次取出内容进行处理。
理论上来说,for循环是无法构建无限循环的(被处理数据集无法无限大)
for循环是一种轮询机制,对一批内容进行逐个处理
5.2.4 for循环使用 else 语句
for…else
在 Python 中,for…else 语句用于在循环结束后执行一段代
语法格式如下:
for item in iterable:
# 循环主体
else:
# 循环结束后执行的代码
当循环执行完毕(即遍历完 iterable 中的所有元素)后,会执行 else 子句中的代码,如果在循环过程中遇到了 break语句,则会中断循环,此时不会执行 else 子句。
sites = ["age", "yys","time-s","lol"]
for site in sites:
if site == "yys":
print("找到阴阳师!")
break
print("循环数据 " + site)
else:
print("没有循环数据!")
print("完成循环!")
在执行上面的脚本时,在循环到 "yys"时会跳出循环体。所以else语句不会被执行
5.2.5 for语句中的range语句
range格式有三种
range(num)
或range(num1,num2)
或range(num1,num2,step)
语法1:
range(num)
获得一个从0开始,到num结束的数字序列(不含num本身)
如:range(5)得到的数据是:0,1,2,3,4
语法2:
range(num1,num2)
获得一个从num1开始,到num2结束的数字序列(不含num2本身)
如:range(5,10)得到的数据是:5,6,7,8,9
语法3:
range(num1,num2,step)
获得一个从num1开始,到num2结束的数字序列(不含num2本身),数字之间的步长为step(默认为1)
如:range(5,10,2)得到的数据是:5,7,9
5.2.6 for循环的变量作用域
for基础语法格式:
for 临时变量 in 待处理数据集:
循环满足条件时执行的代码
临时变量,在编程规范上,作用范围仅限定在for循环的内部
如果在for循环外部去访问这个临时变量:
- 实际上是可以访问到的
- 但是编程规范上不允许、不建议这么做
- 如需访问临时变量,可以预先在循环外定义它
5.2.7 循环的嵌套
循环的嵌套跟if的嵌套类似
for循环 或 while循环:
循环满足时要做的事情
...
for循环 或 while循环:
循环满足时要做的事情
...
注意缩进,嵌套for和while都是通过缩进来确定层次关系(即自己控制的内容)
for循环和while循环可以相互嵌套使用
5.2.8 循环中断break及continue
break:
直接结束所在循环
continue:
中断所在循环的当次执行,直接进入下一次循环
注意:
- break及continue 都能在for和while循环中使用
- 在嵌套的循环中,break及continue 都只能作用在所在循环中,无法控制上层循环
至此第五章内容结束!
6. 函数
6.1 函数的定义
函数:是组织好的,可以重复使用的,用来实现特定功能的代码段,能提高应用的模块性,和代码的重复利用率
Python 中使用 def 关键字来定义函数。
函数的定义:
def 函数名(参数列表):
函数体
return 返回值
函数的调用:
函数名(参数)
例如:
def say_hi():
print("hi 我是yys")
return False
say_hi()
注意:
- 参数可以省略,但仍需要在函数名后加入()
- 返回值可以省略
- 函数必须先定义在使用
6.2 函数参数
函数传入参数的作用使得在函数运行的时候,可以接受外部传入的数据,传入多个参数时,需要逗号隔开
使用方式有:
def add(x,y):
result = x + y
return result
c = a(2,3)
形式参数:函数定义中的参数 ,如上 (x,y) 称之为形式参数
实际参数:函数调用中的参数,如上 (2,3) 称之为实际参数
函数的传入参数可以有以下几类:
- 位置参数
- 关键字参数
- 默认参数
- 不定长参数
6.2.1位置参数
位置参数:调用函数时根据函数定义的参数位置来传递参数,须以正确的顺序传入函数。
def printme(name,age):
"打印任何传入的字符串"
print (f'你的名字是{name},年龄{age})
return
printme() # TypeError: printme() missing 1 required positional argument: 'str'
printme('bob',18)
注意:
传递的参数与定义的参数的顺序及个数必须一致
6.2.2 关键字参数
关键字参数和函数调用关系紧密,函数调用使用关键字参数来确定传入的参数值。
使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名自动匹配传入的参数值
def printinfo( name, age ):
"打印任何传入的字符串"
print ("名字: ", name)
print ("年龄: ", age)
return
printinfo( age=50, name="runoob" )
此例子可以得出,在函数调用时只需要带上关键字参数(形参带上),就可以让形参自动对应实际参数对应值
注意:
函数调用时,如果有位置参数,那么位置参数必须在关键字参数的前面,但是关键字参数之间不存在先后顺序
6.2.3 默认参数
调用函数时,如果没有传递参数,则会使用默认参数。以下实例中如果没有传入 age 参数,则使用默认值:
def printinfo( name, age = 35 ):
"打印任何传入的字符串"
print ("名字: ", name)
print ("年龄: ", age)
return
printinfo( name="runoob" )
此例子可以看出,如果一开始在定义函数时就给形参设置了默认值,那么在实际调用时不传入该形参,就会使用默认值
注意:
默认参数一定要在关键字或者位置参数后面
6.2.4 不定长位置参数 *args
即不清楚实际会传入的参数有多少,需要能处理比当初声明时更多的参数,使用不定长参数.
在定义的形式参数前面加了星号 * 的,则此参数会以元组(tuple)的形式存在,接收不定长数量的参数传入
def printinfo( arg1, *vartuple ):
"打印任何传入的参数"
print (arg1)
print (vartuple)
printinfo( 70, 60, 50 )
一般来顺分配原则是必需参数会对应相应位置的实际参数值,剩余的全归类为不定长位置参数
6.2.5 不定长关键字参数 **kwargs
如果在参数前面加了两个星号 ** ,则参数会以字典的形式导入
def printinfo( arg1, **vartuple ):
"打印任何传入的参数"
print (arg1)
print (vartuple)
printinfo(1, a=2,b=3)
顺序一般是:先必须参数、再关键字参数(包含默认参数)、然后是不定长位置参数、不定长关键字参数
6.3 匿名函数
Python 使用 lambda 来创建匿名函数。
函数的定义中:
- def 关键字,可以定义带有名称的函数
- Lambda关键字,可以定义匿名函数(无名称)
有名称的函数,可以无限重复使用
匿名函数,只可临时使用一次
匿名函数的语法:
lambda 参数列表:表达式
lambda [arg1 [,arg2,.....argn]]:expression
- lambda 是关键字,表示定义匿名函数
- 表达式就是函数的执行逻辑,只能写一行,而不是一个代码块。
- lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。
- 虽然 lambda 函数看起来只能写一行,却不等同于 C 或 C++ 的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。
lambda 的参数形式:
无参数 lambda : 表达式
一个参数 lambda 参数 : 表达式
默认参数 lambda key=value : 表达式
不定长位置参数 lambda *args : 表达式
不定长关键字参数 lambda **kwargs : 表达式
例子:
sum = lambda a,b:a+b
sum(a,b)
6.4 函数作为参数传递
将一个函数名A作为参数 ,传入到另一个函数B中。如:
def test_func(func):
result = add(1,2)
print(result)
def add(x,y):
return x+ y
test_func(add)
- test_func需要一个函数作为参数传入,这个函数需要接收2个数字进行计算,计算逻辑由被传入函数来决定
- add函数接收2个函数进行计算
- test_func(add)将函数add 作为参数,传入到了test_func函数中使用,最终在test_func函数内部,由传入的add函数,完成计算操作
在上面这个例子中,计算逻辑的传递,而非数据的传递
任何逻辑都可以行定义一个函数作为函数参数传递
6.5 函数的返回值
函数的返回值:在函数执行完成后,返回给函数调用者的结果
语法:
return [表达式]
return 语句用于退出函数,选择性地将一个表达式返回给调用方。
注意:
- 不带参数值的 return 语句返回 None
- 函数体在遇到 return 语句时就会返回,所以 return 后的代码不会执行
扩展:
None表示:空的、无意义的
其类型是:<class 'NoneType'>
应用场景:
- 用在函数返回值上
函数返回的 None,表示函数返回了空的意思,等效于return None
- 用在if判断上
在if判断中,None 等同于 False - 用在声明无意义的变量上
定义变量,但暂时不需要变量有具体的值。如:name = None
6.6 函数的说明文档
就是通过多行注释,在函数定义和函数体之前给函数添加注释,用于辅助理解函数的作用
def func(x):
"""
函数说明
:param x:
:return:
"""
return
- 函数说明 – 用于简洁描述此函数功能
- :param: – 用于解释参数
- :return: – 用于解释返回值
6.6 函数的嵌套
函数跟循环控制语句一样,可以嵌套,即:
def func1():
return true
def func2():
c = func1()
return c
c2 = func2()
即将一个定义的函数在另一个函数里面调用,那么在调用func2时,就会先执行func1
6.7 变量的作用域
变量的作用域是指变量的作用范围(变量可以在哪用,不可以在哪用)
py中有四种作用域:
- L(Local):最内层,包含局部变量,比如一个函数/方法内部。
- E(Enclosing):包含了非局部(non-local)也非全局(non-global)的变量。比如两个嵌套函数,A(B()) ,那么对于 B 中的变量来说,A 中的作用域就为 nonlocal。
- G(Global):当前脚本的最外层,比如当前模块的全局变量。
- B(Built-in): 包含了内建的变量/关键字等
扩展:python中变量查找是遵循LGB原则,即优先在局部作用域(local scope)中对变量进行查找,失败则在闭包或者外层嵌套函数的外部作用域(enclosing scope)中查找,没有找到则在全局作用域(global scope)中进行查找,最后尝试再内建作用域(build-in scope)内查找
规则顺序: L –> E –> G –> B
总结:在局部找不到,便会去局部外的局部找(例如闭包/嵌套),再找不到就会去全局找,最后去内置中找。
局部变量 Local
局部变量是指变量是定义在函数体内的变量,即变量只在函数体内部生效
def A():
num =100
print(num)
A()
print(num) # 报错,num未定义
在函数外部调用函数内部定义的变量是非法的,因为局部变量在函数体外无法使用
非全局变量/非局部变量 Enclosing
def outer():
num = 2 # 闭包函数外的函数中,定义一个局部变量
def inner():
print(num) # 输出外部局部变量的值2
num = 3 重新定义一个局部变量
print(num)
inner()
print(num)
outer() # 2, 3, 2
- 在此例函数outer,知道我们可以在嵌套函数内部使用外部函数定义的局部变量
- 在函数里面,对变量进行修改,其实是重新定义了一个局部变量,并对其赋值,其不影响外部函数的非全局变量num
- 所以在最后print(num)是输出仍然是2
如果需要在函数内部实现对全局变量的修改,需要使用关键字 nonlocal
def outer():
num = 10
def inner():
nonlocal num # 用nonlocal声明内部函数的局部变量num 为非全局变量
num = 100
print(num)
inner()
print(num)
outer() # 10,100,100 最后输出的print(num)时,此时的num已被inner()成功修改
全局变量 Global
指变量在函数体内、外都能生效
num = 100
def A():
print(num)
def B():
print(num) # 输出全局变量的值
num = 50 # 重新定义一个局部变量num
print(num) # 输出局部变量的值
A() # 100
B() # 100 ,50
print(num) # 100
- 在此例函数A,知道我们可以在函数内部使用外部定义的全局变量
- 函数B,可以让我们了解,当在函数里面,对变量进行修改,其实是重新定义了一个局部变量,并对其赋值,其不影响外部的全局变量
- 所以在最后print(num)是输出仍然是100
如果需要在函数内部实现对全局变量的修改,需要使用关键字 global
num = 100
def C():
global num # 将内部的局部变量num定义全局变量
num =20
print(num)
C() # 20
print(num) # 20 最后输出的print(num)时,此时的num已被C()成功修改
7.文件
7.1 文件的编码
7.1.1 什么是文件
一般来说,我们在计算机中编写的数据会存储在内存中,但是内存中存放的数据在计算机关机后即会消失。如果要长久保存数据,就要使用硬盘、光盘、U盘等设备。为了便于数据的管理和检索,引入了“文件”的概念
一篇文章、一段视频、一个可执行的文件都可以被保存为一个文件,并且赋予一个文件名。操作系统以文件为单位管理磁盘中的数据。文件可分多种类别:
- 文本文件:TXT等
- 视频文件:WMV、AVI、MPEG、DV、MOV、MOD等
- 音频文件:MP3、WAV、FLAC、WMA、MIDI、CDA等
- 图像文件:jpg、png、gif等
- 可执行文件:.exe、.sys等
7.1.2 编码规则
为什么需要使用编码?
我们知道,计算机底层的机器语言只识别0和1,对于丰富的文本内容计算机又该如何识别呢?我们是怎么转化的呢?
可以使用编码技术(密码本)将内容翻译成0和1存储在计算机中
编码技术:使用特定的翻译规则,将内容翻译成二进制0和1,不同的编码技术,翻译的结果不一样,所以写入和打开文件的编码技术要一致
文件的编码技术有:UTF-8、GBK、Big5等
UTF-8是目前全球通用的编码格式,除非特别要求,一般默认使用此编码格式
7.2 文件的常用操作
文件操作的步骤:
- 打开
- 读写
- 关闭
7.2.1 文件的打开操作
Python 对文件进行操作,需要使用open()
方法用于打开文件,并返回文件对象
完整的语法格式为:
open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
参数说明:
- file:文件路径(相对或者绝对路径),必需参数
- mode:文件打开模式,可选,默认只读
- buffering:设置缓冲,可选,默认-1
- encoding:编码格式,可选,一般为UTF-8
- errors: 报错级别,可选
- newline: 区分换行符,可选
- closefd: 传入的file参数类型,可选
- opener: 设置自定义开启器,开启器的返回值必须是一个打开的文件描述符。可选
常用的格式为:open(file,mode,encoding='UTF-8')
mode参数:
参数 | 描述 | 文件指针位置 |
---|---|---|
r | 以只读方式打开文件,默认模式 | 位于文件的开头 |
rb | 以二进制格式打开一个文件用于只读,一般用于非文本文件如图片等 | 位于文件的开头 |
r+ | 打开一个文件用于读写 | 位于文件的开头 |
rb+ | 以二进制格式打开一个文件用于读写,一般用于非文本文件如图片等 | 位于文件的开头 |
w | 打开一个文件只用于写入。如果文件不存在,则创建新文件 | 位于文件的开头(原内容会被清空) |
wb | 以二进制格式打开一个文件只用于写入。如果文件不存在,则创建新文件 | 位于文件的开头(原内容会被清空) |
w+ | 打开一个文件用于读写。如果文件不存在,则创建新文件 | 位于文件的开头(原内容会被清空) |
wb+ | 以二进制格式打开一个文件用于读写。如果文件不存在,则创建新文件 | 位于文件的开头(原内容会被清空) |
a | 打开一个文件用于写入。如果文件不存在,则创建新文件 | 位于文件的结尾 |
ab | 以二进制格式打开一个文件用于写入。如果文件不存在,则创建新文件 | 位于文件的结尾 |
a+ | 打开一个文件用于读写。如果文件不存在,则创建新文件 | 位于文件的结尾 |
ab+ | 以二进制格式打开一个文件用于读写。如果文件不存在,则创建新文件 | 位于文件的结尾 |
注意:
- 默认为文本模式,如果要以二进制模式打开,加上
b
- 默认是以只读方式打开文件
- w 和 a都是写入,但是两者文件指针的位置不一样,w 文件指针位于开头,会覆盖原本内容;a文件指针位于结尾,不会覆盖原本内容
举例:
f = open('cc.txt','r',encording="UTF-8")
7.2.2 文件的关闭操作
文件的关闭一般都是用close()
方法,文件打开操作完毕后,必须进行关闭操作
close()
方法用于关闭一个已打开的文件。关闭后的文件不能再进行读写操作, 否则会触发 ValueError 错误
语法格式:file.close()
7.2.3 文件的读取操作
文件的读取有四种方法:
- read() 从文件读取指定的字符数
- readline() 从文件读取整行
- readlines() 用于读取所有行(一次性读取,直到结束符 EOF)并返回列表
- for line in 文件对象
read()
用于从文件读取指定的字符数(文本模式 t)或字节数(二进制模式 b)
语法格式:file.read([size])
参数: size: 从文件中读取的字符数(文本模式)或字节数(二进制模式),默认为 -1,如果未给定参数 size 或 size 为负数则表示读取整个文件
返回值:返回读取到的内容
举例,读取文件的内容:
cc.txt文件内容如下:
这是第一行
这是第二行
这是第三行
这是第四行
这是第五行
# 打开文件
fd = open("cc.txt", "r+",encording="UTF-8")
print ("文件名为:{}".format(fd.name)
line = fo.read(10) # 读取10个字符
print ("读取的字符串: {}".format(line)
fd.close() # 关闭文件
总结:read()方法,可以指定需要读取的字符数量
readline()
用于从文件读取整行,包括 “\n” 字符。行的切分是" \n "字符
语法格式:file.readline([size])
参数: size: 从文件中读取的字符数(文本模式)或字节数(二进制模式),默认为 -1,如果未给定参数 size 或 size 为负数则返回指定大小的字节数,包括 “\n” 字符
当执行readline() 时,code会扫描文件中的每一个字节,直到找到一个 \n 位置,然后停止并读取此前的文件内容。并且fileobject 会记录每次调用readline()后的对于读取位置,这样readline()下次被调用的时候就会读取下一行。
举例,读取文件内容:
cc.txt文件内容:
1:www.runoob.com
2:www.runoob.com
3:www.runoob.com
4:www.runoob.com
5:www.runoob.com
fd = open("cc.txt", "r+",encording="UTF-8") 打开文件,获得一个文件对象
print ("文件名为:{}".format(fd.name)
line = fd.readline() # 读取第一行,并将读取位置指针置于第一行末尾
print ("读取第一行:{}".format(line)
line = fd.readline(5) # 读取第二行的前5个字符,并将读取位置指针置于第二行末尾
print ("读取到的字符串:{}".format(line)
fd.close() # 关闭文件
注意:
- 每次调用
readline()
的fileobject会记录此次读取位置,这样readline()
下次被调用的时候就会读取下一行 - '\n’也会包含在读取的字符串中,常用
strip()
对头尾的空白字符处理
readlines()
用于读取所有行(按照行的方式一次性读取,直到结束符 EOF)并返回列表,每一行的数据为一个元素
语法格式:file.readlines()
返回值:返回列表,包含所有的行
举例,读取文件内容:
cc.txt文件内容:
1:www.runoob.com
2:www.runoob.com
3:www.runoob.com
4:www.runoob.com
5:www.runoob.com
fd = open("cc.txt", "r+",encording="UTF-8") 打开文件,获得一个文件对象
print ("文件名为:{}".format(fd.name)
for line in fd.readlines(): #依次读取每行
line = line.strip() #去掉每行头尾空白
print ("读取到的数据:{}".format(line)
fd.close() # 关闭文件
for line in 文件对象
for 循环读取文件行
格式:for line in 文件对象
每一个临时变量 line 都记录了文件的一行数据
fd = open("cc.txt", "r+",encording="UTF-8") 打开文件,获得一个文件对象
print ("文件名为:{}".format(fd.name)
for line in fd:
print ("读取到的数据:{}".format(line)
fd .close() # 关闭文件
7.2.4 文件的写入操作
写入操作常用方法有:
- write() 向文件中写入指定字符串
- writelines() 向文件中写入一序列的字符串, 换行需要加入’\n’
write()
用于向文件中写入指定字符串。
如果文件打开模式带 b,那写入文件内容时,str (参数)要用 encode 方法转为 bytes 形式
语法格式:file.write([str])
参数:str – 要写入文件的字符串
返回值:返回写入的字符长度。
在文件关闭前或缓冲区刷新前,字符串内容存储在缓冲区中,这时你在文件中是看不到写入的内容的
seek() 方法用于移动文件读取指针到指定位置。
语法格式:file.seek(offset,whence=0)
参数:
- offset – 开始的偏移量,也就是代表需要移动偏移的字节数,如果是负数表示从倒数第几位开始。
- whence:表示要从哪个位置开始偏移:
- 0 代表从文件开头开始算起,
- 1 代表从当前位置开始算起,
- 2 代表从文件末尾算起
举例,
cc.txt文件内容如下:
1:www.runoob.com
2:www.runoob.com
3:www.runoob.com
4:www.runoob.com
5:www.runoob.com
fd = open("runoob.txt", "r+",encording="UTF-8")
print("文件名为: {}".format(fd.name))
str = "6:www.runoob.com"
fd.seek(0, 2) # 重新设置文件指针移动到末尾
line = fd.write( str )
fd.seek(0,0) # 重新设置文件指针移动到开头
for index in range(6):
line = next(fd)
print("文件行号{}{}".format(index, line))
fd.flush() # 刷新 将内存中的文件写入硬盘,可以让文件在未关闭下仍然可以更新内容
fd.close() # 内置了flush方法,关闭文件
注意:
- 操作文件时,需要注意文件指针的位置,如 mode=‘w’ 和 mode=‘a’
- 直接调用write(),内容并未真正写入文件,而是积攒在程序的内存中,称之为缓冲区
- 当调用flush的时候,内容会真正的写入文件,避免频繁的操作硬盘,导致效率下降
writelines()
用于向文件中写入一序列的字符串。
这一序列字符串可以是由迭代对象产生的,如一个字符串列表。换行需要制定换行符 \n。
语法格式:file.writelines([str])
参数:str – 要写入文件的字符串序列
返回值:该方法没有返回值。
fd = open("cc.txt", "w+")
print ("文件名为: ", fd.name)
seq = ["菜鸟教程 1\n", "菜鸟教程 2"] # 字符串序列,换行需要加入'\n'
fd.writelines(seq) # 写入字符串序列
fd.close() # 关闭文件
注意:
- 无返回值
- 换行需要加入’\n’
7.3 os模块的使用
待补充
8. 异常
8.1 异常的定义
异常是指程序在运行过程中发生了错误
bug就是指异常的意思,因为历史上有因虫子导致计算机故障的案例,所以,bug就代表软件出现错误
8.2 异常的捕获
为什么要捕获bug?
因为当程序遇到bug时,会有两种情况:
- 整个程序因为一个bug停止运行
- 对bug进行提醒,整个程序继续运行
当没有对bug进行处理时,我们的程序就只会走第一种情况,但是实际上,我们更希望程序走第二种情况
所以捕获异常作用:提前假设某处会发生异常,当真的发生异常时,可以按照我们预先准备的情况发生
8.2.1 捕获常规异常
基本语法:
try:
可能发生异常的代码
except:
出现异常时执行的代码
举例:尝试以’r’模式打开文件,如果文件不存在,则以’w’模式打开
try:
f = open(‘cc.txt’,‘r’)
except:
f =open(‘cc.txt’.‘w’)
8.2.2 捕获特定异常
基本语法:
try:
可能发生异常的代码
except 异常类型:
出现异常时执行的代码
当需要捕获特定异常时,把该异常类型放在except后面
举例:尝试打印name变量,如果name变量未定义,则提示错误信息
try:
print(name)
except NameError as e: # 捕捉特定的 NameError 异常
print('name变量定义错误')
注意:
- 如果尝试执行的代码异常类型与要捕获的不一致,则无法捕获异常
- 一般try下方只放一行尝试执行的代码
8.2.3 捕获多个异常
基本语法:
try:
可能发生异常的代码
except 异常类型组:
出现异常时执行的代码
当需要捕获多个异常时,可以把要捕获的异常类型用元组形式书写在except后面
举例:
待补充。。。
注意:
- 未正确设置捕获异常类型,则无法捕获异常
8.2.4 捕获全部异常
基本语法格式:
try:
可能发生异常的代码
except exception:
出现异常时执行的代码
通常捕获全部异常使用捕获exception类型对象
另一种写法就是捕获常规异常的写法
8.2.4 异常的else语句
try…except…else
try/except 语句还有一个可选的 else 子句,如果使用这个子句,那么必须放在所有的 except 子句之后。
基本语法格式:
try:
可能发生异常的代码
except exception:
出现异常时执行的代码
else:
没有异常时要执行的代码
else 子句将在 try 子句没有发生任何异常的时候执行。
8.2.5 异常的finally语句
try…except…else…finally
表示有没有异常都要执行的语句
基本语法格式:
try:
可能发生异常的代码
except Exception:
出现异常时执行的代码
else:
没有异常时要执行的代码
finally:
有没有异常都要执行的代码
总结:
- 在异常中,else和finally都是可选的,可写可不写
- 捕获全部的异常可以用:except 或 except Exception
- 可以对异常改用别名,except [异常 as 别名]:
8.3 异常的传递
异常是具有传递性的
简单来说就是当你调用一个函数时,可以其嵌套调用的函数会发生异常,如果其没有对异常进行捕获,那么该异常就会传递到当前调用的函数上。
举例:
def func01():
print('这是func01开始')
num = 1 / 0 # 除数为0,引发异常
print('这是func01结束')
def func02():
print('这是func02开始')
func01() # 调用func01 函数
print('这是func02结束')
def main():
try:
func02() # 调用func02
except Exception:
print(Exception)
main()
在上述的例子中,当主函数中的func02调用到func01函数时,发生异常,func01函数没有对其进行捕获,异常就会传递到func02函数,func02函数也没有对这个异常进行捕获,该异常就会传递到main函数中,被main函数捕获。
注意:
- 当所有函数都没有捕获异常时,程序会报错
8.4 抛出异常
为什么还要手动设置异常呢?首先要分清楚程序发生异常和程序执行错误,它们完全是两码事,程序由于错误导致的运行异常,是需要程序员想办法解决的;但还有一些异常,是程序正常运行的结果,比如用 raise 手动引发的异常。
Python 使用 raise 语句抛出一个指定的异常
基本语法格式:raise [exceptionName [(reason)]]
[] 里的参数为可选参数,其作用是指定抛出的异常名称,以及异常信息的相关描述。
三种用法:
raise
:该语句引发当前上下文中捕获的异常(比如在 except 块中),或默认引发 RuntimeError 异常。raise 异常类名称
:表示引发执行类型的异常。raise 异常类名称(描述信息)
:表示在引发指定类型的异常的同时,附带异常的描述信息。
举例:
raise
默认引发 RuntimeError异常
try:
a = input("输入一个数:")
if(not a.isdigit()):
raise
except Exception as e:
print("引发异常:", repr(e))
输入一个数:a
引发异常: RuntimeError('No active exception to reraise')
注意:
- 需要使用repr()函数,才能看到异常名称类型,
- 如果使用format()函数则只能看到异常说明信息
raise
引发当前上下文中捕获的异常,即except 块中的异常
try:
a = input("输入一个数:")
if(not a.isdigit()):
raise ValueError("a 必须是数字")
except ValueError as e:
print("引发异常:", repr(e))
raise
输入一个数:a
引发异常: ValueError('a 必须是数字')
Traceback (most recent call last):
File "E:\project.py\Leetcode\August\sads.py", line 8, in <module>
raise ValueError("a 必须是数字")
ValueError: a 必须是数字
raise 异常类名称
和raise 异常类名称(描述信息)
:引发指定异常。
try:
a = int(input("输入一个数:"))
if a == 0:
raise ZeroDivisionError("除数不能为零")
except ZeroDivisionError as e:
print("引发异常:", repr(e))
输入一个数:0
引发异常: ZeroDivisionError('除数不能为零')
8.5 Python 模块
模块是 Python 程序架构的一个核心概念
- 每一个以扩展名 py 结尾的 Python 源代码文件都是一个 模块
- 模块名 同样也是一个 标识符,需要符合标识符的命名规则
- 在模块中定义的 全局变量 、函数、类 都是提供给外界直接使用的 工具
- 模块 就好比是 工具包,要想使用这个工具包中的工具,就需要先 导入 这个模块
8.5.1 模块的导入
模块在使用前需要先导入,模块的导入语法如下:
[from 模块名] import [模块 | 类 | 变量 | 方法 | *] [as 别名]
[]是可选的意思,表示里面的参数可以省略
常用的导入方式有:
- import 模块名
- import 模块名 as 别名
- from 模块名 import *
- from 模块名 import 类、变量、方法等
- from 模块名 import 类、变量、方法等 as 别名
注意
一个模块只会被导入一次,不管你执行了多少次 import。这样可以防止导入模块被一遍又一遍地执行。
使用 import 语句的时候,Python 解释器是怎样找到对应的文件的呢?
Python 的搜索路径,搜索路径是由一系列目录名组成的,Python 解释器就依次从这些目录中去寻找所引入的模块。
8.5.1.1 import 语句
基本语法:
import 模块名
import 模块名、模块名
模块名.功能名()
通过 import 导入模块后,就可以通过 模块名.功能名
来使用模块中任意内容
举例:导入time模块:
import time # 首先导入内置的time模块
print('开始')
time.sleep(3) # 必须以“模块名.对象名//模块名.方法名”的形式进行访问
print('结束')
注意:
- 当引用一个解释器未安装/定义的模块时,会引发ModuleError的错误
- 按住 Control键 + 鼠标左键点击,可进入该模块的文件
- 引入模块后,可以通过
模块名.
使用任意功能 - 模块的导入一般写在开头位置
- 模块的功能被使用后,导入语句会高亮,否则会置灰
8.5.1.2 from … import 语句
基本语法:
from 模块名 import 功能名
from 模块名 import 功能名, 功能名
功能名()
相比于直接import 直接导入整个模块
通过 from 导入模块,可以确切导入模块中的具体某个或者某几个功能。
举例:导入time模块中的sleep()方法:
from time import sleep() # 导入time模块中的sleep() 方法
print('开始')
sleep(1) # 直接使用对象“对象名//方法名”访问
print('结束')
相比于import导入,使用 from 这种方式仅导入明确指定的对象(函数),在使用该功能时,不需要在前面加入模块名
注意:
- 使用from导入模块,在使用导入的功能时,可以直接用
功能名()
- 仅可以使用导入的功能,不可以使用模块中未导入的其他功能
from math import sin,cos,tan # 可以一次从一个文件中导入多个函数
当然from 也可也把全部的功能导入进来,只需使用如下声明:from modname import *
这种导入与improt导入类似,只是在使用功能时,写法不一样使用
8.5.1.3 as 别名
基本语法:
# 给模块定义别名
import 模块名 as 别名
# 给功能定义别名
from 模块名 import 功能名 as 别名
作用:给部分过长的模块名/功能名 更改为简短的名称
举例:
import time as t # 将time模块更改名为 t
print('开始')
t.sleep(3) # 必须以“别名.对象名//别名.方法名”的形式进行访问
print('结束')
from time import sleep as sl # 将time模块中的sleep 方法更改为sl别名
print('开始')
sl(1)
print('结束')
注意:
- 使用as将模块名或者方法名更改别名后,无法再使用原模块名或方法名,其属于未定义内容
- 模块的导入一般写在开头位置
8.5.2 自定义模块
- 每个py文件都可以当作一个模块,模块的名字即是文件的名字
- 自定义模块名必须符合标识符的命名规则
8.5.2.1 定义模块
有些时候需要自定义一些模块。
新建模块的方法:
举例:新建一个Python文件,命名为my_module.py
,并定义test函数:
my_module.py
:
def test(a,b):
print(a+b)
使用 import 导入my_module
模块,调用 my_module.py
中的 test 方法
import my_module
my_module.test(10,20)
8.5.2.2 调试模块
__name__属性:
每个模块都有一个__name__属性,当其值是’main’时,表明该模块自身在运行,否则是被引入
这一属性很重点,方便调试,一般我们在单个文件中调试函数是如下:
def test(a,b):
print(a+b)
test()
但是上面这样的写法,当文件被导入其他文件时,就会调用test()
def test(a,b):
print(a+b)
if __name__ == '__main__':
print('程序自身在运行')
test()
else:
print('我来自另一模块')
使用__name__属性,这样可以让文件程序自身运行时执行相应的调试功能test()
,非自身运行时,不执行调用test()
# 模块1代码
def my_test(a, b):
print(a + b)
# 模块2代码
def my_test(a, b):
print(a - b)
# 导入模块和调用功能代码
from my_module1 import my_test
from my_module2 import my_test
# my_test函数是模块2中的函数
my_test(1, 1)
注意:
- 一个模块如果是在该模块自身运行: _ name _ == ‘_ main _’
- 一个模块被另一个程序引入时:_ name _ != ‘_ main _’
- 如果使用from … import …或from … import *导入多个模块的时候,且模块内有同名功能。当调用这个同名功能的时候,调用到的是后面导入的模块的功能。
__all__属性
==可以控制import *
能够导入的内容==
如果存在一个叫做 all 的列表变量,那么在使用 from ,module import * 的时候就把这个列表中的所有名字作为模块方法导入。
举例:有个模块my_module.py
的文件内容如下:
__all__ = ["testA"]
def testA():
print('testA')
def testB():
print('testB')
在另一个文件中,使用import *
导入
from my_module import *
testA() #只能导入testA函数
testB() # testB函数报红,说明没有这个函数
因为在_ all _ 的列表中,没有 testB 这个函数,所以上面引用 testB 函数失败
8.5.3模块的查找路径
当导入一个模块,Python解析器对模块位置的搜索顺序是:
- 当前目录
- 如果不在当前目录,Python则搜索在shell变量
PYTHONPATH
下的每个目录。 - 如果都找不到,Python会察看默认路径。UNIX下,默认路径一般为/usr/local/lib/python/
模块搜索路径存储在system模块的sys.path变量中。变量里包含当前目录,PYTHONPATH和由安装过程决定的默认目录。
注意:
- 自己的文件名不要和已有模块名重复,否则导致模块功能无法使用
- 使用
from 模块名 import 功能
的时候,如果功能名字重复,调用到的是最后定义或导入的功能。
8.6 包
8.6.1 包的概念
将有联系的模块组织在一起,即放到同一个文件夹下,并且在这个文件夹创建一个名字为__init__.py 文件,那么这个文件夹就称之为包。
- 包 是一个 包含多个有联系的模块组织在一起 的 特殊目录(文件夹)
- 目录下有一个 特殊的文件__init__.py
- 包名的 命名方式 和变量名一致,小写字母 + _
作用:使用 import 包名
可以一次性导入 包 中 所有的模块
init.py 文件的作用是将文件夹变为一个Python模块,我们在导入一个包时,
实际上是导入了它的__init__.py文件,这样我们可以在__init__.py文件中批量导入我们所需要的模块,
而不再需要一个一个的导入。
8.6.2 创建包
一般操作[New] — [Python Package] — 输入包名 — [OK] — 新建功能模块(有联系的模块)。
注意:新建包后,包内部会自动创建__init__.py文件,这个文件控制着包的导入行为。
示例
新建包mypackage
新建包内模块:my_module1.py
和 my_module2.py
:
my_module1.py
:
print(1)
def info_print1():
print('my_module1')
my_module2.py
:
print(2)
def info_print2():
print('my_module2')
8.6.3 导入包
模块和包的导入方法基本一致,只是需要写清楚这个模块来源哪个包
import 包名.模块名
基本语法:
# 导入
import 包名.模块名
# 调用功能
包名.模块名.功能名()
举例:
# 导入my_package.my_module1
import my_package.my_module1
# 调用
my_package.my_module1.info_print1()
from 包名.模块名 import 功能名
基本语法:
# 导入
from 包名.模块名 import 功能名
# 调用功能
功能名()
举例:
# 从 my_package.my_module1 里导入 info_print1 函数
from my_package.my_module1 import info_print1
# 调用
info_print1()
import 语法中的item总结:
import item.subitem.subsubitem 这种导入形式,除了最后一项,都必须是包,而最后一项则可以是模块或者是包,但是不可以是类,函数或者变量的名字。
import 语法会首先把 item 当作一个包定义的名称,如果没找到,再试图按照一个模块去导入。如果还没找到,抛出一个 :exc:ImportError 异常。
from package import item
这种形式,对应的 item 既可以是包里面的子模块(子包),或者包里面定义的其他名称,比如函数,类或者变量。
as 别名
跟前面module的用法一致
_ all _属性:
导入语句遵循如下规则:如果包定义文件 _ init .py 存在一个叫做 _ all _ 的列表变量,那么在使用 from package import *
的时候就把这个列表中的所有名字作为包内容导入。
注意: all _属性仅能控制from package import *
导入的
8.6.4 导入第三方包
对于一个第三方的包,如果我们想使用其中的功能的话,那么我门首先需要导入这个包
例如:导入requests包:
如果使用pycharm,可以在设置-解释器环境里面安装包。
在命令提示符中,可以使用 pip 工具来安装
pip安装包
基本用法:pip install packagename
pip的网络优化
由于pip时连接的国外的网站进行包的下载,所以有时候会下载失败或者速度很慢
优化做法,让其连接国内的网站进行包的安装:
基本语法:pip install packagename -i 网址 --trusted-host 网址的顶级域名
参数:
- packagename:需要安装的包名
- -i :表示切换,后接需要更改的连接网址
- –trusted-host :表示信任,后接前面连接网址的顶级域名
举例:
安装:pip install packagename -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com
卸载:pip uninstall packagename -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com
常用的国内镜像源:
中国科学技术大学 : https://pypi.mirrors.ustc.edu.cn/simple
豆瓣:http://pypi.douban.com/simple/
阿里云:http://mirrors.aliyun.com/pypi/simple/
清华大学:https://pypi.tuna.tsinghua.edu.cn/simple
8.6.5 包的访问路径
相对路径访问
package/
– – init.py
– – subpackage1/
– – – – init.py
– – – – moduleX.py
– – – – moduleY.py
– – – – – – def spam()
– – subpackage2/
– – – – init.py
– – – – moduleZ.py
– – ---- – – def eggs()
– – moduleA.py
– – – – def foo()
假设当前文件是moduleX.py
或 subpackage1 / __ init__.py
,则以下是新语法的正确用法:(.py文件不是模块)
同级文件目录下引用
引用moduleY.py
文件中的方法: import moduleY
不同级文件目录下引用
引用 import subpackage2// from subpackage2 import py文件
中的方法
# .表示当前目录,对于`moduleX.py`文件,当前目录路径为:package/subpackage1/
from .moduleY import spam
from .moduleY import spam as ham
from . import moduleY
# ..表示上一级目录,对于`moduleX.py`文件,当前目录路径为:package/
from ..subpackage1 import moduleY
from ..subpackage2.moduleZ import eggs
from ..moduleA import foo
# ...表示上上一级目录
from ...package import bar
from ...sys import path
父目录调用子目录
例如 moduleA.py
调用subpackage2中的 moduleZ.py
文件
父目录调用子目录,可以使用以下两种方式
# 方式一
from subpackage2 import moduleZ
# 方式二
from subpackage2.moduleZ import eggs
from subpackage2.moduleZ import *
推荐常用方式二
同级目录下文件的调用
例如moduleX.py件调用subpackage2中的moduleZ.py文件
import sys
sys.path.append('../B') sys.path.append(os.path.split(os.getcwd())[0])
from B.B1 import *
9. 面向对象
面向对象编程(Object-oriented Programming,简称OOP)是一种编程范例,它提供了一种结构化程序的方法,以便将属性和行为捆绑到单个对象中。面向对象编程OOP将现实世界的实体建模为软件对象,以及与之相关的数据,并可以执行某些功能。
对象是面向对象编程范例的核心,不仅在函数编程中表示数据,而且在程序的整体结构中也是如此
三大特性:
封装、继承、多态
封装 :根据 职责 将 属性 和 方法 封装 到一个抽象的 类 中
封装的意义:
将属性和方法放到一起做为一个整体,然后通过实例化对象来处理;
隐藏内部实现细节,只需要和对象及其属性和方法交互就可以了;
对类的属性和方法增加 访问权限控制。
9.1 类和对象
9.1.1 类的定义
类:用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法
类就是封装
类大体结构为:
- 类:
-
- 类属性
-
- 实例属性
-
- 类方法
-
- 实例方法
在Python中,定义类是通过class关键字来实现的
class ClassName:
<statement-1>
.
.
.
<statement-N>
在Python中定义类很简单,使用关键字class既可,后面接上类的名称(需要使用大驼峰命名)
在Python 3中,定义新类时,Python 3隐式使用object作为父类;在python2.X中,需要主动指定使用
# Python 2.x类定义:
class ClassName(object):
<statement-1>
.
.
.
<statement-N>
(object)表示该类对象继承自Python内置的类对象object,Python中所有的类对象都继承自一个统一的基类:object
9.1.2 对象的定义
对象:python中一切皆对象。 上述的类其实也是一个对象object.
语法解析,当解释器执行class语句时,就会创建一个类对象,类的数据类型是type类型
# 定义一个类对象
class Dog:
pass
注意:
对象包括两个数据成员(类变量和实例变量)和 类方法
9.2 实例化对象
将抽象对象转变为实体化对象,就是实例化对象
实例化一个类是通过在类名后面加小括号“()”来实现
类的实例化就是创建一个类的实例,类的具体对象
根据类对象创建实例对象基本语法: 变量名 = 类名([实参])
# 定义类对象
class Dog:
name = '大黄'
# 类对象
dog1 = Dog
# 实例对象
dog2 = dog1()
注意:python的类,带括号是实例化,不带括号是赋值
具体需要了解一个_ new _ 方法
9.3 类属性
类属性通常用于保存与类相关的常量或配置信息,类属性是属于类的。
类属性:定义在类里面,类方法外面的变量就是类属性
类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用
9.3.1 定义类属性
属性的定义方法跟变量的定义方法类似,保持一致的命名规则
如下是定义一个空属性name
:
class Dog:
name = None # 名字
9.3.2 访问类属性
访问公有类属性可以分为:类外部访问 和 类内部访问
9.3.2.1 类外部访问
类外部访问可以通过两种方式访问类属性:
- 创建一个类之后,可以通过类名访问其属性
- 类实例化后,可以通过实例名访问其属性
属性引用使用和 Python 中所有的属性引用一样的标准语法:obj.name。
注意:推荐类属性使用 类名.name 方式操作访问
当某个实例调用类属性时,它们访问的是类属性的副本,这意味着当类属性的值发生改变时,所有实例都会受到影响
通过类名访问其属性
调用方法:类名.name
如下:
#类定义
class People:
# 定义基本属性
name = '小美'
age = 18
# 直接通过类名访问其属性
print("我叫:%s,今年:%s岁" % (People.name, People.age))
输出结果:
我叫:小美,今年:18岁
通过实例名访问其属性
调用方法:实例名.name
class People:
# 定义基本属性
name = '小美'
age = 18
a = People()
print("我叫:%s,今年:%s岁" % (a.name, a.age))
输出结果:
我叫:小美,今年:18岁
9.3.2.1 类内部访问
类内部访问也分为类方法访问和实例方法访问
类方法访问类属性
类方法访问类属性需要使用cls关键字
cls 关键字
cls 关键字是类方法定义的时候,必须要填写的参数
- 它用来表示类自身的意思
- 当我们使用类对象调用方法的时候,cls 会被python自动传入
- 在类方法的内部,想要访问类的类变量,必须使用cls
- cls 在传参的过程时候可以忽略
class People:
# 定义基本属性
name = '小美'
age = 18
@classmethod
def adc(cls):
print("我叫:%s,今年:%s岁" % (name, age)) # 报错,类内部使用应该用self来绑定如:cls.name, cls.age
实例方法访问类属性
实例方法访问类属性需要使用self关键字
self 关键字
self关键字是实例方法定义的时候,必须要填写的参数
- 它用来表示实例自身的意思
- 当我们使用类对象调用方法的时候,self会被python自动传入
- 在实例方法的内部,想要访问类的类变量,必须使用self
- self在传参的过程时候可以忽略
举例:
class People:
# 定义基本属性
name = '小美'
age = 18
def adc(self):
print("我叫:%s,今年:%s岁" % (name, age)) # 报错,类内部使用应该用self来绑定如:self.name, self.age
9.3.3 修改类属性
修改一个类属性的值有两种方法
- 对象名.属性名 = 数据 ----> 直接修改
- 对象名.方法名() ----> 间接修改
对象名.属性名 = 数据 ----> 直接修改
class People:
# 定义基本类属性
name = '小美'
age = 18
#定义两个实例化对象
a=People()
b = People()
# 通过类名.变量名来修改基本属性
People.name = '小帅'
People.age = 24
print("我叫:%s,今年:%s岁" % (People.name, People.age))
# 通过实例名.变量名来修改基本属性
a.name = "小梅"
a.age = 25
print("我叫:%s,今年:%s岁" % (a.name, a.age))
# 检查没有主动修改的b实例对象中类属性值有无改变
print("我叫:%s,今年:%s岁" % (b.name, b.age))
输出结果:
我叫:小帅,今年:24岁
我叫:小梅,今年:25岁
我叫:小帅,今年:24岁
详解:
类对象名.变量名
访问的方式可以更改了类属性,从上述中实例b的输出可知实例对象名.变量 = 数据
默认是给实例对象添加实例属性,并不能操作类属性
注意:
- 类属性只能通过类对象修改,不能通过实例对象修改
- 如果类属性和实例属性同名,实例对象名只能操作实例属性
对象名.方法名() ----> 间接修改
需要先了解方法的知识才比较好懂这个内容
class Boy:
name = 'silver' # 私有变量
def chName(self,name): # 定义一个方法,使得在外部可以调用
self.name = name
b = Boy()
b.chName('逍遥')
print(b.name) 输出结果:'逍遥'
通过调用方法,来修改属性
9.4 类方法
在类中可以定义函数用来记录行为
类中定义的行为(函数)叫做类方法,也叫做成员方法
9.4.1 定义类方法
类方法需要用修饰器@classmethod来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以cls作为第一个参数
class A(object):
name = 'I am Class A'
@classmethod
def class_method(cls, s):
print("调用类方法")
a = A()
A.class_method()
a.class_method()
9.4.2 访问类方法
有两种访问方式:
- 使用类直接调用
- 使用实例调用
class A(object):
name = 'I am Class A'
@classmethod
def class_method(cls, s):
print("调用类方法")
a = A()
A.class_method()
a.class_method()
能够通过实例对象和类对象去访问。类方法通常使用类直接调用,也可以用实例调用(不推荐)。当实例调用的时候,Python会将实例的最底层类(即实例直接所属类)型传入cls参数中
注意:类方法是无法调用实例属性的
举例子:
class A(object):
def __init__(self):
self.age = 18
@classmethod
def class_method(cls):
print("调用实例属性%s "%(cls.age)) # 出错,类无age属性
a = A()
A.class_method()
a.class_method()
运行结果:
print("调用实例属性%s " % (cls.age)) # 出错,类无age属性
AttributeError: type object 'A' has no attribute 'age'
类方法没有实例属性,无法调用该属性
9.5 实例属性
实例属性是从属于实例对象的属性,也称为“实例变量”,其跟类变量不一样
类体中,所有函数内部:以self.变量名
的方式定义的变量,称为实例属性或实例变量
实例变量就是一个用 self
修饰的变量
9.5.1 定义实例属性
定义实例属性需要使用我们的魔法构造方法:_ init _
_ init _() 方法也称之为构造方法:
- 在创建类对象的时候,会自动执行_ init _() 方法
- 在创建类对象的时候,将传入参数传递给 _ init _ 方法使用
_ init _() 方法无需主动调用,在创建新的类对象时会自动调用,其可以传入参数也可也不传入参数
9.5.1.1 不传参数方式定义
举例:
class Lange:
# 魔法构造方法
def __init__(self):
self.name = "中文"
self.address = "http://"
# 下面定义了一个say实例方法
def say(self):
self.age = 13
clang = Lange()
print(clang.name)
print(clang.address)
print(clang.age)
输出结果:
"中文"
"http://"
报错:AttributeError: 'Lange' object has no attribute 'age'
此 Lange
类中,name
、address
以及 age
都是实例变量。但是Lange
类的实例对象都会包含 name
和 add
实例变量,而只有调用了 say()
方法的实例对象,才包含 age
实例变量
9.5.1.2 传参方式定义
class Student:
# 魔法构造方法
def __init__(self, name):
self.name = name # 此时实例变量name等于外部传入的参数name
s = Student('小红')
print(s.name)
输出结果:
小红
此例中,此时实例变量name等于外部传入的参数name。这使得类具有很好的扩展性
9.5.2 访问实例属性
在类内部访问
使用:self.name
例如:
class Student:
# 魔法构造方法
def __init__(self):
self.name = "中文"
# 下面定义了一个say1实例方法
def say1(self):
print("我的名字是:%s" %self.name)
# 下面定义了一个say2实例方法
def say2(self):
print("我的名字是:%s" %name)
s = Student()
print(s.say1())
print(s.say2())
输出结果:
我的名字是:中文
NameError: name 'name' is not define
在上面的例子中,在类方法中,调用实例变量需要使用self关键字
结合之前的学习,在类方法中,调用类变量也需要使用self关键字
在类外部调用
使用:实例对象名.name
class Student:
# 魔法构造方法
def __init__(self):
self.name = '小红'
s = Student()
print(s.name) #使用实例对象名.name调用
print(Student.name) #使用类对象名.name调用
输出结果:
小红
AttributeError: type object 'Student' has no attribute 'name'
在上述例子中,调用实例变量只能使用 实例对象名.name 调用
总结:
在类方法中,不管调用实例变量还是类变量,都需要使用self关键字
在类外部,调用实例变量只能使用 实例对象名.name 调用
9.5.3 添加实例属性
可以在对象实例化后,再给对象添加实例属性
举例:
class Student:
# 魔法构造方法
def __init__(self):
self.name = '小红'
s = Student()
s.age = 18 # 新增一个实例属性,age
在前面类变量中提到通过实例对象可以访问类变量,但无法修改类变量的值。这是因为,通过实例对象修改类变量的值,不是在给“类变量赋值”,而是定义新的实例变量
实例变量只能通过对象名访问,无法通过类名访问。
9.6 实例方法
9.6.1 实例方法定义
在类的内部,使用 def 关键字来定义一个方法,与一般函数定义不同,类方法必须包含参数 self, 且为第一个参数,self 代表的是类的实例
定义语法
def 方法名(self, 形参1, ....., 形参n):
方法体
9.6.2 调用实例方法
在类内部调用实例方法时,需要使用self关键字
但是在类外部调用实例方法时,无需传入self参数
如:
class Student:
name = '小蓝'
# 定义实例方法,并且使用self关键字
def say_1(self):
# 在方法的内部,想要访问类的类变量,必须使用self
print("hello 大家好,我叫做%s"%(self.name))
# 定义实例方法,并且使用self关键字,外部传入参数
def say_2(self,msg):
print("时间过得真快,%s"%(msg))
# 内部调用实例方法
def say_3(self):
print("我是方法3")
self.say_1()
stu = Student()
stu.say_1() # 类外部调用实例方法
stu.say_2("我已经15岁了") # 类外部调用实例方法
stu.say_3()
输出结果:
hello 大家好,我叫做小蓝
时间过得真快,我已经15岁了
hello 大家好,我叫做小蓝
总结:
- self 表示类对象本身的意思
- 在方法的内部,想要访问类的类变量,必须使用self
- self 虽然出现在形参列表中,但是无需理会
9.7 私有属性
对于部分属性和行为不希望对外公开或者开放使用。所以有了私有属性这个概念
9.7.1 定义私有属性
__private_attrs:两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问,在类内部的方法中使用时 self.__private_attrs
私有属性也可也分为私有类属性和私有实例属性
举例:
class Boy:
__name = 'silver' # 私有类属性
def __init__(self):
__age = 18 # 私有实例属性
9.7.2 访问私有属性
9.7.2.1 类内部访问
私有属性,可以在类内部通过self或者cls调用
class Boy:
__name = 'silver' # 私有变量
def __init__(self):
self.__age = 18 # 私有实例属性
def get1(self): # 类内部,实例方法调用
print("实例方法调用:%s %s"%(self.__name, self.age))
@classmethod
def get2(cls): # 类内部,类方法调用
print("类方法调用:%s %s"%(cls.__name))
总结:
无论是公有还是私有属性
在类内部,实例方法调用需要 self 调用类或者实例属性
在类内部,类方法调用需要 cls 调用类属性, 但是无法调用实例属性
9.7.2.2 类外部访问
私有属性不能在类的外部被使用或直接访问,通用方法:定义一个可以调用的公有方法
class Boy:
__name = 'silver' # 私有变量
def getName(self):
print(self.__name)
a = Boy()
a.getName()
通过定义公有方法,使得私有属性可以在外部调用
9.8 私有方法
__private_method:两个下划线开头,声明该方法为私有方法,只能在类的内部调用 ,不能在类的外部调用。self.__private_methods。
9.8.1 定义私有方法
私有方法也可也分为私有类方法和私有实例方法
举例:
class A(object):
__name = '小明'
def __init__(self):
self.__age = 18
@classmethod
def __class_method(cls): # 定义私有类方法
print("调用私有类属性%s " % (cls.__name))
def __self_method(self): # 定义私有实例方法
print("调用私有实例属性%s " % (self.__age))
9.8.2 访问私有方法
9.8.2.1 类内部访问
私有方法,可以在类内部通过self或者cls调用
class A(object):
__name = '小明'
def __init__(self):
self.__age = 18
@classmethod
def __class_method(cls): # 定义私有类方法
print("调用私有类属性%s " % (cls.__name))
def __self_method(self): # 定义私有实例方法
print("调用私有实例属性%s " % (self.__age))
@classmethod
def get1(cls): #
print("调用私有类方法 ")
cls.__class_method()
def get2(self): #
print("调用私有实例方法 ")
self.__self_method()
9.8.2.2 类外部访问
私有属性不能在类的外部被使用或直接访问,通用方法:定义一个可以调用的公有方法
class A(object):
__name = '小明'
def __init__(self):
self.__age = 18
@classmethod
def __class_method(cls): # 定义私有类方法
print("调用私有类属性%s " % (cls.__name))
def __self_method(self): # 定义私有实例方法
print("调用私有实例属性%s " % (self.__age))
@classmethod
def get1(cls): #
print("调用私有类方法 ")
cls.__class_method()
def get2(self): #
print("调用私有实例方法 ")
self.__self_method()
a = A()
a.get1()
a.get2()
运行结果:
调用私有类方法
调用私有类属性小明
调用私有实例方法
调用私有实例属性18
注意:
- 类的属性和方法,在对象实例化时,会被实例化为实例方法,所以cls类属性和类方法,可以用 self 来访问,但是实例属性和实例方法,不能被 cls访问
9.8.3 属性和方法总结
调用属性总结:
- 类内部:
-
- 实例方法中需要
self.name
调用类属性或者实例属性
- 实例方法中需要
-
- 类方法中需要
cls.name
调用类属性
- 类方法中需要
- 类外部:
-
- 推荐用
类名.属性名
调用类属性
- 推荐用
-
- 推荐用
对象名.属性名
调用实例属性,虽然也可调用类属性,但不推荐
- 推荐用
-
- 只能通过调用公有方法来间接调用私有属性
调用方法总结:
- 类内部:
-
- 实例方法中需要
self.method()
调用类方法或者实例方法
- 实例方法中需要
-
- 类方法中需要
cls.method()
调用类方法
- 类方法中需要
- 类外部:
-
- 推荐用
类名.方法名()
调用类方法
- 推荐用
-
- 推荐用
对象名.方法名()
调用实例方法,虽然也可调用类方法,但不推荐
- 推荐用
-
- 只能通过调用公有方法来间接调用私有方法
私有属性和方法总结:
- 命名:都是以双下划线__作为开头
- 访问限制:无法通过类对象直接访问 ,本类中内部可以访问;
- 继承:都不会被子类继承,子类也无法访问
- 作用:通常用来处理类的内部事情,不通过对象处理,起到安全作用。
9.9 内置魔法方法
除了前面使用到的_ init _ 构造方法,python中还存在一些内置的类方法,各自有各自的功能,统称为魔法方法。
魔法方法:
- _ init _ 构造方法,在生成对象时自动调用
- _ new _ 生成方法,在生成对象时调用
- _ str _ 字符串方法
- _ del _ 析构方法
- _ call _ 当对象被当作函数调用时,自动调用
- _ eq _ == 符号比较方法
9.9.1 _ new _ 生成对象方法
__new__魔术方法返回的就是self的内存地址
- 如果不在__new__方法里面调object的__new__方法就不会创建对象,__init__不会被执行;
- 如果不在__new__方法里面return创建好的对象,__init__不会被执行
class Test:
def __new__(cls, *args, **kwargs):
print("我是__new__方法")
obj = object.__new__(cls)
print(obj)
return obj
def __init__(self):
print(self)
print("我是__init__方法")
a = Test()
运行结果:
我是__new__方法
<__main__.Test object at 0x123902f70>
<__main__.Test object at 0x123902f70>
我是__init__方法
对象初始化时执行__new__,目的是为该对象分配内存空间。
对象初始化时一般先执行__new__,再执行__init__,如果缺少__new__返回的对象,那么将不会执行__init__
9.9.2 _ str _ 字符串方法
__str__方法用于返回对象的描述信息:
- 如果不使用__str__方法,直接print,或者return,返回的是对象的内存地址。
- 如果在__str__中定义了描述信息,print或者return时,返回的就不是内存地址,显示更友好,实现了类到字符串的转化。
举例:
不使用__str__方法
class Student:
def __init__(self,name,age):
self.name = name
self.age = age
stu = Student('小花',20)
print(stu) # 输出类对象地址
print(str(stu)) # 将类对象转化为字符串
运行结果:
<__main__.Student object at 0x000001CD8B6ED340>
<__main__.Student object at 0x000001CD8B6ED340>
当类对象需要被转换为字符串时,会输出内存地址。
使用__str__方法
可以自定义方法内容,通过__str__方法来控制类转化为字符串的行为
class Student:
def __init__(self,name,age):
self.name = name
self.age = age
def __str__(self):
return f"name={self.name},age={self.age}"
stu = Student('小花',20)
print(stu)
print(str(stu))
运行结果:
name=小花,age=20
name=小花,age=20
_ str _ 字符串方法 相当于是对 str 运算符重载
还有一个魔术方法__repr__,与__str__类似,当同时出现时,str__优先级高于__repr
9.9.3 _ del _ 析构方法
对象被删除的时候调用此方法:
- 对象在内存中被释放时,自动触发执行;
- 此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以__del__()的调用是由解释器在进行垃圾回收时自动触发执行的
class Test:
def __init__(self):
print("初始化对象")
def __del__(self):
print("对象被删除了")
a = Test()
print("end")
运行结果:
初始化对象
对象被删除了
9.9.4 _ call _ 方法
默认自动调用,把类实例当成函数执行的时候会触发__call__方法,对于 call 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
举例:
class Test:
def __call__(self, *args, **kwargs):
print("调用了__call__")
a = Test()
a()
运行结果:
调用了__call__
9.9.5 _ eq _ == 符号比较方法
调用相等判断的时候自动调用
class Test:
def __init__(self, name, age):
self.name = name
self.age = age
def __eq__(self, other):
print(self.age)
print(other.age)
return self.age == other.age #判断当前的跟其他的
t1 = Test("狗子", 22)
t2 = Test("小李", 23)
print(t1 == t2)
运行结果:
22
23
False
当调用了 == 符号时,就会去自动调用__eq__ 方法
9.10 继承
继承: 实现代码的重用,相同的代码不需要重复的编写
作用:继承就是可以获取另外一个类的静态属性或者普通方法,(并非所有成员)。让类和类之间产生父子关系,子类可以拥有父类的静态属性和方法。
9.10.1 继承的基础语法
在Python中,新建的类可以继承一个或者多个父类,其中:
- 父类又可以称为基类或者超类,新建的类称为派生类或子类
- 子类 继承自 父类,可以直接 使用 父类中已经封装好的公有方法,不需要再次开发
- 子类 封装 子类特有的 属性和方法
继承又可以分为:单继承和多继承
继承的基本语法:
单继承
class 类名(父类名):
pass
多继承
class 类名(父类名1, 父类名2):
pass
9.10.2 单继承
子类继承一个父类,不仅可以调用子类定义方法,也调用父类中的方法
单继承举例:
# 定义一个类使用4g通话,再定义一个类使用5g通话
class iphone: # 定义一个父类iphone
name = '苹果手机'
def call_by_4g(self):
print("4g通话")
# 定义一个子类iPhone8 继承至父类
class iphone8(iphone):
face_id = '100001'
def call_by_5g(self):
print("新功能,5g通话")
phone = iphone8()
print(phone.name) # 打印父类的name属性
phone.call_by_4g() # 调用父类的方法
phone.call_by_5g() # 调用子类方法
继承具有传递性:
如果C 类从 B 类继承,B 类又从 A 类继承,那么 C 类就具有 B 类和 A 类的所有属性和方法
9.10.2 多继承
子类继承多个父类,不仅可以调用子类定义方法,也调用父类中的方法
举例:
class NFCReader:
nfc_type = '第三代'
producer = "MM"
def read_card(self):
print("nfc读卡")
def write_card(self):
print("nfc写卡")
class RemoteControl(self):
rc_type = "第二代"
def control(self):
print("红外遥控")
class MYphone(NFCReader, RemoteControl):
pass
phone = MYphone()
phone.read_card() # 调用父类的读卡功能
phone.control() # 调用父类的红外遥控功能
MYphone 这个类继承了 NFCReader 和 RemoteControl 两个父类,所以它具有两个类所拥有的全部功能
父类之间 存在 同名的属性或者方法:
继承多个父类,如果有同名的成员(属性和方法),那么默认以(从左到右的)继承顺序为优先级
class NFCReader:
nfc_type = '第三代'
producer = "MM"
def read_card(self):
print("nfc读卡")
def write_card(self):
print("nfc写卡")
class RemoteControl(self):
producer = "NN"
rc_type = "第二代"
def control(self):
print("红外遥控")
class MYphone(NFCReader, RemoteControl):
pass
phone = MYphone()
phone.producer() # 输出结果为:"MM"
注意:开发时,应该尽量避免这种容易产生混淆的情况!
—— 如果 父类之间 存在 同名的属性或者方法,应该 尽量避免 使用多继承
Python 中的 MRO —— 方法搜索顺序
Python 中针对 类 提供了一个 内置属性 mro 可以查看 方法 搜索顺序
MRO 是 method resolution order,主要用于 在多继承时判断 方法、属性 的调用 路径
在搜索方法时,是按照 mro 的输出结果 从左至右 的顺序查找的:
- 如果在当前类中 找到方法,就直接执行,不再搜索
- 如果没有找到,就继续查找下一个类中是否有对应的方法,如果找到,就直接执行,不再搜索
- 如果找到最后一个类,还没有找到方法,程序报错
9.10.3 方法重写
子类继承父类的方法和属性后,可以进行复写。
即:再子类中重写定义同名的属性或方法
class iphone: # 定义一个父类iphone
name = '苹果手机'
def call_by(self):
print("使用4g通话")
class Myphone(iphone):
name = '这是我的苹果手机' # 复写父类属性
def call_by(self):
print("新功能,可以使用5g通话") # 复写父类方法
myphone = Myphone()
print(myphone.name) # 输出, '这是我的苹果手机'
myphone.call_by() # 输出, '新功能,可以使用5g通话'
9.10.4 调用父类同名成员
一旦复写父类成员,那么类对象调用成员的时候,就会调用复写后的新成员
如果在类内部需要使用被复写的父类成员,需要使用以下两种调用方式:
- 使用类名调用父类成员, 具体为: 父类名.name \ 父类名.method
- 使用super()调用父类成员,具体为:super().name \ super().method
使用类名调用父类成员
举例:
class iphone: # 定义一个父类iphone
name = '苹果手机'
def call_by(self):
print("使用4g通话")
class Myphone(iphone):
name = '这是我的苹果手机' # 复写父类属性
def call_by(self):
print("新功能,可以使用5g通话") # 复写父类方法
def call_by_parent(self):
print("调用父类的属性和方法,")
print(iphone.name)
iphone.call_by(self)
myphone = Myphone()
myphone.call_by_parent()
运行结果:
调用父类的属性和方法,
苹果手机
使用4g通话
注意:
- 需要注意再使用类名.method() 调用父类方法时,需要传入参 数self
- 不推荐使用这种方法调用,因为一旦 父类发生变化,方法调用位置的 类名 同样需要修改
使用super()调用父类成员
举例:
class iphone: # 定义一个父类iphone
name = '苹果手机'
def call_by(self):
print("使用4g通话")
class Myphone(iphone):
name = '这是我的苹果手机' # 复写父类属性
def call_by(self):
print("新功能,可以使用5g通话") # 复写父类方法
def call_by_parent(self):
print("调用父类的属性和方法,")
print(super().name)
super().call_by()
myphone = Myphone()
myphone.call_by_parent()
运行结果同上,
super类
在 Python 中 super 是一个 特殊的类,super() 就是使用 super 类创建出来的对象
使用的场景:就是在 重写父类方法时,调用 在父类中封装的方法实现
注意:
- 使用super.method() 调用父类方法时,不需要传入参 数self
- 只可以在子类内部调用给你父类的同名成员,子类的实例对象默认都是调用子类复写后的
- 子类对象不能在自己的方法内部,直接访问父类的 私有属性 或 私有方法,可以通过父类的公有方法间接访问
9.11 类型注解
python在3.5版本之后引入了类型注解,以方便静态类型检查工具、IDE等第三方工具进行类型检查,起提示的作用,对 Python 程序的运行不会产生任何影响
类型注解:在代码中涉及数据交互的地方,提供数据类型的注解
主要功能:
- 帮助第三方IDE工具(如Pycharm)对代码进行类型检查,做代码提示
- 帮助开发者对变量进行类型注解
主要分为:变量类型注解、函数类型注解、容器类型注解
提示:按快捷键 ctrl + p
可以查看提示
9.11.1 变量类型注解
Python 是动态语言,其显著特点是在声明变量时,你不需要显式声明它的类型,不用纠结类型声明、类型转化等麻烦事
举例:
age = 20
print(age+1)
上面例子中虽然代码里没有明确指定 age 的类型,但是程序运行时隐式推断出它是 int 类型,因此可以顺利执行 age + 1 的动作。但是:如果变量的类型有错,编辑器、IDE等工具无法在早期替你纠错,只能在程序运行阶段才能够暴露问题。
所以,引入了类型注解方法,来明确的声明变量的类型。
基础语法:变量名: 数据类型 = 值
如:
var_1: int = 18
var_2: float = 3.5
var_3: str = 'abc'
var_4: bool = False
var_5: list = []
扩展-- 类对象类型注解
class Student:
pass
stu: Student = Student()
语法是:对象名: 抽象类名 = 抽象类名()
9.11.2 容器类型注解
列表、字典、元组等包含元素的复合类型,用简单的 list,dict,tuple 不能够明确说明内部元素的具体类型。
因此要用到 typing 模块提供的复合注解功能
from typing import List, Dict, Tuple
my_list: List[int] = [1,2,3]
my_tuple: Tuple(str,int,bool) = ("yys", 889, True)
my_set: Set[int] = {1,2,3}
my_dict: Dict[str, int] = {"yys": 889}
在Python 3.9+ 版本后,内置的容器类型就支持了复合注解:
my_list: list[int] = [1,2,3]
my_tuple: tuple(str,int,bool) = ("yys", 889, True)
my_set: set[int] = {1,2,3}
my_dict: dict[str, int] = {"yys": 889}
注意:
- 元组类型设置类型注解时,需要将每个元素都标记出来
- 字典类型设置类型注解时,需要2个类型,第一个是key,第二个是value
9.11.3 函数类型注解
函数和方法的形参及返回值都可以进行类型注解
函数和方法的形参类型注解
基本语法:
def 函数方法名(形参名: 类型, 形参名: 类型,......):
pass
举例:
def add(x: int, y: int):
return x + y
当调用add函数时,按下ctrl+p可以查看提示x和y的变量类型
函数和方法的返回值类型注解
基本语法:
def 函数方法名(形参名: 类型, 形参名: 类型,......) -> 返回值类型:
pass
举例:
def add(x: int, y: int) -> int:
return x + y
print(add(1,2))
注意:返回值类型注解需要使用符号:->
9.11.4 Union类型注解
当列表或者字典等数据类型中存储的数据类型为混合类型时,就需要使用Union进行联合类型注解
语法:Union[类型1, 类型2......]
from typing import Union
# 使用Union对列表内的数据类型,进行联合类型注解
my_list: list[Union[str, int]] = [1,2,'yys']
# 使用Union对字典内的数据类型,进行联合类型注解
my_tuple: dict[str, Union[str, int]] = {'yys': '招财', 'age': 18}
Union类型注解也可也运用在函数(方法)形参及返回值中
Union的使用总结:
- 导包:from typing import Union
- 使用:Union[类型1, 类型2…]
虽然python是一门动态语言,但是对于在项目中使用来说,希望大家写好类型注解,编译器能在我们调用时提前进行检查,能减少我们程序出错的概率
9.12 多态
多态定义:不同的 子类对象 调用相同的 父类方法,产生不同的执行结果
总结:
- 多态 可以 增加代码的灵活度
- 以 继承 和 重写父类方法 为前提
- 是调用方法的技巧,不会影响到类的内部设计
步骤:
- 定义父类,并提供公共方法
- 定义子类(继承父类),并重写父类方法
- 传递子类对象给调用者,可以看到不同子类执行效果不同
举例子,“需求:警务人员和警犬一起工作,警犬分2种:追击敌人和追查毒品,携带不同的警犬,执行不同的工作”:
# 1. 定义父类,提供公共方法: 警犬 和 人
class Dog(object):
def work(self): # 父类提供统一的方法,哪怕是空方法
pass
# 2. 定义子类,子类重写父类方法:定义2个类表示不同的警犬
class ArmyDog(Dog): # 继承Dog类
def work(self): # 子类重写父类同名方法
print('追击敌人...')
class DrugDog(Dog):
def work(self):
print('追查毒品...')
# 定义人类
class Person(object):
def work_with_dog(self, dog): # 传入不同的对象,执行不同的代码,即不同的work函数
dog.work() # 在方法内部,直接让 狗对象 调用 work 方法
# 3. 创建对象,调用不同的功能,传入不同的对象,观察执行的结果
ad = ArmyDog()
dd = DrugDog()
jingyuan = Person()
jingyuan.work_with_dog(ad)
jingyuan.work_with_dog(dd)
恭喜你,你已经学完了python基础的全部内容。完结撒花!
以上是笔者学习过程中的笔记,如有错误,欢迎大家指正
参考资料:
官方文档:https://docs.python.org/zh-cn/3/index.html
菜鸟编程:https://www.runoob.com/python3/python3-tutorial.html
视频资料:https://www.bilibili.com/video/BV1qW4y1a7fU