一、入门
1.1Python简介
各种不同语言的学习内容:
Python程序员的研究方向:
Python属于解释型语言(边翻译边执行), Python解释器用于运行Python代码,即,在程序运行的过程中将代码翻译为机器语言并交给计算机运行。而Pycharm是一款面向开发者的Python编辑器,可用于创建Python项目、管理Python代码。
Python代码需要遵循以下规范:
1.2入门程序
本文采用的是Anaconda+Pycharm环境,可以在命令行中找到Anaconda安装的python解释器,打开解释器并运行代码:
C:\>cd Anaconda
C:\Anaconda>python
Python 3.11.7 | packaged by Anaconda, Inc. | (main, Dec 15 2023, 18:05:47) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> print('Hello World')
Hello World
设置完系统环境变量后,可在命令行中直接使用python命令的打开python:
C:\Users\28591>python
Python 3.11.7 | packaged by Anaconda, Inc. | (main, Dec 15 2023, 18:05:47) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>
事实上,也可以在文本文件中编写好代码,再使用命令进行运行:
1.3注释
代码是给机器执行使用的,而注释是给人看的,并不会运行,方便阅读代码时能够快速了解代码的功能。
单行注释
以#
开始,一直到本行结束都是注释。
#输出"Hello World"
print("Hello World")
多行注释
若希望编写的注释信息很多,一行无法显示,就可以使用多行注释。多行注释用一对连续的三个引号(单引号和双引号均可)来表示,但是注释内容中不能有连续的三个相同引号。
'''
输出Hello World
'''
"""
输出Hello World
"""
print("Hello World")
1.4用户输入input()函数
二、不可变数据类型
2.1变量、常量、数据类型
2.1.1变量的概念
代码用于处理数据,而变量则用于存储数据。变量创建的语法格式:
变量名=变量值;
常用写法:
#创建单个变量
name='Mike'
#多个变量相同值
num1=num2=num3=10
#多个变量不同值
num1,num2=10,20
变量的命名有以下要求:
- 可由字母、下划线和数字组成。
- 不能以数字开头。
- 不能与关键字重名。
- 不能有空格,但可用下划线分隔单词。
当变量名由多个单词组成时,可按照以下命名方式(也可使用小驼峰命名法):
- 每个单词都使用小写字母。
- 单词与单词之间使用下划线隔开,如first_name、last_name。
关键字就是在python内部已经使用的标识符,关键字具有特殊的功能和含义,开发者不允许定义和关键字相同的名字的标识符。可使用以下方式查看所有的关键字:
import keyword
print(keyword.kwlist)
2.1.2常量的概念
程序在运行过程中,值永远不会发生改变的量称为常量。python没有专门的常量类型,一般约定俗成使用大写表示常量。
#圆周率
PI=3.1415926
2.1.3不可变数据类型
Python中有六个标准的数据类型,其中前三种称为不可变数据类型:
- number(数字类型):包含int(整型)、float(浮点型)、bool(布尔型)、complex(复数型)四种基本类型,用于存储数据。
- string(字符串):使用单/双/三引号括起来的一系列字符。
- tuple(元组):使用小括号括起来保存数据,元组的值不可修改,只能将变量重新赋值。
- list(列表):使用方括号括起来保存数据,列表的数据项可以是不同类型,且各个元素也是可改变的。
- set(集合):集合使用大括号括起来保存数据,是一个无序不重复元素的序列,各个元素也是可改变的,常用于去重操作。
- dictionary(字典):字典的每个元素是键值对,且是无序的对象集合,字典可以通过键来引用,键必须是唯一的且键名必须是不可改变的(即键名必须为Number、String、元组三种类型的某一种),但值无要求。
本章只介绍不可变数据类型,在第一次赋值声明时,会在内存中开辟一块空间,用来存放这个变量被赋的值,而这个变量实际上存储的,并不是被赋予的这个值,而是存放这个值所在空间的内存地址,通过这个地址, 变量就可以在内存中取出数据了。所谓不可变就是说,我们不能改变这个数据在内存中的值,所以当我们改变这个变量的赋值时,只是在内存中重新开辟了一块空间,将这一条新的数据存放在这一个新的内存地址里,而原来的那个变量就不在引用原数据的内存地址而转为引用新数据的内存地址。例如:
s='abcd'
s[0]='y'
可见,虽然字符串s可以使用索引进行访问,但不能对字符串s的值进行修改。注意:
s='abcd'
s='asd'
并不属于对字符串s的值进行修改,而是将变量s重新赋值。而可变数据类型的值可以在创建后被修改,在python中,列表、字典、集合都属于可变数据类型。
可以理解为,python的变量本身就是一个指针(存储数据的地址),这个地址可以指向任何类型数据的存储空间。当使用del
关键字回收变量时,del
将指向该对象的指针删除,而当这个对象没有任何指向的时候,Python虚拟机才会删除这个对象。
可通过type()与isinstance()判断数据类型。
a='Hello World'
#使用type()判断
print(type(a))#<class 'str'>
#使用isinstance()判断
if isinstance(a,str):
print('字符串类型')
else:
print('非字符串类型')
2.2数字类型
python的数字类型(Number型)包含int(整型)、float(浮点型)、bool(布尔型)、complex(复数型)四种。
2.2.1整型
python中的整型没有限制大小,可以处理任意大小的整数,也包括负整数。
num1 = 9999999999999999999999999999999999999999999999999999
print(num1)
可以使用sys模块的maxsize来获取系统支持的最大整数:
import sys
print(sys.maxsize) #9223372036854775807
2.2.2浮点型
由整数部分和小数部分组成,事实上,python中的浮点数和其他语言中的double类型一致。浮点型可以用小数点的形式表示,也可以使用科学计数法进行表示,如:
a = 0.0000000000003
print(a) # 3e-13
a = 1e2
print(a) # 100.0
注意,由于二进制数不能完全表示十进制小数 ,所以使用浮点数运算时可能会出现误差:
num1 = 0.1
num2 = 0.2
print(num1 + num2)
#0.30000000000000004
为避免此运算的出现,python中有用于四舍五入的函数round():
math.round(number, ndigits=None)#保留小数点后ndigits位
num1 = 0.1
num2 = 0.2
print(round(num1 + num2, 3))#当小数末尾为0时会自动舍去
#0.3
在python的math库中还提供了向上/下取整函数:
math.ceil(x,/)
math.floor(x,/)
import math
#向上取整
num1=math.ceil(0.1+0.6)
#向下取整
num2=math.floor(1.2+0.2)
print(num1,num2)
在实际代码中,也可通过以下方式判断浮点数的值是否相同:
a=0.3
b=0.4
if (a+b)-0.7 < 1e-6:
print("a+b==0.7")
else:
print("a+b!=0.7")
2.2.3布尔型
布尔类型的变量只有True、False两种,可作为真假的判断。在python中,能够解释为假的值有:None、0、0.0、False、所有的空容器(空列表、空元组、空字典、空集合、空字符串)。常见的布尔变量运算:
注意:布尔值可以相加,相加之后类型就会转换为int类型。
c=True
d=False
d=c+d
print(type(c),type(d),d)
# 输出结果
# <class 'bool'> <class 'int'> 1
2.2.4.复数型
复数由实数部分和虚数本分组成,在python中常用a+bj或者complex(a,b)表示,其中a表示复数的实部,b表示复数的虚部,并且复数的实部a和虚部b都是浮点型。可以使用real()函数获取复数的实部,使用imag()函数获取虚部,使用conjuate()获取复数的共轭复数。
c = 10 + 20j
d = complex(5, 10)
# c.real 实部 c.imag 虚部 c.conjugate 共轭复数
print(c, d, c.real, c.imag, c.conjugate())
输出的结果如下:
(10+20j) (5+10j) 10.0 20.0 (10-20j)
2.3字符串
2.3.1字符串简介
字符串就是一串字符,是编程语言中表示文本的数据类型。在python中是使用一对双引号或一对单引号表示(不能混合使用),也可以是使用三引号(单/双引号均可)的任意文本。其中,三引号方式一般用于表示带有换行的字符串。
str1='''Hello
World
'''
str2='Hello\nWorld'
print(str1)
print(str2)
字符串是不可变类型,虽然可以使用索引、切片访问字符与字串,但当使用索引进行赋值时就会出错,事实上,将变量再次赋值时也是让其指向一个新的字符串,原先的字符串则会被回收。
注意:当文本中含有引号而与两侧引号冲突时,可使用转义字符\
。
str1="It's a hat"
str2='123\'"456'
print(str1)
print(str2)
2.3.2字符串的运算
#1.字符串拼接
str1="Hello"
str2="world"
str3=str1+str2
print("Hello+world="+str3)
#2.重复输出字符串
print('str3*2='+str3*2)
#3.字符串索引
print('str3[2]'+str3[2])
#4.字符串切片
print('str3[2:6]='+str3[2:6])
#5.in
print('w' in str3)
对于r/R
,可避免字符串中特殊字符的转义:
print("使用r之前:"+"hello\nworld")
print("使用r之后:"+r"hello\nworld")
注意,python与Java不同,+
不能将其他类型转为字符串类型与字符串拼接:
str1 = "Hello Python"
print(str1 + 1)
2.3.3字符串切片操作
字符串索引:可以使用索引获取字符串中指定位置的字符,索引计数从0开始。
str="hello,world"
print(str[0])#h
print(str[4])#o
print(str[-1])#d
python提供了字符串切片操作,使得可以从字符串中取出多个字符。语法格式:
#字符串名[开始字符:结束字符:步长]
字符串名[start:end:step]
- start:开始字符的索引,默认为0.
- end:结束字符的索引(不包含该字符),默认为字符的长度,此时最后一个字符被包含在切片范围内。
- step:切片的步长,默认为1。当步长为负数时表示反向。
字符串 | h | e | l | l | o | , | w | o | r | l | d |
---|---|---|---|---|---|---|---|---|---|---|---|
正索引 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
负索引 | -11 | -10 | -9 | -8 | -7 | -6 | -5 | -4 | -3 | -2 | -1 |
str = "hello,world"
#第三个字符(l)开始,第6个字符(,)结束
print(str[2:6])
#第三个字符开始(l),第6个字符(,)结束,步长为2
print(str[2:6:2])
#第1个字符(h)到第6个字符(,)
print(str[:6])
#第3个字符(l)到最后一个字符(d)
print(str[2:])
#从倒数第2个字符(l)开始,到第2个字符(e)结束,倒序输出(指从右向左),步长为-2
print(str[-2:2:-2])
#从倒数第10个字符(e)开始,到第9个字符(r)结束,正序输出(指从左向右),步长为2
print(str[-10:9:2])
注意,切片方向是start->end,当step方向(正数为从左向右,负数反之)与之相反时,切片无效,如:
#从倒数第2个字符(l)开始,到第2个字符(e)结束,倒序输出(指从右向左),步长为2
print(str[-2:2:2])
#从倒数第10个字符(e)开始,到第9个字符(r)结束,正序输出(指从左向右),步长为-2
print(str[-10:9:-2])
字符串反转:
str = "hello,world"
print(str[-1:-12:-1])
注意,end并不包括所指字符,当采用非负索引写法时,0号字符并不能被包括在切片范围内。此处采用负索引写法,且end指向超出正常负索引范围。也可写为:
print(str[::-1])
2.3.4字符串的格式化
2.3.4.1使用格式化操作符
可利用格式化操作符将信息格式化输出,%
被称为格式化操作符,专门用于处理字符串中的格式。注意,%
和不同的字符连用构成格式化字符,不同类型的数据需要使用不同的格式化字符。基本格式:
print("格式化字符串"%(变量1,变量2...))
- %s :字符串。
- %d :整数。
- %c :输出ASCII码对应的字符(Python当中只有字符串类型而无字符类型,故%c会输出字符的ASCII码)。
- %f:浮点数。
- %o :无符号八进制数。
- %x :无符号十六进制数。
- %e : 将整数、浮点数转换成科学计数法。
- %u:无符号整数。
- %%:当字符串中存在格式化标志时,用于输出%。
- #:可配合%o在八进制数前面显示零(‘0’),即%#o,可配合%x在十六进制前面显示’0x’。
- +:使正数带上正号。
- -:左对齐。
year = 2024
month = 4
day = 26
print("今天是%d年%d月%d日" % (year, month, day))#输出:今天是2024年4月26日
#%c的使用
print('%d在ASCII码中表示字母%c'%(56,56))#输出:56在ASCII码中表示字母8
#%%的使用
print('当前下载进度为%d%%'%(99))#输出:当前下载进度为99%
#%o的使用
print('%d使用八进制为%o'%(18,18))#输出:18使用八进制为22
#%x的使用
print('%d使用十六进制为%x'%(18,18))#输出:18使用十六进制为12
#%f的使用(小数点保留一位)
print('我的身高有%.1f米'%(high))#输出:我的身高有174.5米
#%e的使用
print('%f使用科学记数法为%e'%(174.5,174.5))#输出:174.500000使用科学记数法为1.745000e+02
%
可以与占位符配合使用,即,在%后面加数字表示给这个字符多少个位置,不足时会自动使用空格补齐。格式说明:
- %md:以宽度m输出整型数,不足m时,左补空格,超出m时不起作用。
- %0md: 以宽度m输出整型数,不足m时,左补零,超出m时不起作用。
- %m.nf:以宽度m输出实型小数(包括整数部分、小数点、小数部分),小数位为n位,当所有位数之和超出m时不起作用,不足m时,左补空格。
其中,正数表示左对齐,负数表示右对齐。如:%4d表示左对齐一共占4的位置,%-8d表示右对齐一共占8个位置。
#左右对齐
name='小明'
print('我的名字叫%s!'%(name))#我的名字叫小明!
print('我的名字叫%4s!'%(name))#我的名字叫 小明!
print('我的名字叫%-4s!'%(name))#我的名字叫小明 !
#补空格
print("今天是%d年,%2d月,%2d日"%(2024,4,24))#今天是2024年, 4月,24日
#补0
print("今天是%d年,%02d月,%02d日"%(2024,4,24))#今天是2024年,04月,24日
#%f的使用
print("2/3的结果近似等于%6.3f"%(5/3))#2/3的结果近似等于 1.667
print("2/3的结果近似等于%06.3f"%(5/3))#2/3的结果近似等于01.667
2.3.4.2format()函数
format()函数是字符串类的内嵌函数,用于格式化字符串,以大括号{}来标明被替换的字符串。语法:
'{}'.format()
1.基本用法
可按照{}的顺序依次匹配括号中的值:
s = "{} is a {}".format('Tom', 'Boy')
print(s) # Tom is a Boy
可通过索引的方式匹配参数:
s = "{0} is a {1}".format('Tom', 'Boy')
print(s) # Tom is a Boy
通过参数名来匹配参数
s = "{name} is a {sex}".format(name='Tom', sex='Boy')
print(s) # Tom is a Boy
#参数若已定义,则可直接格式化:
name = 'Tom'
sex = 'Girl'
# 以f开头表示在字符串中支持大括号内的python表达式 此用法3.6之后支持
s = f"{name} is a {sex}"
print(s) # Tom is a Boy
通过索引可对参数的部分进行取值:
list1 = ['hello','say','world','s']
'LiMing {0[1]}{0[3]} {0[0]} to {0[2]}'.format(list1) # 'LiMing says hello to world'
2.数字格式化
format()进行数字格式化时不使用%
,而使用:
,其余转换方式基本相同。
#保留一位小数点并携带正负符号
print('{:+.1f}'.format(1.2684)) # +1.3
#保留整数
print('{:.0f}'.format(-45.62556)) # -46
#b二进制,>右对齐,长度为20
'{:>20b}'.format(23) # ' 10111'
#d十进制,<左对齐,长度为15
'{:<15d}'.format(892) # '892 '
#x十六进制,^居中对齐,长度为10
'{:^10x}'.format(16894) # ' 41fe '
#o八进制,^居中对齐,长度为10
'{:^10o}'.format(1394) # ' 2562 '
#给数字加千位
s = "{:,}".format(1000000) # 1,000,000
#可在居中/左/右符号前加上字符,当位数不够时使用字符填充,而不再使用空格
s = "{:-^20}".format('123456') # '-------123456-------'
3.在format格式化时,可使用 * 或者 * * 进行对list、tuple拆分。
foods = ['fish', 'beef', 'fruit']
s = 'i like eat {} and {} and {}'.format(*foods) # i like eat fish and beef and fruit
foods = ['fish', 'beef', 'fruit']
s = 'i like eat {2} and {0} and {1}'.format(*foods) # i like eat fruit and fish and beef
dict_temp = {'name': 'Lily', 'age': 18}
# 字典需要用 ** 进行拆分
s = 'My name is {name}, i am {age} years old'.format(**dict_temp) # My name is Lily, i am 18 years old
2.3.5字符串常用方法
2.4元组
2.4.1元组简介
元组是有序且不可更改的集合,要比列表更加轻量级,性能速度要优于列表。可用代码查看空元组与空列表的大小:
>>> listdemo = []
>>> listdemo.__sizeof__()
40
>>> tupleDemo = ()
>>> tupleDemo.__sizeof__()
24
由于列表是动态的,它需要存储指针来指向对应的元素(占用 8 个字节,因为是64位操作系统)。另外,由于列表中元素可变,所以需要额外存储已经分配的长度大小(占用 8 个字节)。但是对于元组,情况就不同了,元组长度大小固定,且存储元素不可变,所以存储空间也是固定的。
2.4.2元组的创建
元组有以下创建方式:
#1.直接创建空元组
tuple1=()
#2.创建有元素的列表
tuple2-('a','b','c')
#3.利用函数创建空元组
list3=tuple()
注意,当元组中只包含一个元素时,需要在元素后面添加逗号,否则会被当作整型变量
tuple_1 = (20)
print(type(tuple_1)) # <class 'int'>
tuple_2 = (20,)
print(type(tuple_2)) # <class 'tuple'>
与函数list()相似,tuple()函数有多种创建元组的方式:
tuple(iterable=(), /)
#将字符串转换成元组
tup1 = tuple("hello")
print(tup1)
#将列表转换成元组
list1 = ['Python', 'Java', 'C++', 'JavaScript']
tup2 = tuple(list1)
print(tup2)
#将字典转换成元组
dict1 = {'a':100, 'b':42, 'c':9}
tup3 = tuple(dict1)
print(tup3)
#将区间转换成元组
range1 = range(1, 6)
tup4 = tuple(range1)
print(tup4)
#创建空元组
print(tuple())
2.4.3元组的操作
tuple1 = ('a', 5, 'c', 7, 'd', 9)
tuple2 = ('a', 'b', 'c', 'd', 'e')
#元组的加法
print("tuple1+tuple2=", tuple1+tuple2)
#元组的乘法
print("tuple1*2=",tuple1*2)
#元组的索引
print('tuple1[3]=',tuple1[3])
#元组的切片(与列表类似)
print(tuple1[1:5:2])
#元组的长度
print(len(tuple1))
#元组的加法
print(tuple1+tuple2)
#元组的乘法
print(tuple1*2)
#in的使用
print(5 in tuple1)
#元组的遍历
for i in tuple1:
print("tuple1[{0}]".format(i),"=",i,end=" ")
#删除元组
del tuple1
元组的常用方法:
方法名 | 用途 |
---|---|
count() | 统计元素个数 |
index | 查找元素第一次出现时的索引 |
tuple1 = (1, 2, 4, 1, 3, 5)
print('tuple1中1的个数为:', tuple1.count(1))
print('tuple1中4第一次出现时的索引为:', tuple1.index(4))
2.5类型转换与进制转换
注意:
- int()函数无法将小数字符串如
2.23
转化为整数,此时会抛出ValueError异常。但是可先将该字符串转为float类型,再转为int类型。 - True与False转为整型会依次转为1与0,转为浮点型则对应1.0与0.0。
- 浮点型转为布尔型,0.0会转为False,其余均为True,整型转布尔型同理。
- 字符串型转布尔型时,非空字符串(注意空串与空格串不同)都会转为True,空字符串则转为False。
进制转换
int()函数中提供了可用于进制转换的参数:
int([x]) -> integer
#给定字符串x,base表示x所包含数据的进制,int()函数返回值为x对应的十进制值
int(x, base=10) -> integer
#输出十六进制为10ACDE的十进制值
a=int('10ACDE', 16)
print(a)
2.6小整数的地址问题
>>> a=3
>>> id(a)
140703780946792
>>> b=3
>>> id(b)
140703780946792
可见,a与b虽然是不同的变量,但由于存储的相同的小整数,所以id值相同(id相同于哈希映射)。
三、可变数据类型
3.1序列的概念
在python中,有一些类型,它们的成员是有序排列的,并且可通过下表访问成员,这些类型称之为序列,包括列表、range、元组和字符串等。对于后文中的集合,其无序不属于序列,而字典是映射类型,不能根据下标查找,只能根据key来查找,同样不属于序列
注意,有的方法如max()、min(),对字典使用时,只能对key生效。
3.1列表
3.1.1列表的创建
List(列表)是Python中内置的有序、可变序列(<class 'list'>
),在其他语言中通常叫做数组,专门用于存储一串信息,不同的是,列表当中存储的数据不一定有相同的数据类型。列表使用[]
定义,数据之间使用,
分隔,可以使用索引来访问数据(索引即为数据在列表中的位置编号,又被称为下标)。列表有以下创建方式:
#1.直接创建空列表
list1=[]
#2.创建有元素的列表
list2=[10,9,True,'张三']
#3.利用函数创建空列表
list3=list()
函数list()有多种创建列表的方式:
list(iterable=(), /)
可使用内置函数isinstance()与Iterable
来判断一个对象是否可迭代,如:
from collections.abc import Iterable
def is_iterable(obj):
return isinstance(obj, Iterable)
my_list = [1, 2, 3]
my_dict = {'a': 1, 'b': 2}
my_string = "Hello"
print(is_iterable(my_list)) # 输出: True
print(is_iterable(my_dict)) # 输出: True
print(is_iterable(my_string)) # 输出: True
print(is_iterable(1))# 输出: False
print(is_iterable(True))# 输出: False
如使用可迭代对象字符串进行初始化时可得到:
print(list("Hello"))
3.1.2列表的基本操作
list1 = ['a', 5, 'c', 7, 'd', 9]
list2 = [True, 8, 9, False, 12]
#列表的索引
print("list1[3]=", list1[3])
#列表的切片
print(list1[2:6:2]) #与字符串的切片方法相同
#列表的加法
print(list1 + list2)
#列表的乘法
print(list1 * 2)
#成员运算
print('a' in list1)
#获取长度
print(len(list1))
#删除列表元素
del list1[2]
#删除列表
del list1
#列表的遍历
list1 = ['a', 5, 'c', 7, 'd', 9]
for i in list1:
print("i=", i,end=',')
也可使用枚举方法遍历:
enumerate(iterable, start=0)
list1 = ['a', 5, 'c', 7, 'd', 9]
for i,j in enumerate(list1):
print("index=",i,",value=",j,end=",")
3.1.3列表的常用方法
对于sort()函数,其会改变列表中元素的顺序且丢失原列表,此时可使用sorted()函数,使用新变量来接收修改后的列表而不改变原列表。
3.2字典
字典使用{}
进行定义,用键值对存储数据,是python唯一内建的映射类型。其中,键值对之间使用,
进行分隔。键key是索引,值value是数据,键值对之间使用:
进行分隔,且键必须是唯一的,而值可以是任意数据类型,但键只能使用字符串、数字或元组。
3.2.1字典的基本操作
字典的定义:
#1.直接生成空字典
dict1={}
#2.定义有元素的字典
dict2={"nane": "张三", "age": 20, "sex": "男"}
#3.使用函数生成字典
dict3=dict()
字典的常用操作:
# 定义一个字典
dict1 = {"name": "张三", "age": 20, "sex": "男"}
# 增加元素
dict1["score"] = 100
print(dict1)
#删除元素
del dict1["name"]
print(dict1)
#查找元素
value = dict1["sex"]
print(value)
#修改元素
dict1["name"] = "李四"
print(dict1)
#清空字典
dict1.clear()
print(dict1)
#删除字典
del dict1
字典的遍历:
# 定义一个字典
dict1 = {"name": "张三", "age": 20, "sex": "男"}
#遍历字典键值对
for k,v in dict1.items():
print(k,v)
#遍历字典所有键
for k in dict1.keys():
print(k)
#遍历字典所有值
for v in dict1.values():
print(v)
3.2.2字典的常用函数
函数名 | 作用 |
---|---|
len() | 返回键值对的个数 |
keys() | 返回字典中键的集合 |
values() | 返回字典中值的集合 |
items() | 返回包含(键,值)元组的列表 |
in/not in | 判断key是否存在于字典中 |
pop() | 删除键对应的键值对 |
clear() | 清空字典 |
copy() | 复制字典对象,返回新对象(id不同) |
get() | 获取键对的值,不存在时返回None |
pop() | 参数为键,返回值为对应的值,键不存在时报错 |
popitem() | 无参数,删除最后一个键值对,并返回由键值对构成的元组 |
update() | 参数为键值对,用于更新字典 |
# 定义一个字典
dict1 = {"name": "张三", "age": 20, "sex": "男"}
#len():测量字典中的键值对
print(len(dict1))
#keys():返回所有的key
print(dict1.keys())
#values():返回包含value的列表
print(dict1.values())
#items():返回包含(键值,实值)元组的列表
print(dict1.items())
#in not in
if 20 in dict1.values():
print("我是年龄")
if "李四" not in dict1.values():
print("李四不存在")
常使用update()函数更新字典,key重复的话则新的覆盖老的,如:
d = {'小明': 180, "小芳": 175, "小红": 170} # 创建字典
d.update({'小明':170,'小芳':180})
3.3集合
注意:集合(Set)只存储不可变的数据类型,如Number(数字)、字符串、元组等,而无法存储列表、字典、集合这些可变的数据类型。
3.3.1集合的基本操作
1.集合的创建
#1.直接创建空集合
set1={}
#2.创建包含元素的集合
set2={1,'abc',False,True}
#利用函数创建集合
set3=set()
集合创建函数:
set() -> new empty set object
set(iterable) -> new set object
s1=set([1,2,3])#列表转为集合
print(s1)
s2=set((1,2,3))#元组转为集合
print(s2)
s3=set('123')#字符串转为集合
print(s3)
s4=set({1:'a',2:'b'})#字典转为集合
print(s4)#只保存key值
print(1 in s1)#in操作
print(len(s1))#len操作
print(max(s1))#max操作
del s1#删除操作
2.常用方法
s = {'a', 'b', 'c', 'd', 'e'}
s.remove('a') #删除元素
print(s)
s.update({'a', 1, 2, 3}) #增加元素,重复元素会被过滤
print(s)
s.add((4)) #增加元素
print(s)
s.pop() #删除表头元素
print(s)
s.clear() #清空集合
print(s)
del s #删除集合
3.3.2集合的数学运算
s1 = {1, 2, 3, 4, 5}
s2 = {4, 5, 6, 7, 8}
print(s1 & s2) #求交集
print(s1 | s2) #求并集
#列表去重
score = [80, 70, 80, 60, 70, 50]
s = set(score)
print(s)
#统计每个分数有多少名学生
for i in s:
print('得分为', i, '的学生有', score.count(i), '人')
四、运算符
4.1算术运算符
4.2赋值运算符
4.3比较运算符
4.4逻辑运算符
4.5位运算符
4.6成员运算符与身份运算符
成员运算符的应用广泛,可以运用在列表、字符串等序列当中,如:
print(1 in [1,2,3,4,5,6,7,8,9])//True
print('he' in 'hello world')//True
print('ho' not in 'hello world')//True
print(a is b)//True
4.7运算的优先级
五、流程控制
5.1分支选择结构
注意,代码的缩进为一个tab键或者四个空格。
#单分支选择结构
if 条件:
执行语句;
#双分支选择结构
if 条件:
执行语句;
else:
执行语句;
#多分支选择结构
if 条件1:
执行语句;
elif 条件2:
执行语句:
elif 条件3:
执行语句;
...
else:
执行语句;
5.2match匹配语句
5.3while循环
#求1~100的和
i = 1
sum = 0
while i <= 100:
sum = sum + i
i = i + 1
print("1~100的和为:" + str(sum))
5.4range()函数与for循环
5.4.1range()函数
range()函数
注意,range返回的序列属于不可变序列,不支持元素修改,不支持+和*操作。
range()函数用于生成包含连续多个整数的整数序列,函数声明如下:
range(stop) -> range object
range(start, stop[, step]) -> range object
- start:开始元素,默认为0.
- stop:结束元素,但不包括该元素。
- step:步长,默认为1.
#传递一个参数
range(5)#返回序列[0,1,2,3,4]
#传递两个参数
range(2,5)#返回序列[2,3,4]
#传递三个参数
range(2,10,2)#返回序列[2,4,6,8]
注意,step可为负值,即返回由大到小的整数序列,但此时start的值也必须大于stop,即step的正负应与start和stop的方向相同。
在实际应用当中,range()函数有很多常见用法:
- 构建for循环。
- 使用range(len(list))遍历列表。
- 使用range()生成固定长度的等差数列
- 使用range()生成逆序整数序列数列
#构建for循环
for i in range(5):
print('i='+str(i))
#使用range(len(list))遍历列表
fruit_list = ['apple', 'banana', 'orange']
for i in range(len(fruit_list)):
print('Index:', i, 'Value:', fruit_list[i])
#使用range()生成固定长度的等差数列
a = [2 + 3 * i for i in range(5)]
print(a)#[2, 5, 8, 11, 14]
#使用range()生成逆序整数序列数列
for i in range(9, 5, -1):
print(i)#[9,8,7,6]
5.4.2for循环
#求1~100的和
sum = 0
for i in range(101):
sum = sum + i
print("1~100的和为:" + str(sum))
5.5控制关键字
六、函数
6.1函数的定义
在开发程序时,需要某块代码多次,但是为了提高编写的效率以及代码的重用,所以把具有独立功能的代码块组织为一个小模块,这就是函数。函数使用关键字def
进行定义,在定义函数前应确定函数名称、参数名称、参数个数、编写函数体(用于实现函数功能的代码)。定义好函数之后,需要主动通过调用函数的名称来执行函数。
def add(num1, num2):
print(num1 + num2)
add(2, 3)
函数的参数传递
- 形参:函数定义时括号里的参数,用于接收参数,在函数内部作为变量使用。
- 实参:函数调用时括号里的参数,用于将数据传递到函数内部使用。
在函数add()的定义中,变量num1、num2就是形参,而传递给函数的2、3就是实参,在调用执行时,将实参的值传递并保存在了形参当中,这就是函数的参数传递。
函数的返回值
函数的返回值时函数完成工作后,最后给调用者的一个结果。在函数中使用return关键字可以返回结果,而调用函数的一方可使用变量来接收函数的返回结果。
def add(num1, num2):
return num1 + num2
print('2+3=', add(2, 3))
6.2函数参数
6.2.1函数参数类型
向函数传递实参的方式有很多,分为位置参数、默认参数和可变参数三种:
- 位置参数:要求实参顺序和形参顺序完全一致,由形参顺序决定实参顺序。
- 默认参数:定义函数时,可以给某个参数指定一个默认值,称为默认参数。函数调用时,若未给该形参传递实参,则采用默认值。注意,默认参数必须要位于位置参数之后(否则会有
SyntaxError
异常)。 - 关键字参数:传递实参时可指明要传递的形参名称,格式为
形参=实参
,此时无需考虑函数调用中的实参顺序,还清楚指出函数调用中各个值的作用。 - 可变数量的位置参数:允许接收任意数量的位置参数,可以是0、1、2、…个,在函数定义中使用
*
来指定一个可变数量的位置参数,此时会被打包为元组在函数内部使用。 - 可变数量的关键字参数:允许接受任意数量的关键字参数,在函数定义中使用
**
来指定一个可变数量的关键字参数,此时会被打包为字典在函数内部使用。
在参数列表中,位置参数必须在关键字参数(关键字参数包含默认参数)之前,即:位置参数、可变数量的位置参数、默认参数、可变数量的关键字参数。在函数调用时同样遵循,即:位置参数、可变数量的位置参数、关键字参数、可变数量的关键字参数。
def infos(name, age=18, gender='Male'): # age、gender是默认参数
return '大家好,我叫%s,我今年%d岁,我是一名%s生'%(name,age,gender)
Mike = infos('Mike', gender='Male') # 使用关键字参数gender进行传递
Jack = infos('Jack', 20, 'Female') # 使用位置参数进行传递,保证实参顺序与形参列表一致
6.2.2组包与拆包
组包(packing)和拆包(unpacking)是指在没有明确指定变量数量的情况下,对多个值进行打包成一个元组(或者其它集合类型),或者将一个集合类型的对象解开成单独的变量。
组包
当我们将多个值赋给一个单独的变量时,Python会自动将这些值组包成一个元组:
a = 1,2,3 #1,2,3被打包成元组赋给a
print(a) #输出:(1,2,3)
拆包
当我们有一个集合类型的对象(比如元组或列表)并且我们想要将其内部的元素赋给多个单独的变量时,我们可以使用拆包。
a, b, c = (1, 2, 3)
print(a) # 输出: 1
print(b) # 输出: 2
print(c) # 输出: 3
当函数有多个返回值时,这些返回值就会被打包为一个元组,此时需要以拆包的方式进行接收:
def fun():
return 10, 20
a, b = fun()
在python中提供了拆包运算符*
,可将序列中的值全部拆包返回,适用于列表、元组、字典,在对字典拆包时,返回所有键。
list1=[1,2,3,4]
tuple1=('a','b','c')
dict1={'a':1, 'b':2, 'c':3}
print('*list1=',*list1,sep='')
print('*tuple1=',*tuple1,sep='')
print('*dict1=',*dict1,sep='')
6.2.3可变参数传递
拆包符可用于传递可变参数,使用*形参名
,此时所有相应参数都会被打包为一个元组,该元组存储在形参当中:
def total(*args):
sum = 0
print('args=',args) # 输出:args= (1, 2, 3, 4)
for i in args:
sum = sum + i
return sum
print('1+2+3+4=', total(1, 2, 3, 4),sep='') # 输出:1+2+3+4=10
当需要传递字典时,可使用**形参名
,也可使用关键字参数的方式进行传递:
def fun(**kwargs):
for key, value in kwargs.items():
print(f'{key}: {value}')
# 传递字典
dict1={'name':'Mike', 'age':20, 'sex':3}
fun(**dict1)
#传递多个关键字参数
fun(name='Mike',age=20,sex=3)
6.2.4混合参数传递
def print_info(name, *args, age=18, **kwargs):
print("Name:", name)
print("Age:", age)
print("Additional arguments:")
for arg in args:
print("-", arg)
print("Keyword arguments:")
for key, value in kwargs.items():
print("-", key, ":", value)
print_info("Frica", "arg1", "arg2", age=25, city="GuangZhou", country="China")
- name:位置参数。
- *args:可变数量的位置参数。
- age:默认参数,在调用函数时使用了关键字参数的传递方式。
- **kwargs:可变数量的关键字参数。
6.3局部变量与全局变量
- 局部变量:在函数内部定义的变量,只能在函数内部使用。
- 全局变量:在函数外部定义的变量,所有函数内部都可使用。
num1=10
def fun():
print(num1)
fun()
6.3.1.python对局部变量与全局变量的判断
在python当中没有变量的声明,而通过以下规则判断变量的作用范围:查找函数每一行的赋值语句,如果有一个函数内部的变量赋值了(python眼里,赋值即重新定义,此时就定义了一个val),就判断该变量是局部变量。若不存在赋值语句,则判断是全局变量。
在下例中,num1在函数体执行前就被判断为局部变量,故执行时会报错。
num1=10
def fun():
print(num1)
num1=20
fun()
为实现函数内部更新全局变量的值,python提供global
关键字,用于声明当前函数内部使用全局变量。
num1=10
def fun():
global num1
print(num1)
num1=20
fun()
print(num1)
注意,当有global
声明语句时,被声明的变量不能在该语句之前使用:
6.3.2局部变量与全局变量的优先级
在函数体中,局部变量的优先级大于全局变量,但在函数执行完成后,局部变量就会被销毁。
num=10
def fun1():
num=20 #局部变量
print(num)
def fun2():
print(num) #全局变量
fun1()
fun2()
即,在自定义函数中的同名局部变量仅能在函数内部使用,当执行下一个函数时,该变量会被销毁,此时默认使用全局变量。
6.3.3可变类型的全局变量
对于可变类型如list类型,修改其值时无需使用global
声明,可直接进行修改(可理解为并未改变变量的引用,只是对对象内部值进行修改,而不可变数据类型的重新赋值会改变引用)。
list1 = [1, 2, 3, 4]
def fun():
list1[3] = 5
fun()
print(list1)
其余部分与不可变数据类型基本一致:
list1 = [1, 2, 3, 4]
def fun():
list1=[5,6,7,8] #局部变量
print(list1)
fun()
print(list1)
list1 = [1, 2, 3, 4]
def fun():
print(list1)
list1=[5,6,7,8] #局部变量
fun()
print(list1)
6.4匿名函数
lambda函数是一种快速定义的单行最小函数,可以用在任何需要函数的地方,可使代码更加精简,且无需考虑命名问题。lambda函数能接收任何数量的参数,但只能返回一个表达式的值,lambda函数是一个函数对象,直接赋值给一个变量,这个变量就成了一个函数型变量。
事实上,匿名函数特性是在得到第一个返回值的时候就会把变量的内存释放,无法得到第二个返回值。
1.定义格式
name = lambda [arg1 [,arg2,...,argn]]:expression
- name:函数变量名,用于调用lambda表达式。
- expression:用于实现具体功能的表达式。
power=lambda x,y=1:x**y #将定义好的函数内存地址赋值给变量power
print(power)
print(power(2,3)) #调用方式与普通函数相同
#简化写法:
print((lambda x,y=1:x**y)(2,3))
2.条件表达式与lambda函数的结合
# 如果x>y输出x,否则输出y
print((lambda x, y=1: x if x > y else y)(3)) #x作为返回值
# 如果x>y输出x,x<y输出y,否则两个都输出
print((lambda x, y=1: x if x > y else y if x < y else f'{x} {y}')(3, 6))
3.lambda函数作为函数参数传递
def sub_func(a, b, func):
print('a =', a)
print('b =', b)
print('a - b =', func(a, b))
sub_func(100, 1, lambda a, b: a - b)
在python的一些常用内置函数中,提供了函数参数进行传递:
- sorted():Python中对列表排序的内置函数,可以使用lambda来获取排序对象的关键字属性。
- map():Python中用来做映射的一个内置函数,接收两个参数,第一个参数是一个函数,第二个参数是一个可迭代对象,map会遍历可迭代对象的值,然后将值依次传递给函数执行。
member_list = [
{"name": "风清扬", "age": 99, "power": 10000},
{"name": "无崖子", "age": 89, "power": 9000},
{"name": "王重阳", "age": 120, "power": 8000}
]
new_list = sorted(member_list, key=lambda dict_: dict_["power"]) #dict_即为传入的单个对象(此处为字典类型)
print(new_list)
number_list = [100, 77, 69, 31, 44, 56]
num_sum = list(map(lambda x: {str(x): x}, number_list))
print(num_sum)
七、异常处理
2.6.1简介
若代码没有语法错误,可以运行,但会出运行时的错误,如除零错误、下标越界等问题,这种在运行期间检测到的错误被称为异常。出现了异常必须处理,否则程序会终止运行,python支持程序员使用try-except语句进行异常的检测和处理。常见报错类型:
2.6.2异常的捕获
try:
print('可能出现异常的代码')
print(5/0)
except IOError:
print('如出现异常,则进入该代码块执行')
虽然进行了异常处理,但程序仍然因出现异常而终止运行,即,except并未生效。这是因为except捕捉的是IOError,但程序此时产生的是ZeroDivisionError异常。在实际开发当中可以使用元组同时捕获多个异常,并且使用关键字as
存储异常的基本信息:
try:
print('执行try代码块')
print(5/0)
open('123.text','r')#123.txt文件不存在,会产生IOError异常
except (IOError,ZeroDivisionError) as result:
print('出现异常,类型为',result)
事实上,可通过Exception
捕捉所有异常:
try:
print('执行try代码块')
open('123.text','r')#123.txt文件不存在,会产生IOError异常
print(5 / 0)
except Exception as result:
print('出现异常,类型为',result)
2.6.3else与finally关键字的使用
else关键字
try:
num = 100
print(num)
except NameError as errorMsg:
print('产生错误了:%s'%errorMsg)
else:
print('没有捕获到异常')
当except下的代码块未执行时,就会执行else下的代码块。
finally关键字
try:
5/0
except ZeroDivisionError as errorMsg:
print('产生错误了:%s'%errorMsg)
else:
print('没有捕获到异常')
finally:
print('程序运行结束')
finally下的代码块始终会被执行。
2.6.4异常的传递
1.函数嵌套中异常的传递
def test1():
print('test1开始执行')
print(num)
print('test1结束执行')
def test2():
print('test2开始执行')
test1()
print('test2结束执行')
test2()
def test1():
print('test1开始执行')
print(num)
print('test1结束执行')
def test3():
try:
print('test3开始执行')
test1()
print('test3结束执行')
except Exception as e:
print('捕捉到异常:',e)
test3()
当函数嵌套执行过程中出现异常时,如函数A---->函数B---->函数C,其中函数C中抛出异常,若函数C并未对该异常进行处理,则该异常会传递到函数B中,并以此类推。若所有函数都未进行异常处理,则程序报错,并终止运行。
2.try嵌套中的异常传递
try:
print('执行try1下的代码块')
try:
print('执行try2下的代码块')
5/0;
except IOError:
print('try2下检测出异常')
finally:
print('try2结束执行')
except Exception as e:
print('try1检测出异常')
finally:
print('try1结束执行')
执行思路同函数的嵌套,try2代码下并未捕获到该异常,而被try1代码块捕获。
八、模块、包
模块相当于时工具包,要想使用这个工具包中的工具,就需要导入(import)这个模块。每一个以扩展名py结尾的python源代码文件都是一个模块,在模块中定义的全局变量、函数都是模块能够提供给外界直接使用的工具。常见的标准库:
8.1模块的导入
通过关键字import
可引入两种模块:
- 安装好的第三方库(安装在虚拟环境当中)。
- 运行文件所在目录下的文件。
常见引用格式:
- import os,sys:一次引用多个模块,使用
模块名.函数名
调用函数。 - import pandas as pd:引入模块并起别名,使用
别名.函数名
调用函数。 - from random import randint:仅仅引入模块中的某一功能(可能是函数、变量),可直接调用函数,但不可通过
模块名.函数名
调用其他函数。 - from random import *:可直接调用random模块下的所有函数。
8.2软件包的使用
包是Python模块的一种组织方式,将多个模块组合在一起,形成一个强大的Python工具库。包是一个拥有__init__.py
文件的目录,其定义了包的属性和方法,在资源管理器中,包以文件夹的形式存在。
新建软件包后,可在helloworld.py中通过语句调用包下Arithmetic模块中的函数:
from myPackage.Arithmetic import * #调用Arithmetic模块
__init__.py
文件用于组织包(package),方便管理各个模块之间的引用、控制着包的导入行为。该文件可以什么内容都不写,即为空文件,相当于一个标记,但此时也无法使用from 包名 import *
来从包中导入所有模块(from 包名 import 模块
导入单个模块仍可行)。可在__init__.py
文件中使用__all__
:
__all__
:用来指定此包被import *
时,哪些模块会被导入。不在__all__
列表中的模块不会被其他程序引用。
__all__ = ['Arithmetic','print']
#myPackage下的模块文件名
8.3import的运行原理
8.3.1sys.modules
sys
模块是Python标准库中的一个内置模块,提供了与Python解释器和运行环境相关的功能。它包含了一些与系统操作和交互相关的函数和变量,可以用于获取命令行参数、控制程序的执行、管理模块和包、处理异常等。而sys.modules
是一个全局字典,在python启动后就加载在内存中,包含了当前已导入的所有模块,起到模块加载的缓存作用,当再次导入模块时,python会先在字典当中进行查找,加快程序运行速度。
C:\Anaconda\envs\demoProject\python.exe C:\Users\28591\PycharmProjects\pythonProject\helloworld.py
"sys": "<module 'sys' (built-in)>"
...此处进行省略,内容为已导入的模块名(包括使用的插件模块等等)与模块的路径
"traceback": "<module 'traceback' from 'C:\\Anaconda\\envs\\demoProject\\lib\\traceback.py'>"
"sitecustomize": "<module 'sitecustomize' from 'C:\\Pycharm\\plugins\\python\\helpers\\pycharm_matplotlib_backend\\sitecustomize.py'>"
"site": "<module 'site' from 'C:\\Anaconda\\envs\\demoProject\\lib\\site.py'>"
首次导入logging模块后在sys.modules中加入的模块名: {'myPackage'}
再次导入logging模块后在sys.modules中加入的模块名: set()
事实上,程序在导入某个模块时,会首先查找sys.modules中是否包含此模块名,若存在,则只需将模块的名字加入到当前模块的Local名字空间中;若不存在,则需要从sys.path目录中按照模块名称查找模块文件,模块文件可以是py、pyc、pyd,找到后将模块加载到内存,并加入到sys.modules字典,最后将模块的名字加入当前模块的Local名字空间中。
8.3.2命名空间的概念
命名空间(Namespace)是从名称到对象的映射,大部分的命名空间都是通过 Python 字典来实现的,它的键就是变量名,它的值就是那些变量的值。命名空间提供了在项目中避免名字冲突的一种方法。各个命名空间是独立的,没有任何关系的,所以一个命名空间中不能有重名,但不同的命名空间是可以重名而没有任何影响。命名空间有如下级别:
- 内置命名空间(build-in命名空间):Python 语言内置的名称,比如函数名 abs、char 和异常名称 BaseException、Exception 等等。
- 模块命名空间(global namespace):模块中定义的名称,记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。
- 函数命名空间(local namespace):函数中定义的名称,记录了函数的变量,包括函数的参数和局部定义的变量。(类中定义的也是)
locals()和globals()有一个区别是,locals只读,globals可以写。当在模块命名空间字典当中新增键值对时,即使没有定义该变量,仍然可以像访问对象一样进行访问:
# 模块命名空间可自由修改
glos=globals()
glos['a']=3
print(a) # 输出3
def fun():
los=locals()
los['b']=3
print(b)
fun() # 报错,NameError: name 'b' is not defined
事实上,在使用import
时本质就是对命名空间就行操作:
import module
:添加模块命名空间,但保留当前模块原有的命名空间,此时可使用module.name
的方式访问导入模块的函数与变量。from module import fun
:将模块的函数或者变量引到当前模块的命名空间中,所以就不需要使用 module.name 这种方式访问。
8.3.3import路径搜索
sys.path
保存了模块搜索路径的列表,当使用import语句导入模块时,会在sys.path
中寻找模块路径,如果在当前搜索路径列表sys.path中找不到该模块的话,就会报错。
8.4自定义模块导入方式
当需要导入的模块不在sys.path
中任意一条路径下时,使用import语句就无法找到该模块导入,此时就需要使用内置函数sys.path.append()
在sys.path
中增加临时路径方便更简洁的import其他包和模块。这种方法导入的路径会在python程序退出后失效。
8.5模块内置属性
在python语言当中,一切皆是对象,包括模块也被视作一种对象,有着自己的属性。Python模块的属性有两种,分别是内置属性和自定义属性。常见内置属性如下:
- __name__:每个模块都具有一个__name__属性,其中__name__属性是一个字符串,它包含了当前模块的名称。如果当前模块是主模块,则__name__属性为__main__,否则表示当前模块是一个被导入的模块。
- __doc__:包含了当前模块的文档注释信息。在编写Python程序时,我们通常会在每个模块的开头添加一个注释,用来说明该模块的作用和使用方法。而__doc__属性就是用来存储这些注释信息的。
- __file__:包含了当前模块的文件路径。当我们编写Python程序时,通常会将不同的模块存储在不同的文件中,而__file__属性就是用来表示当前模块所在的文件路径的。
- __cached__ :包含了当前模块的编译后的文件路径。在Python解释器第一次导入一个模块时,会将该模块编译成字节码并存储在硬盘上。而__cached__属性就是用来存储编译后的字节码文件路径。
- __loader__ :__loader__属性是一个对象,用来加载当前模块。通常情况下,我们使用Python的import语句来导入模块,而__loader__属性就是用来执行这个过程的。如果我们使用的是Python的默认导入机制,那么__loader__属性就是一个_builtinImporter对象。
8.5.1__name__
当一个模块被导入时,其内部代码会被自动执行:
但在实际开发过程中,一个模块作为主模块运行时所执行的代码并不希望其被导入到其他模块中时也执行,此时就可使用模块内置属性__name__
,是字符串类型。当模块作为主模块运行时,该值为“main”,否则其值为模块名的字符串。
8.6第三方库的安装与管理
8.6.1Pycharm安装第三方库
可查看当前库的安装位置(当前虚拟环境的第三方库文件夹内):
使用同样的方法可以移除该第三方库。
8.6.2使用pip命令
pip是Python的包管理器,它可以帮助你轻松地安装、更新和卸载Python包(库或工具)。你可以把pip想象成一个应用商店,你可以从中获取你需要的Python包,然后安装到你的Python环境中。
使用pip help
可查看所有命令:
在针对具体命令时,可使用pip 选项 -h
查看该命令所有选项:
//查看pip版本
pip --version
//更新pip版本
python -m pip install --upgrade pip
//安装包
pip install package_name
//升级包
pip install --upgrade package_name
//卸载包
pip uninstall package_name
//列出已安装的包
pip list
//查看指定包的信息,包括安装路径、版本信息
pip show package_name
//使用指定镜像网站进行下载
pip install package_name -i https://pypi.tuna.tsinghua.edu.cn/simple
//指定安装第三方库的版本,可使用==、=>、<=、<、>
pip install package_name == 版本号
//安装多个包
pip install package1 package2 package3
//批量安装
pip install -r C:\...文件路径\requirements.txt
//查看需要升级的库
pip list -o
//卸载单个库
pip uninstall package_name
//批量卸载
pip uninstall -r C:\...文件路径\requirements.txt
常用镜像网站:
- 清华大学:https://pypi.tuna.tsinghua.edu.cn/simple
- 阿里云:http://mirrors.aliyun.com/pypi/simple
- 豆瓣:http://pypi.douban.com/simple
- 中科大:https://pypi.mirrors.ustc.edu.cn/simple
- 网易: https://mirrors.163.com/pypi/simple
九、类
面向对象(Object oriented Programming,OOP)编程的思想主要是针对大型软件设计而来的,面向对象编程将数据和操作数据相关的方法封装到对象中,从而大大提高了编程的效率。Python 完全采用了面向对象的思想,是真正面向对象的编程语言,完全支持面向对象的基本功能,例如:继承、多态、封装等。在Python中,一切皆对象。我们在前面学习的数据类型、函数等,都是对象。
对象由三部分组成:
- 对象的id:id用来标识对象的唯一性,每一个对象都有唯一的id。id是由解析器生成的,相当于对象的内存地址,对象一旦创建,则它的id永远不能再改。,可以通过id()函数来查看对象的id。
- 对象的类型:类型用来标识当前对象所属的类型,类型决定了对象有哪些功能,python是一门强类型的语言,对象一旦创建类型便不能修改。可以通过type()函数来查看对象的类型。
- 对象的值:对象并没有直接存储到变量中,在python中变量更像是给对象起了一个别名。事实上,变量中存储的不是对象的值,而是对象的id(内存地址),当我们使用变量时,实际上就是在通过对象id在查找对象。
在Python中,类是一种用户自定义的数据类型,它可以表示具有相同属性和方法的对象的集合,在创建类的实例之前,需要先定义类。一旦定义了类,就可以通过创建其实例来访问其属性和方法。
9.1类的基本使用
类通常使用class关键字来定义,类名通常使用首字母大写的驼峰命名法。类的定义一般包括属性和方法。在定义类中的方法时,需要在参数列表中增加self
参数,这一参数相当于Java中的this指针,指向当前对象(谁调用该方法,self就指向谁),可用于区分成员变量与局部变量、成员方法与局部方法。虽然self出现在形参列表中,但是不占用参数位置。定义类的基本格式:
# 设计类
class 类名:
'''类的信息说明'''
# 类的属性
my_attribute = 0
# 类的方法
def method(self):
print("Hello World")
# 创建对象
varA=类名()
# 调用类方法
varA.method()
# 为对象属性赋值
varA.my_attrubute=1
在完成类的定义后,实际上并不会立即创建一个实例,而需要使用类名来创建实例,即实例化该类的对象。
#定义Dog类
class Dog:
species = 'Dog'
def speak(self):
print("self=",self)
print('I am a ' + self.species)
#实例化Dog对象
dog = Dog()
#查看dog对象类型
print(type(dog))
#查看dog地址
print(dog)
#调用类方法
dog.speak()
可见,self就是调用speak()方法的dog对象本身。
9.2实例属性与类属性
9.2.1实例属性
实例属性是从属于实例对象的属性,有以下几种创建方式:
- 在init()方法中进行定义,此时其他实例方法、实例对象也可访问。
- 在本类的其他实例方法中使用self定义,只有执行该方法后该实例属性才会被创建(不建议使用)。
在创建实例对象后,可通过对象来访问实例属性,也可新增属性:
class Cat:
def __init__(self, name):
self.name = name # 初始化Cat类实例属性
cat = Cat("John")
cat.age = 3 # 给cat对象新增实例属性
print(cat.name) # John
print(cat.age) # 3
注意,有对象cat新增的属性age只有该对象拥有,此类的其他对象无法访问。
9.2.2类属性
类属性是从属于类对象的属性,即使对象不创建,该属性也会存在。在类中或者类的外面,可以通过类名.类变量名
来访问。
class student:
classroom='123'
def __init__(self,name,age):
self.name=name
self.age=age
s1 = student('Mike',23)
s1.classroom='456' # s1的classroom类属性被修改
s2=student('John',24)
print(s2.classroom) # s1修改并不影响s2中classroom的值
print(student.classroom) # classroom可直接通过类名访问
9.3类的内置方法
在python类中提供了一些首尾有双下划线的内置方法,这些方法一般是系统定义的特殊方法,会在特殊时刻自动调用,如__init__(),用于实现类的特殊行为,例如构造函数、析构函数、字符串表示等。需要注意的是,特殊方法的名称是由Python预先定义的,无法修改。开发者只需要按照特定的方法名和参数列表来实现特殊方法即可。
9.3.1__init__()
在创建类之后,类通常会自动创建一个名为__init__()的方法。这个方法是Python中类的构造方法,每当创建一个类的新实例时,Python会自动调用它。即使不定义__init__方法,系统会提供一个默认的__init__方法。如果我们定义了带参的__init__ 方法,系统不创建默认的__init__方法。在__init__方法中,第一个参数固定,必须为self表示刚刚创建好的实例对象。构造函数往往用于初始化实例对象的实例属性,使得构建类对象时可以通过传参的方式给参数赋值,如:
class Dog:
def __init__(self):
print("Dog:无参构造")
class Cat:
def __init__(self,name):
self.name = name # 初始化Cat类实例属性
print("Cat:有参构造")
dog = Dog() # Dog:无参构造
cat = Cat("John") # Cat:有参构造
9.3.2其他内置方法
1.__str__字符串方法
__str__
方法决定类的实例对象在打印时输出的内容,默认情况下,打印对象时会输出其内存地址:
class student:
def __init__(self,name,age):
self.name=name
self.age=age
s1 = student('Mike',23)
print(s1)
在重写后,会改变对象打印输出的内容:
class student:
def __init__(self,name,age):
self.name=name
self.age=age
def __str__(self):
return f'{self.name} is {self.age} years old'
s1 = student('Mike',23)
print(s1)
2.__lt__比较方法
创建类后,若未重写__lt__()
,直接使用<
(或>
)比较对象会报错TypeError:< not support between instances of Student and Student
,此时需重写该方法,制定比较规则:
class student:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f'{self.name} is {self.age} years old'
def __lt__(self, other):
return self.age < other.age # 数字之间的比较已由python解释器实现。
s1 = student('Mike', 23)
s2 = student('John', 25)
print(s1 < s2)
print(s1 > s2)
3.__le__比较方法
创建类后,若未重写__le__()
,直接使用<=
(或=>
)比较对象会报错TypeError:<= not support between instances of Student and Student
,此时需重写该方法,制定比较规则:
class student:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f'{self.name} is {self.age} years old'
def __le__(self, other):
return self.age <= other.age # 等于时返回True
4.__eq__比较方法
创建类后,若未重写__e1__()
,直接使用==
比较对象会报错TypeError:== not support between instances of Student and Student
,此时需重写该方法,制定比较规则:
class student:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f'{self.name} is {self.age} years old'
def __eq__(self, other):
return self.age == other.age # 等于时返回True
9.4封装
面向对象编程是许多编程语言都支持的一种编程思想,简单理解就是,基于模板(类)去创建实体(对象),使用对象完成功能开发。面向对象包含三大主要特征:封装、继承、多态。
9.4.1私有成员
在类中提供了私有成员变量、私有成员方法,这些方法可以在类内部被使用。定义格式为:
# 私有成员变量:变量名以两个下划线开头
__变量名=值
# 私有成员方法:方法名以两个下划线开头
__方法名(参数列表):
方法体
私有方法无法直接被类对象使用,私有变量无法被类对象赋值、获取值。
class Phone:
# 定义私有成员变量
__brand = 'Apple'
# 定义私有成员方法
def __kee_single_core(self):
print("使CPU单核运行")
私有成员可以在类的内部被使用:
class Phone:
__current_voltage=0.5 # 当前手机运行电压
def __keep_single_core(self):
print("开始单核运行")
def call_by_5g(self):
if(self.__current_voltage>=1):
print("5g已开启")
else:
self.__keep_single_core()
print("电量不足,关闭5g,开启单核模式")
Phone().call_by_5g()
9.4.2保护成员
在类中使用单下划线开头的方法、变量称为保护成员,表示该变量是类的内部使用,不建议在类外部直接访问。但是这只是一种约定,Python并不会强制限制对这些变量的访问。因此,开发者仍然可以在类外部访问以单下划线开头的变量,但这并不符合约定,也不被推荐。
class MyClass:
def __init__(self):
self._internal = 0 # 内部变量
def _internal_method(self):
print('Internal method')
my_obj = MyClass()
my_obj._internal_method()
print('num=',my_obj._internal,sep='')
9.5继承
9.5.1基本使用
通过继承,可以创建一个新的类,它继承了现有类的属性和方法。新类称为子类(派生类),现有类称为父类(基类、超类)。子类可以添加新的属性和方法,也可以覆盖父类的属性和方法。
继承的基本语法:
class 类名(父类名):
类内容实体
- 子类继承父类的成员变量和成员方法,但不能继承父类的私有成员。
- 子类不能删除父类的成员,但可以重写父类的成员。
class Phone:
__IMEI=None # 序列号
producer="Apple" # 厂商
def carry(self):
print('运行在4g')
def __open(self):
print('手机开始运行')
class Phone2024(Phone):
face_id='10001' #面部识别id
def carry(self):
self.__open() # 错误写法,子类不能访问父类的私有方法
print('运行在5g')
def print_MIEI(self):
print('IMEI:',self.__IMEI) # 错误写法,子类不能访问父类的私有变量
phone=Phone2024()
print('face_id:',phone.face_id)
# phone.carry() 报错 AttributeError: 'Phone2024' object has no attribute '_Phone2024__open'
# phone.print_MIEI() AttributeError: 'Phone2024' object has no attribute '_Phone2024__IMEI'
注意,在单继承背景下,使用对象的某一成员时,其查找优先级为:对象>子类>父类>父父类。
9.5.2复写和调用父类成员
1.复写
子类继承父类的成员属性和成员方法后,可对其进行重写,只需在子类中重新定义同名的属性和方法即可。
一旦复写父类成员,类对象在调用成员时就会调用复写后的新成员,若需要使用被复写的父类成员,则需要特殊的调用方式。
- 方式一:使用父类名
- 使用父类成员变量:父类名.成员变量
- 使用父类成员方法:父类名.成员方法(self)
class Phone:
IMEI=None # 序列号
producer='ITCAST' # 厂商
def call_by_5g(self):
print('开启5g通话')
class MyPhone(Phone):
producer='ITEIMA'
def call_by_5g(self):
print('通话开始')
print(f'父类的厂商是:{Phone.producer}')
Phone.call_by_5g(self) # 需要传入self
print('通话结束')
my_phone=MyPhone()
my_phone.call_by_5g()
print(my_phone.producer)
- 方式二:使用super()
- 使用父类成员变量:super().成员变量
- 使用父类成员方法:super().成员方法()
class Phone:
IMEI=None # 序列号
producer='ITCAST' # 厂商
def call_by_5g(self):
print('开启5g通话')
class MyPhone(Phone):
producer='ITEIMA'
def call_by_5g(self):
print('通话开始')
print(f'父类的厂商是:{super().producer}')
super().call_by_5g() # 不需要传入self
print('通话结束')
my_phone=MyPhone()
my_phone.call_by_5g()
print(my_phone.producer)
9.5.3多继承
python支持多继承,新建的类可以支持一个或多个父类
class 类名(父类1,父类2,...):
类内容体
#NFC类
class NFCReader:
nfc_type='第五代'
producer='HM'
def read_card(self):
print('读取NFC卡')
def write_card(self):
print('写NFC卡')
#红外遥控类
class RemoteControl:
ry_type='红外遥控'
def control(self):
print('开启红外遥控')
#手机类
class Phone:
IMIE=None
producer='ITCAST'
def call_by_4g(self):
print('4g通话')
#子类
class MyPhone(Phone,NFCReader,RemoteControl):
pass
phone=MyPhone()
phone.read_card()
phone.write_card()
phone.control()
print('producer',phone.producer)
可以注意到,父类Phone,NFCReader均有成员producer,而子类最后继承的是Phone的成员。事实上,有多个父类时,若有同名的成员,则默认以继承的顺序(从左到右)为优先级,即,先继承的保留,后继承的被覆盖。
上图表示D类有B、C父类,而B、C类有父类A,此时调用同名成员时就会涉及到查找顺序问题。事实上,python当中提供了MRO列表。MRO即Method Resolution Order(方法解析顺序),调用方法时会对当前类及当前类的所有父类(基类)进行搜索,确定方法位置并执行,搜索方法的顺序就是MRO方法解析顺序。MRO列表查找准则:
- 子类先查,再查父类。
- 当继承多个父类的时候,按mro列表顺序被检查(从左到右的优先级)。
class A:
def print(self):
print('A')
class B(A):
def print(self):
print('B')
class C(A):
def print(self):
print('C')
class D(B,C):
pass
print(D.mro())
d=D()
d.print()
补充方法:
- sel.class 查看对象所属类。
- 类名/对象名.dict 查看类/对象名称空间。
- 类名/对象名.bases 查看父类。
- 子类名.mro 打印继承顺序。
- locals() 查看局部名称空间。
- globals() 查看全局名称空间
十、注解
python在3.5版本引入类型注解,以方便静态类型检查工具、IDE等第三方工具。类型注解是在代码中涉及数据交互的地方,提供数据类型的注解(显示的说明),主要功能为:
- 帮助第三方IDE工具(如Pycharm)对代码进行类型推断,协助做代码提示。
- 帮助开发者自身对变量进行类型注解。
目前python中支持变量的类型注解、函数形参列表和返回值的类型注解。
10.1变量注解
基础语法:
变量 : 类型
注意,类型注解只是提示性的,IDE并不会对注解进行验证与判断:
10.2函数注解
函数注解同样也是提示性的,Pycharm并不会进行验证,在类型不等时会给出警告:
10.3联合注解
在给列表、字典设置注解时,若二者所含元素类型固定,可直接使用数据类型进行注解,而当二者包含多种类型时,此时可使用Union构建联合注解。语法为:
from typing import Union
Union[类型1,类型2,...,类型n]
1.给普通变量设置联合注解
var_i: Union[str, int] = 0
var_s: Union[str, int] = "Tom"
var_n: Union[str, int] = None
2.给容器变量设置联合注解
此时,列表中的元素既可以设置为str字符串类型 , 又可以设置为int数字类型。同理,键值对 既可以设置为str字符串类型 , 又可以设置为int数字类型 。
3.给函数参数、返回值设置联合注解
十一、多态
11.1基本概念
11.2抽象类(接口)
十二、文件
12.1文件编码的概念
计算机内部只能识别包含0和1的机器指令,为了存储各类数据,可使用编码技术将内容翻译为0和1存入存储设备当中。
不同的编码,会将内容翻译为不同的二进制吗,只有对二进制文件使用正确的编码进行反向转换,才能获取原本的数据。
UTF-8是目前全球通用的编码格式,在无特殊要求的情况下一律使用该编码方式进行文件编码即可。
12.2文件的读操作
文件操作基本流程为:
- 打开文件。
- 读文件。
- 关闭文件。
需要注意,系统会对被读取的文件维护一个读指针,使得当前读操作只能从上一次读操作结尾处开始。
1.打开文件
2.读取文件
3.关闭文件
12.3文件的写操作
#1.打开不存在的文件,而在'w'模式下会自动创建文件
f = open(r"C:\Users\28591\Desktop\test.txt", 'w', encoding='utf8')
#2.写入数据
f.write("hello world") # 此时数据被写入内存
#3.flush刷新
f.flush() # 将内存中积攒的内容写入到硬盘的文件中
#4.关闭文件
f.close() # 事实上,close()方法内置了flush功能
注意,w
模式下若文件不存在则会自动创建,而若文件存在,则会清空文件数据。
12.4文件的追加操作
#1.打开不存在的文件,而在'w'模式下会自动创建文件
f = open(r"C:\Users\28591\Desktop\test.txt", 'a', encoding='utf8')
#2.写入数据
f.write("hello python") # 此时数据被写入内存
#3.flush刷新
f.flush() # 将内存中积攒的内容写入到硬盘的文件中
#4.关闭文件
f.close() # 事实上,close()方法内置了flush功能
创建test.txt文件:
Hello World
Hello Python
在python中提供了seek()
函数来重新定位文件指针的位置:
文件对象.seek(offset, whence=0)
- 偏移量:表示要移动的字节数。
- 参考位置:默认值为零,有三种参考参考可选。
- 0:将开头作为参考位置(默认)。
- 1 :将当前作为参考位置
- 2 :将末尾作为参考位置
即,如果 whence
设置为0,那么文件指针会移动到距离文件开头 offset
字节的位置。如果设置为1,文件指针会从当前位置前进 offset
字节。如果设置为2,文件指针则会从文件末尾向前移动 offset
字节。