下学期有Python的课,所以这两天没事就学习了一下 Python,因为有 JavaScript 和 Java 的基础,基础部分一切都很顺利,下面是自己针对自己的情况,总结的Python学习笔记。
Python基本语法
注释
Python中单行注释以 # 开头,多行注释可以用 ‘’’ 和 “””
1 | # 单行注释 |
行与缩进
python最具特色的就是使用缩进来表示代码块,不需要使用大括号 {} 。
1 | if 1: |
print输出
print 默认输出是换行的,如果要实现不换行需要在变量末尾加上 end=””:
1 | print("不换行",end="") |
基本数据类型
变量不需要声明
Python 中的变量不需要声明。每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建。
在 Python 中,变量就是变量,它没有类型,我们所说的”类型”是变量所指的内存中对象的类型。
1 | name = 'gsgs' #不需要声明 |
标准数据类型
Python3 中有六个标准的数据类型:
- Number(数字)
- String(字符串)
- List(列表)
- Tuple(元祖)
- Set(集合)
- Dictionary(字典)
不可变数据(3 个):Number(数字)、String(字符串)、Tuple(元组);
可变数据(3 个):List(列表)、Dictionary(字典)、Set(集合)。
Number(数字)
Python3 支持 int、float、bool、complex(复数)。
内置的 type() 函数可以用来查询变量所指的对象类型。
1 | bb = True |
此外还可以用 isinstance 来判断。
1 | print(isinstance(bb, int)) #True |
isinstance 和 type 的区别在于:
- type()不会认为子类是一种父类类型。
- isinstance()会认为子类是一种父类类型。
注意:在 Python2 中是没有布尔型的,它用数字 0 表示 False,用 1 表示 True。到 Python3 中,把 True 和 False 定义成关键字了,但它们的值还是 1 和 0,它们可以和数字相加。
数值运算
数值的除法包含两个运算符:/ 返回一个浮点数,// 返回一个整数。
用 ** 来进行乘方操作
1 | 2 / 4 # 除法,得到一个浮点数 |
String(字符串)
Python中的字符串用单引号 ''
或双引号 ""
括起来,同时使用反斜杠 \
转义特殊字符。
1 | str = 'Runoob' |
输出结果:
1 | Runoob |
Python 使用反斜杠 \
转义特殊字符,如果你不想让反斜杠发生转义,可以在字符串前面添加一个 r
,表示原始字符串:
1 | print('gs\ngs') |
输出结果:
1 | gs |
与 C 字符串不同的是,Python 字符串不能被改变。向一个索引位置赋值,比如word[0] = ‘m’会导致错误。
List(列表)
List(列表) 是 Python 中使用最频繁的数据类型。
列表可以完成大多数集合类的数据结构实现。列表中元素的类型可以不相同,它支持数字,字符串甚至可以包含列表(所谓嵌套)。
列表是写在方括号 [] 之间、用逗号分隔开的元素列表。
和字符串一样,列表同样可以被索引和截取,列表被截取后返回一个包含所需元素的新列表。
1 | list = ['abcd', 786, 2.23, 'runoob', 70.2] |
与Python字符串不一样的是,列表中的元素是可以改变的:
1 | list[1] = 111 |
Python 列表截取可以接收第三个参数,参数作用是截取的步长,以下实例在索引 1 到索引 4 的位置并设置为步长为 2(间隔一个位置)来截取字符串:
1 | dd = "h e l l o" |
List 内置了有很多方法,例如 append()、pop() 等等,后面再说。
Tuple(元祖)
元组(tuple)与列表类似,不同之处在于元组的元素不能修改。元组写在小括号 ()
里,元素之间用逗号隔开。
1 | myTuple = ('abcd', 786, 2.23, 'runoob', 70.2) |
元组与字符串类似,可以被索引且下标索引从0开始,-1 为从末尾开始的位置。
其实,可以把字符串看作一种特殊的元组。
虽然tuple的元素不可改变,但它可以包含可变的对象,比如list列表。
Set(集合)
集合(set)是由一个或数个形态各异的大小整体组成的,构成集合的事物或对象称作元素或是成员。
基本功能是进行成员关系测试和删除重复元素。
可以使用大括号 { } 或者 set() 函数创建集合,注意:创建一个空集合必须用 set()
而不是 { }
,因为 { }
是用来创建一个空字典。
创建格式:
1 | parame = {value01,value02,...} |
1 | sites = {'Google', 'Taobao', 'Runoob', 'Facebook', 'Zhihu', 'Zhihu', 'Baidu'} |
列表和元祖内的元素可重复 ,但是集合内的元素重复的话会自动删除重复的元素,并且每次输出集合的元素,顺序不一样
成员测试
1 | if 'Taobao' in sites: |
集合运算
1 | a = set('abracadabra') |
输出结果:
1 | {'d', 'b', 'r'} |
Dictionary(字典)
字典(dictionary)是Python中另一个非常有用的内置数据类型。
列表是有序的对象集合,字典是无序的对象集合。两者之间的区别在于:字典当中的元素是通过键来存取的,而不是通过偏移存取。
字典是一种映射类型,字典用 { } 标识,它是一个无序的 键(key) : 值(value) 的集合。
键(key)必须使用不可变类型。
在同一个字典中,键(key)必须是唯一的。
1 | person = {'name': 'zhangsan', 'age': 20, 'email': '123@qq.com'} |
输出结果:
1 | {'name': 'zhangsan', 'age': 20, 'email': '123@qq.com'} |
Python数据类型转换
函数 | 描述 |
---|---|
int(x [,base]) | 将x转换为一个整数,base为进制数,默认为2 |
float(x) | 将x转换到一个浮点数 |
complex(real[,imag]) | 创建一个复数 |
str(x) | 将对象 x 转换为字符串 |
repr(x) | 将对象 x 转换为表达式字符串 |
eval(str) | 用来计算在字符串中的有效Python表达式,并返回一个对象 |
tuple(s) | 将序列 s 转换为一个元组 |
list(s) | 将序列 s 转换为一个列表 |
set(s) | 转换为可变集合 |
dict(d) | 创建一个字典。d 必须是一个 (key, value)元组序列。 |
frozenset(s) | 转换为不可变集合 |
chr(x) | 将一个整数转换为一个字符 |
ord(x) | 将一个字符转换为它的整数值 |
hex(x) | 将一个整数转换为一个十六进制字符串 |
oct(x) | 将一个整数转换为一个八进制字符串 |
1 | 1) float( |
输入和输出
输出
格式符号 | 转换 |
---|---|
%s | 字符串 |
%d | 有符号的十进制数 |
%f | 浮点数 |
%c | 字符 |
%u | 无符号的十进制数 |
%o | 八进制整数 |
%x | 十六进制整数(小写ox) |
%X | 十六进制整数(大写OX) |
%e | 科学计数法(小写e) |
%E | 科学计数法(大写E) |
%g | %f和%e的简写 |
%G | %f和%E的简写 |
技巧
- %04d,表示输出的整数为4位,前面不足的用0补全;超出4位,则原样输出
- %.2f,表示小数点后显示的小数位数
- 格式化字符串除了%s,还可用
f'{表达式}'
1 | name = 'Tom' |
输出结果:
1 | 我的名字是:Tom |
输入
1 | password = input('请输入您的密码:') |
- 一般将input接收的数据存储到变量中
- input接收的任何数据类型默认都是字符串数据类型
运算符
Python语言支持以下类型的运算符:
- 算术运算符
- 比较(关系)运算符
- 赋值运算符
- 逻辑运算符
- 位运算符
- 成员运算符
- 身份运算符
算术运算符
运算符 | 描述 | 实例 |
---|---|---|
+ | 加 | |
- | 减 | |
* | 乘 | |
/ | 除 | |
% | 取余 | |
** | 乘方 | 2 ** 3 = 8 |
// | 取整除,向下接近商的整数 | 5 // 2 = 2 |
比较运算符
==
、!=
、>
、<
、 >=
、 <=
赋值运算符
=
、+=
、-=
、*=
、 /=
、 %=
、 **=
、//=
:=
:海象运算符,可在表达式内部为变量赋值。Python3.8 版本新增运算符。
位运算符
按位运算符是把数字看作二进制来进行计算的。
先上代码:
1 | a = 60 # 60 = 0011 1100 |
Python中的按位运算法则如下:
运算符 | 描述 | 实例 |
---|---|---|
& | 按位与运算符 | (a & b) 输出结果 12 ,二进制解释: 0000 1100 |
| | 按位或运算符 | (a | b)输出结果 61 ,二进制解释:0011 1101 |
^ | 按位异或运算符 | (a ^ b) 输出结果 49 ,二进制解释: 0011 0001 |
~ | 按位取反运算符 | (~a ) 输出结果 -61 ,二进制解释: 1100 0011 |
<< | 左移动运算符 | a << 2 输出结果 240 ,二进制解释: 1111 0000 |
>> | 右移动运算符 | a >> 2 输出结果 15 ,二进制解释: 0000 1111 |
在python中对一个数进行取反运算,得到的值(1100 0011)是我们要求的数的补码,现在问题变成了 一个数x的补码等于1100 0011 我们要求这个x,x的符号由左边第一位确定,如果为1则为负数,为0则为正数
求一个数的补码,是对这个数取反,再加一,得到原码,1100 0011的补码 0011 1100再加一,得到 0011 1101也就是61,加上负号就是-64
逻辑运算符
Python语言支持逻辑运算符,以下假设变量 a 为 10, b为 20:
运算符 | 逻辑表达式 | 描述 | 实例 |
---|---|---|---|
and | x and y | 布尔”与” - 如果 x 为 False,x and y 返回 False,否则它返回 y 的计算值。 | (a and b) 返回 20 |
or | x or y | 布尔”或” - 如果 x 是 True,它返回 x 的值,否则它返回 y 的计算值。 | (a or b) 返回 10 |
not | not x | 布尔”非” - 如果 x 为 True,返回 False 。如果 x 为 False,它返回 True。 | not(a and b)返回False |
成员运算符
除了以上的一些运算符之外,Python还支持成员运算符,测试实例中包含了一系列的成员,包括字符串,列表或元组。
运算符 | 描述 | 实例 |
---|---|---|
in | 如果在指定的序列中找到值返回 True,否则返回 False。 | x 在 y 序列中 , 如果 x 在 y 序列中返回 True。 |
not in | 如果在指定的序列中没有找到值返回 True,否则返回 False。 | x 不在 y 序列中 , 如果 x 不在 y 序列中返回 True。 |
1 | myList = [1, 2, 3, 4, 5] |
身份运算符
身份运算符用于比较两个对象的存储单元
运算符 | 描述 | 实例 |
---|---|---|
is | is 是判断两个标识符是不是引用自一个对象 | x is y, 类似 id(x) == id(y) , 如果引用的是同一个对象则返回 True,否则返回 False |
is not | is not 是判断两个标识符是不是引用自一个对象 | x is not y ,类似 id(a) != id(b)。如果引用的不是同一个对象则返回结果 True,否则返回 False。 |
注: id() 函数用于获取对象内存地址。
is 与 == 区别:
is 用于判断两个变量引用对象是否为同一个, == 用于判断引用变量的值是否相等。
1 | myList1 = [1, 2, 3, 4, 5] |
条件语句
if语句
1 | i = 1 |
Python 中用 elif 代替了 else if,所以if语句的关键字为:if – elif – else。
注意:
- 1、每个条件后面要使用冒号 :,表示接下来是满足条件后要执行的语句块。
- 2、使用缩进来划分语句块,相同缩进数的语句在一起组成一个语句块。
- 3、在Python中没有switch – case语句。
while语句
求1到100的和
1 | i = 1 |
在 Python 中没有 do..while 循环。
为什么python为什么不能使用 i++ ?
Python 中的可迭代对象/迭代器/生成器提供了非常良好的迭代/遍历用法,能够做到对 i++ 的完全替代。
for语句和range()函数
如果你需要遍历数字序列,可以使用内置range()函数。它会生成数列,例如:
1 | for x in range(6): |
可以使用range指定区间的值,并指定不同的增量(甚至可以是负数,有时这也叫做’步长’)
字符串
1 | name = 'zhangsan' |
输出结果:
1 | zhangsan |
三引号的字符串支持换行
查找
find() : 检测某个子串是否包含在这个字符串中,如果在则返回子串开始的位置下标,否则返回-1
语法:字符串序列.find(子串 [, 开始位置下标, 结束位置下标])
index() : 检测某个子串是否包含在这个字符串中,如果在则返回子串开始的位置下标,否则报异常
语法和 find() 相同
1 | words = 'str : hello world and hello world' |
rfind() : 和 find() 功能相同,但查找方向从右侧开始
rindex() : 和 index() 功能相同,但查找方向从右侧开始
count() : 返回某个子串在字符串中出现的次数
语法:字符串序列.count(子串 [, 开始位置下标, 结束位置下标])
1 | print(words.count('hello')) # 2 |
修改
replace() : 替换
语法:字符串序列.replace( 旧子串 , 新子串 , [, 替换次数])
1
2print(words.replace('and', 'ands')) # str : hello world ands hello world
print(words) # str : hello world and hello world如果替换次数超出,则替换次数为最大可替换次数
字符串为不可变类型,因此不会修改原字符串
split() : 按照指定字符分割字符串
语法:字符串序列.split( 分割字符 [, num])
1
2myList = words.split(' ')
print(myList) # ['str', ':', 'hello', 'world', 'and', 'hello', 'world']join() : 用一个字符或子串合并字符串,即是将多个字符串合并为一个新的字符串
语法:字符或子串.join(多字符串组成的序列)
1
print('-'.join(myList)) # str-:-hello-world-and-hello-world
capitalize() : 将字符串第一个字母转换成大写,其他的字符全部小写
title() : 将字符串每个单词的首字母转换成大写
lower() : 将字符串中的大写转换成小写
upper() : 将字符串中的小写转换成大写
strip() : 删除字符串两侧空白字符
lstrip() : 删除字符串左侧空白字符
rstrip() : 删除字符串右侧空白字符
center() : 填充字符使原字符居中,默认填充为空格
语法:字符串序列.cneter( 长度 [, 填充字符])
1
2str = 'hello'
print(str.center(10, '.')) # ..hello...ljust() : 填充字符使原字符左对齐,默认填充为空格,语法和
center()
一样rjust() : 填充字符使原字符右对齐,默认填充为空格,语法和
center()
一样
判断
- startswith() : 检查字符串是否以指定字符串开头
- endswith() : 检查字符串是否以指定字符串结尾
- isalpha() : 如果字符串至少有一个字符且所有字符全都是字母返回True,否则False
- isdigit() : 如果字符串至少有一个字符且所有字符全都是数字返回True,否则False
- isalnum() : 如果字符串至少有一个字符且所有字符全都是字母或者数字返回True,否则False
- isspace() : 如果字符串中至包含空白,则返回True,否则返回False
列表
列表的格式:
1 | [数据1, 数据2, 数据3, 数据4, ...] |
列表可以一次性存储多个数据,且可以为不同的数据类型
查找
下标对应值
1 | myList = ['Tom', 'Lily', 'Rose', 'Lily'] |
函数:
index() : 返回指定数据所在位置的下标
1
print(myList.index('Lily')) # 1
count() : 统计指定数据在当前列表出现的次数
1
print(myList.count('Lily')) # 2
len() : 列表长度
1
print(len(myList)) # 4
判断是否存在
in : 判断指定数据是否在某个列表序列,若在返回True,否则返回False
not in : 判断指定数据是否不在某个列表序列,若不在返回True,否则返回False
1
2print('Tom' in myList) # True
print('Tom' not in myList) # False
增加
作用:增加指定数据到列表中
append() : 列表结尾增加数据
1
2myList.append('xiaoming')
print(myList) # ['Tom', 'Lily', 'Rose', 'Lily', 'xiaoming']注意点:
如果append增加的是一个序列,则会追加整个序列到列表
1
2
3
4name_list = ['Tom', 'Lily', 'Rose']
other_list = ['zhangsan', 'lisi']
name_list.append(other_list)
print(name_list) # ['Tom', 'Lily', 'Rose', ['zhangsan', 'lisi']]extend() : 列表结尾增加数据,如果末尾增加的是一个序列,则会追加这个序列的数据逐一添加到列表
1
2
3
4name_list = ['Tom', 'Lily', 'Rose']
other_list = ['zhangsan', 'lisi']
name_list.extend(other_list)
print(name_list) # ['Tom', 'Lily', 'Rose', 'zhangsan', 'lisi']insert() : 指定位置新增数据
1
2
3name_list = ['Tom', 'Lily', 'Rose']
name_list.insert(1, 'xiaoming')
print(name_list) # ['Tom', 'xiaoming', 'Lily', 'Rose']
删除
del
删除整个列表
1
2
3name_list = ['Tom', 'Lily', 'Rose']
del name_list
print(name_list) # 会报错:NameError: name 'name_list' is not defined删除指定数据
1
2
3name_list = ['Tom', 'Lily', 'Rose']
del name_list[1]
print(name_list) # ['Tom', 'Rose']
pop() : 删除指定下标的数据(默认为最后一个),并返回该数据
1
2
3
4
5name_list = ['Tom', 'Lily', 'Rose', 'xiaoming']
name_list.pop()
print(name_list) # ['Tom', 'Lily', 'Rose']
name_list.pop(0)
print(name_list) # ['Lily', 'Rose']remove() : 移除列表中某个数据的第一个配置项
1
2
3name_list = ['Tom', 'Lily', 'Rose', 'xiaoming', 'Lily']
name_list.remove('Lily')
print(name_list) # ['Tom', 'Rose', 'xiaoming', 'Lily']clear() : 清空列表
1
2
3name_list = ['Tom', 'Lily', 'Rose', 'xiaoming', 'Lily']
name_list.clear()
print(name_list) # []
修改
修改指定下标元素
1
2
3name_list = ['Tom', 'Lily', 'Rose']
name_list[0] = 'David'
print(name_list) # ['David', 'Lily', 'Rose']逆置:reverse()
1
2
3name_list = ['Tom', 'Lily', 'Rose']
name_list.reverse()
print(name_list) # ['Rose', 'Lily', 'Tom']排序:sort()
1
2
3num_list = [2, 8, 4, 6]
num_list.sort()
print(num_list) # [2, 4, 6, 8]
复制
函数:copy()
1 | name_list = ['Tom', 'Lily', 'Rose'] |
元祖
一个元祖可以存储多个元素,元祖内的数据是不能修改的
定义元祖
1 | t1 = (10, 20, 30) |
如果定义单个元祖,最好在这个数据后面添加一个 逗号 ,
,否则数据类型为唯一的这个数据类型
1 | t2 = ('hello') |
常见操作
- 按下标查找数据
- index() : 查找某个数据,返回对应下标,否则报错
- count() : 统计某个数据在元祖中出现的次数
- len() : 统计元祖中数据的个数
注意:元祖内数据直接修改会立即报错,如果元祖里有列表,修改列表中的数据则是支持的
1 | t5 = ('aa', 'bb', ['cc', 'dd', 'ee'], 'ff') |
集合
创建集合
创建集合使用 {}
和 set()
,但如果要创建空集合只能使用 set()
,因为 {}
用来创建空字典
1 | s1 = {10, 20, 30, 10} |
特点:
- 集合可以去掉重复元素
- 集合是无序的,所以不支持下标
增加数据
add() : 追加数据
1
2
3
4s5 = {10, 20, 30}
s5.add(40)
s5.add(10)
print(s5) # {40, 10, 20, 30}update() : 追加数据,追加的数据必须是序列
1
2
3s6 = {10, 20, 30}
s6.update(['a', 'b', 'c'])
print(s6) # {10, 'b', 20, 'c', 'a', 30}
删除数据
remove() : 删除集合中的指定数据,如果数据不存在则报错
discard() : 删除集合中的指定数据,如果数据不存在也不报错
pop() : 随机删除集合中的某个数据,并返回这个数据
1
2
3
4
5
6
7
8
9
10
11s7 = {10, 20, 30, 40, 50, 60}
s7.remove(60)
print(s7) # {40, 10, 50, 20, 30}
# s7.remove(70) # 报错:KeyError: 50
s7.discard(50)
s7.discard(70) # 不会报错
print(s7) # {40, 10, 20, 30}
s7.pop()
print(s7) # {10, 20, 30}
查找元素
- in : 判断数据在集合序列
- not in : 判断数据不在集合序列
字典
字典里面的数据是以键值对形式出现,字典数据和数据顺序没有关系,即字典不支持下标,后期无论数据这怎样变化,只需要按照对应的键的名字查找数据即可
创建字典
1 | d1 = {'name': 'Tom', 'age': 20, 'gender': '男'} |
增
写法:字典序列[key] = 值
注意:如果key值存在则修改这个key对应的值;如果key值不存在则新增这个键值对
1 | d2 = {'name': 'Tom', 'age': 20, 'gender': '男'} |
删
del() / del : 删除字典或字典中指定键值对
clear() : 清空字典
1
2
3
4
5d3 = {'name': 'Tom', 'age': 20, 'gender': '男'}
del d3['age']
print(d3) # {'name': 'Tom', 'gender': '男'}
d3.clear()
print(d3) # {}
改
增的那一节:字典序列[key] = 值,key值存在则更新,不存在则添加
查
key值查找
如果当前查找的key值存在,则返回对应的值;否则则报错
1 | d4 = {'name': 'Tom', 'age': 20, 'gender': '男'} |
get()
语法:字典序列.get(key [, 默认值])
如果查找的key值不存在,则返回第二个参数默认值,如果没有第二个参数,则返回 None
1 | d5 = {'name': 'Tom', 'age': 20, 'gender': '男'} |
keys()
以列表返回一个字典所有的键。
1 | d6 = {'name': 'Tom', 'age': 20, 'gender': '男'} |
values()
以列表返回一个字典所有的值。
1 | d7 = {'name': 'Tom', 'age': 20, 'gender': '男'} |
items()
以列表返回可遍历的(键, 值) 元组数组。
1 | d8 = {'name': 'Tom', 'age': 20, 'gender': '男'} |
遍历
遍历key:dict.keys()
遍历value:dict.values()
遍历元素:dict.items()
遍历键值对:for key, value in dict.items():
1
2
3d9 = {'name': 'Tom', 'age': 20, 'gender': '男'}
for key, value in d9.items():
print(f'{key} = {value}')输出结果:
1
2
3name = Tom
age = 20
gender = 男
公共操作
运算符
运算符 | 描述 | 支持的容器类型 |
---|---|---|
+ | 合并 | 字符串、列表、元祖 |
* | 复制 | 字符串、列表、元祖 |
in | 元素是否存在 | 字符串、列表、元祖、字典 |
not in | 元素是否不存在 | 字符串、列表、元祖、字典 |
1 | str1 = 'Hello' |
公共方法
函数 | 描述 |
---|---|
len() | 计算容器中元素个数 |
del 或 del() | 删除 |
max() | 返回容器中元素最大值 |
min() | 返回容器中元素最小值 |
range(start, end, step) | 生成从start到end的数字,步长为step,供for循环使用 |
enumerate() | 函数用于将一个可遍历的数据对象(如列表、元祖或字符串)组成一个索引序列,同时列出数据和数据下标,一般用在for循环中 |
前面几个都很容易理解,这里重点介绍一下最后一个方法:
1 | list3 = ['Tom', 'Lily', 'June'] |
输出结果都为:
1 | 0->Tom |
还可以为enumerate()方法设置start属性:
1 | # enumerate,设置start属性 |
输出结果:
1 | 1->Tom |
容器类型转换
- tuple() : 将某个序列转换成元祖
- list() : 将某个序列转换成列表
- set() : 将某个序列转换成集合
注意:
- 集合可以快速完成列表去重
- 集合不支持下标
推导式
推导式的作用:简化代码
列表推导式
作用:用一个表达式创建一个有规律的列表或控制一个有规律列表
列表推导式又叫列表生成式
1 | # 构建一个 0-9 的列表 |
字典推导式
1 | # 创建一个字典,key是1-4数字,value是key的平方 |
集合推导式
1 | tuple1 = {i**2 for i in range(1, 5)} |
和列表推导式差不多,集合推导式具有去重功能
函数
在Python中,函数必须先定义再使用
1 | def sum(a, b): |
参数传递
在Python中,类型属于对象,变量是没有类型的
1 | a = [1, 2, 3] |
以上代码中,[1,2,3] 是 List 类型,“hello” 是 String 类型,而变量 a 是没有类型,她仅仅是一个对象的引用(一个指针),可以是 List 类型对象,也可以指向 String 类型对象。
在 Python 中,strings, tuples, 和 numbers 是不可更改的对象,而 list,dict 等则是可以修改的对象。
1 | def changeInt(a): |
1 | def changeList(myList): |
参数
必备参数
必备参数须以正确的顺序传入函数。调用时的数量必须和声明时的一样。
调用printStr()函数,你必须传入一个参数,不然会出现语法错误:
1
2
3
4def printStr(str):
print(str)
return
printStr() # 会报错:TypeError: printStr() missing 1 required positional argument: 'str'关键字参数
关键字参数和函数调用关系紧密,函数调用使用关键字参数来确定传入的参数值。
使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值。
1
2
3
4def printInfo(name, age):
print(f'姓名:{name},年龄:{age}')
return
printInfo(age=20, name='Tom') # 姓名:Tom,年龄:20默认参数
1
2
3
4
5def printInfo2(name, age = 30):
print(f'姓名:{name},年龄:{age}')
return
printInfo2(name='Tom') # 姓名:Tom,年龄:30
printInfo2('July', 20) # 姓名:July,年龄:20不定长参数
1
2
3
4def info(*args):
print(args)
return
info('Tom', 'Lily', 'June') # ('Tom', 'Lily', 'June')传递进来的所有参数都会被args变量收集,它会根据传进参数的位置合并为一个元祖(tuple)
1
2
3
4def info2(**args):
print(args)
return
info2(name='Tom', age=20) # {'name': 'Tom', 'age': 20}
匿名函数
Python 使用 lambda 来创建匿名函数。
- lambda只是一个表达式,函数体比def简单很多。
- lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
- lambda函数拥有自己的命名空间,且不能访问自有参数列表之外或全局命名空间里的参数。
- 虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。
1 | sum = lambda arg1, arg2: arg1 + arg2 |
体验高阶函数
把函数作为参数传入,这样的函数被称为高阶函数,高阶函数是函数式编程的体现。
需求:一个函数完成任意两个数字的绝对值之和
方法一
1
2
3def add_nums1(a, b):
return abs(a) + abs(b)
print(add_nums1(10, -10)) # 20方法二
1
2
3def add_nums2(a, b, f):
return f(a) + f(b)
print(add_nums2(10, -10, abs)) # 20
明显第二种方法的代码会更加简洁
函数式编程大量使用函数,减少了代码的重复
内置高阶函数
map()
map(func, lst),将传入的函数变量func作用到lst变量的每个元素中,并将结果组成新的列表(Python2)/迭代器(Python3)返回
需求:计算list1序列中每个数字的平方
1
2
3
4
5
6
7
8
9list1 = [1, 2, 3, 4]
def func(x):
return x ** 2
result = map(func, list1)
print(result) # <map object at 0x000001DEA2123F98>
print(list(result)) # [1, 4, 9, 16]reduce()
reduce(func(x, y), lst),其中func必须有两个参数。每次func计算的结果继续和序列的下一个元素做累积运算
需求:计算list1序列中各个数字的累加和
1
2
3
4
5
6
7
8import functools
list2 = [1, 2, 3, 4]
def func(a, b):
return a + b
result = functools.reduce(func,list2)
print(result) # 10filter()
filter(func, lst)函数用于过滤序列,过滤掉不符合条件的元素,返回一个filter对象。如果要转换成列表,可以用list() 来转换
1
2
3
4
5
6
7
8list3 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
def func(x):
return x % 2 ==0
result = filter(func, list3)
print(result) # <filter object at 0x000001FC68C7B9B0>
print(list(result)) # [2, 4, 6, 8, 10]
文件操作
打开文件
open 函数:你必须先用Python内置的open()函数打开一个文件,创建一个file对象,相关的方法才可以调用它进行读写。语法:
1 | file object = open(file_name [, access_mode][, buffering]) |
各个参数的细节如下:
- file_name:file_name变量是一个包含了你要访问的文件名称的字符串值
- access_mode:access_mode决定了打开文件的模式:只读,写入,追加等。所有可取值见如下的完全列表。这个参数是非强制的,默认文件访问模式为只读(r)
- buffering:如果buffering的值被设为0,就不会有寄存。如果buffering的值取1,访问文件时会寄存行。如果将buffering的值设为大于1的整数,表明了这就是的寄存区的缓冲大小。如果取负值,寄存区的缓冲大小则为系统默认
不同模式打开文件的完全列表:
模式 | 描述 |
---|---|
t | 文本模式 (默认)。 |
x | 写模式,新建一个文件,如果该文件已存在则会报错。 |
b | 二进制模式。 |
+ | 打开一个文件进行更新(可读可写)。 |
U | 通用换行模式(不推荐)。 |
r | 以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。 |
rb | 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。一般用于非文本文件如图片等。 |
r+ | 打开一个文件用于读写。文件指针将会放在文件的开头。 |
rb+ | 以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。一般用于非文本文件如图片等。 |
w | 打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 |
wb | 以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。 |
w+ | 打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 |
wb+ | 以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。 |
a | 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 |
ab | 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 |
a+ | 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。 |
ab+ | 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。 |
模式 | r | r+ | w | w+ | a | a+ |
---|---|---|---|---|---|---|
读 | + | + | + | |||
写 | + | + | + | + | + | |
创建 | + | + | + | + | ||
覆盖 | + | + | ||||
指针在开始 | + | + | + | + | ||
指针在结尾 | + | + |
File对象的操作
close() 方法
File 对象的 close()方法刷新缓冲区里任何还没写入的信息,并关闭该文件,这之后便不能再进行写入
read() 方法
read()方法从一个打开的文件中读取一个字符串。需要重点注意的是,Python字符串可以是二进制数据,而不是仅仅是文字。
语法:
1
fileObject.read([count])
在这里,被传递的参数是要从已打开文件中读取的字节计数。该方法从文件的开头开始读入,如果没有传入count,它会尝试尽可能多地读取更多的内容,很可能是直到文件的末尾
write() 方法
write()方法可将任何字符串写入一个打开的文件。需要重点注意的是,Python字符串可以是二进制数据,而不是仅仅是文字
文件定位
tell() 方法
告诉你文件内的当前位置, 换句话说,下一次的读写会发生在文件开头这么多字节之后。
比如我的
test.txt
文件中就hello
这五个字节构成的字符串,那么如果以a
的模式打开这个文件,调用它的tell()
方法,返回值为:51
2
3f = open('test.txt', 'a')
print(f.tell()) # 5
f.close()seek(offset [,from]) 方法
改变当前文件的位置。Offset变量表示要移动的字节数。From变量指定开始移动字节的参考位置
如果from被设为0,这意味着将文件的开头作为移动字节的参考位置。如果设为1,则使用当前的位置作为参考位置。如果它被设为2,那么该文件的末尾将作为参考位置
例:现在我的 test.txt
文件中就 hello world!
这12个字节构成的字符串,以 a+
的模式打开这个文件
1 | f = open('test.txt', 'a+') |
重命名和删除文件
Python的os模块提供了帮你执行文件处理操作的方法,比如重命名和删除文件。
rename() 方法需要两个参数,当前的文件名和新文件名。
1 | os.rename(current_file_name, new_file_name) |
你可以用 remove() 方法删除文件,需要提供要删除的文件名作为参数。
1 | os.remove(file_name) |
例:把 test1.txt
重命名为 test2.txt
,在将其删除
1 | import os |
目录相关
os模块有许多方法能帮你创建,删除和更改目录。
mkdir() 方法
在当前目录下创建新的目录
chdir() 方法
改变当前的目录
getcwd() 方法
显示当前的工作目录
rmdir() 方法
删除目录
listdir() 方法
列出所有文件,包括文件夹
例:在项目根目录下创建一个 test
的文件夹,并改变当前目录到 test
文件夹下面,输出当前目录,再改变到项目根目录回来,删除这个 test
文件夹
1 | import os |
面向对象——基础
面向对象技术简介
- 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
- 类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
- 数据成员:类变量或者实例变量, 用于处理类及其实例对象的相关的数据。
- 方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
- 局部变量:定义在方法中的变量,只作用于当前实例的类。
- 实例变量:在类的声明中,属性是用变量来表示的。这种变量就称为实例变量,是在类声明的内部但是在类的其他成员方法之外声明的。
- 继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟”是一个(is-a)”关系(例图,Dog是一个Animal)。
- 实例化:创建一个类的实例,类的具体对象。
- 方法:类中定义的函数。
- 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
类的创建
直接先上代码,创建一个 Person
类,有两个属性:name
和 age
1 | class Person: |
输出结果:
1 | <__main__.Person object at 0x0000024B6DCDBA20> |
在类的内部,使用 def
关键字可以为类定义一个方法,与一般函数定义不同,类方法必须包含参数 self
,且为第一个参数
先解释 self
这个词:是指调用该函数的对象,感觉和 Java
的 this
差不多
__init__
方法,在创建一个对象时默认调用,不需要手动调用,感觉和 Java
的构造方法差不多
我们还在类中定义了一个 print_info
方法,执行此方法会打印这个对象的相关信息
对象的创建直接用类名创建即可,不用 new
关键字等:p1 = Person('zhangsan', 20)
,创建时这个对象的 name
属性会被赋值为 zhangsan
,age
属性会被赋值为 20
除了 __init__()
方法,还有 __str__()
方法 和 __del__()
方法
__str__()
方法:当使用print输出对象时,默认打印对象的内存地址。如果类定义了__str__
方法,那么就会打印从这个方法里 return 的数据__del__()
方法:当删除对象时,python解释器会调用__del__
方法1
2
3
4
5
6
7
8
9
10
11
12class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f'这是一个Person对象,属性name为: {self.name},属性age为: {self.age}.'
def __del__(self):
print(f'名字为{self.name}的对象已被删除')
p1 = Person('zhangsan', 20)
print(p1)
del p1
输出结果:
1 | 这是一个Person对象,属性name为: zhangsan,属性age为: 20. |
访问属性
可以使用点号 .
来访问对象的属性。
也可以使用以下函数的方式来访问属性:
- getattr(obj, name[, default]) : 访问对象的属性。
- hasattr(obj,name) : 检查是否存在一个属性。
- setattr(obj,name,value) : 设置一个属性。如果属性不存在,会创建一个新属性。
- delattr(obj, name) : 删除属性。
内置类属性
__dict__
: 类的属性(包含一个字典,由类的数据属性组成)__doc__
:类的文档字符串__name__
: 类名__module__
: 类定义所在的模块(类的全名是’main.className’,如果类位于一个导入模块mymod中,那么className.module 等于 mymod)__bases__
: 类的所有父类构成元素(包含了一个由所有父类组成的元组)
1 | print(Person.__dict__) |
输出结果:
1 | {'__module__': '__main__', '__doc__': 'Person类', '__init__': <function Person.__init__ at 0x000001798C0689D8>, '__str__': <function Person.__str__ at 0x000001798C068AE8>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>} |
面向对象——继承
面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。
通过继承创建的新类称为子类或派生类,被继承的类称为基类、父类或超类。
1 | class Person: |
还可以继承多个类:
1 | class A: # 定义类 A |
我们还可以使用issubclass()或者isinstance()方法来检测。
- issubclass(sub,sup) :布尔函数判断一个类是另一个类的子类或者子孙类
- isinstance(obj, Class) :布尔函数如果obj是Class类的实例对象或者是一个Class子类的实例对象则返回true。
1 | print(issubclass(Student, Person)) # True |
方法重写
如果你的父类方法的功能不能满足你的需求,我们可以在子类重写你父类的方法
类的私有属性和私有方法
类的私有属性
__private_attrs:两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问。在类内部的方法中使用时 self.__private_attrs
类的私有方法
__private_method:两个下划线开头,声明该方法为私有方法,不能在类的外部调用。在类的内部调用 self.__private_methods
1 | class People: |
单下划线、双下划线、头尾双下划线说明:
- foo: 定义的是特殊方法,一般是系统定义名字 ,类似 init() 之类的。
- _foo: 以单下划线开头的表示的是 protected 类型的变量,即保护类型只能允许其本身与子类进行访问,不能用于 from module import *
- __foo: 双下划线的表示的是私有类型(private)的变量, 只能是允许这个类本身进行访问了。
类属性和对象属性
类属性就是 类对象 所拥有的属性,它被 该类所有实例对象 所共有
类属性可以通过 类对象 或 实例对象 访问
1 | class A: |
类方法和静态方法
类方法特点:
- 第一个形参是类对象的方法
- 需要使用装饰器
@classmethod
来标识其方法,对于类方法,第一个参数必须是类对象,一般以cls
作为第一个参数
使用场景:
- 当方法中 需要使用类对象(如访问私有属性等)时,定义类方法
- 类方法一般和类属性配合使用
1 | class Dog: |
静态方法特点:
- 需要通过装饰器
@staticmethod
来进行修饰,静态方法既不需要传递类对象也不需要传递实例对象(形参也没有self/cls) - 静态方法也可以通过 实例对象 和 类对象 去访问
使用场景:
- 当方法中 既不需要使用实例对象(如实例对象、实例属性),也不需要使用类对象(如类属性、类方法、创建实例等),定义静态方法
- 取消不需要的参数传递,有利于减少不必要的内存占用和性能消耗
1 | class Dog: |
异常
异常语法
1 | try: |
实例:
1 | try: |
输出结果:
1 | 出现了异常 |
捕获异常
1 | except 异常类型: |
实例:
1 | try: |
输出结果:
1 | division by zero |
自定义异常
在Python中,抛出自定义异常的语法为 raise 异常类对象
1 | # 1. 自定义异常 |
需求:密码长度不足,则报异常(用户提示输入密码,若不足三位,则报错,即抛出自定义异常,并捕获该异常)
1 | # 自定义异常类 |
输出结果:
1 | 请输入密码:1 |
1 | 请输入密码:123 |
模块和包
Python 模块(Module),是一个 Python 文件,以 .py 结尾,包含了 Python 对象定义 和 Python语句
模块能定义函数,类和变量,模块里也能包含可执行的代码
导入模块
方式:
- import 模块名
- from 模块名 import 功能名
- from 模块名 import *
- import 模块名 as 别名
- from 模块名 import 功能名 as 别名
1 | # import 模块名 |
1 | # from 模块名 import 功能名 |
1 | # from 模块名 import * |
1 | # import 模块名 as 别名 |
1 | # from 模块名 import 功能名 as 别名 |
制作模块
新建一个 my_module1.py
的文件:
1 | def test1(a, b): |
另外主函数里面进行调用:
1 | import my_module1 as m1 |
__all__
如果一个模块文件中有 __all__
变量,当使用 from xxx import *
导入时,只能导入这个列表中的元素
my_module1.py
的文件:
1 | __all__ = 'test' |
主函数里面代码:
1 | from my_module1 import * |
会报错:
1 | Traceback (most recent call last): |
导入包
在Python项目根目录中新建 Python Package
,新建包,包内部会自动创建 __init__.py
文件,这个文件控制着包的导入行为
我们新建一个名字为 my_package
的包,里面创建两个模块,模块内代码如下:
1 | # my_module1.py |
1 | # my_module2.py |
导入包的两种方式:
import 包名.模块名
1
2import my_package.my_module1 as m
m.test1(10, 20) # 30from 包名 import *
先要在包内的
__init__.py
里面声明:__all__ = ['my_module1']
,意思是只能导入my_module1
这个模块包1
2
3from my_package import *
my_module1.test1(10, 20) # 30
# my_module2.test2(10, 20) # 编译不能通过