Python基础
一、Python入门
1、Python基本概念
- 表达式
- 表达式就是一个类似于数学公式的东西
- 比如:10 + 5 8 - 4
- 表达式一般仅仅用了计算一些结果,不会对程序产生实质性的影响
- 如果在交互模式中输入一个表达式,解释器会自动将表达式的结果输出
- 语句
- 在程序中语句一般需要完成某种功能,比如打印信息、获取信息、为变量赋值。。。
- 比如:
- print()
- input()
- a = 10
- 在交互模式中不一定会输出语句的执行结果
- 程序(program)
- 程序就是由一条一条的语句和一条一条的表达式构成的。
- 函数(function)
- 函数就是一种语句,函数专门用来完成特定的功能
- 函数长的形如:xxx()
- 函数的分类:
- 内置函数:
- 由Python解释器提供的函数,可以在Python中直接使用
- 自定义函数:
- 由程序员自主的创建的函数
- 内置函数:
- 当我们需要完成某个功能时,就可以去调用内置函数,或者自定义函数
- 函数的两个要素:
- 参数:
- ()中的内容就是函数的参数
- 函数中可以没有参数,也可以有多个参数,多个参数之间使用,隔开
- 返回值:- 返回值是函数的返回结果,不是所有的函数都有返回值
- 参数:
2、基本语法
在Python中严格区分大小写
Python中的每一行就是一条语句,每条语句以换行结束,语句后面不需要加 ; 结束,换行就结束了
Python中每一行语句不要过长(规范中建议每行不要超过80个字符)
- “rulers”:[80],
一条语句可以分多行编写,多行编写时语句后边以\结尾
1
2
3
4
5
6
7
8
9
10#一条赋值语句多行编写,\就像Java中的 + 一样
'''
Java中一条语句多行写
String str="how " +
"are " +
"you"
'''
a: str = "how " \
"are " \
"you"
Python是缩进严格的语言,所以在Python中不要随便写缩进
在Python中使用【 # 、’’’ ‘’’ 、””” 这是文档注释 “””】来表示注释,#后的内容都属于注释,注释的内容将会被解释器所忽略
我们可以通过注释来对程序进行解释说明,一定要养成良好的编写注释的习惯
注释要求简单明了,一般习惯上#后边会跟着一个空格
1
2
3
4
5
6
7
8
9""""
我是多行注释
"""
''''
我是多行注释
'''
#我是单行注释
3、字面量和变量
- 字面量就是一个一个的值,比如:1,2,3,4,5,6,‘HELLO’
- 字面量所表示的意思就是它的字面的值,在程序中可以直接使用字面量
- 变量(variable)变量可以用来保存字面量,并且变量中保存的字面量是不定的
- 变量本身没有任何意思,它会根据不同的字面量表示不同的意思
- 一般我们在开发时,很少直接使用字面量,都是将字面量保存到变量中,通过变量来引用字面量
4、变量和标识符
变量
- Python中使用变量,不需要声明,直接为变量赋值即可(如:a=10)
- 不能使用没有进行过赋值的变量
- 如果使用没有赋值过的变量,会报错:NameError: name ‘a’ is not defined(变量a没有定义)
- Python是一个动态类型的语言,可以为变量赋任意类型的值,也可以任意修改变量的值
标识符
- 在Python中所有可以自主命名的内容都属于标识符
- 比如:变量名、函数名、类名
- 标识符必须遵循标识符的规范
- 标识符中可以含有字母、数字、_,但是不能使用数字开头
- 例如:例子:a_1 _a1 _1a
- 标识符不能是Python中的关键字和保留字
- 也不建议使用Python中的函数名作为标识符,因为这样会导致函数被覆盖
- 命名规范:
- 在Python中注意遵循两种命名规范:
- 下划线命名法
- 所有字母小写,单词之间使用_分割
- max_length min_length hello_world xxx_yyy_zzz
- 帕斯卡命名法(大驼峰命名法)
- 首字母大写,每个单词开头字母大写,其余字母小写
- MaxLength MinLength HelloWorld XxxYyyZzz
- 下划线命名法
- 在Python中注意遵循两种命名规范:
- 标识符中可以含有字母、数字、_,但是不能使用数字开头
- 如果使用不符合标准的标识符,将会报错 SyntaxError: invalid syntax
5、数据类型
1 | 数据类型指的就是变量的值得类型,也就是可以为变量赋哪些值 |
在Python数值分成了三种:整数、浮点数(小数)、复数
在Python中所有的整数都是int类型
1
2a = 20
b = 10Python中的整数的大小没有限制,可以是一个无限大的整数
1
c = 999999999999999999999999999999999999999999999 ** 100
如果数字的长度过大,可以使用下划线作为分隔符
1
c = 123_456_789
d = 0123、 10进制的数字不能以0开头
其他进制的整数,只要是数字打印时一定是以十进制的形式显示的
二进制 0b开头
1
c = 0b10 # 二进制的10
八进制 0o开头
1
c = 0o10
十六进制 0x开头
1
c = 0x10
也可以通过运算符来对数字进行运算,并且可以保证整数运算的精确
浮点数(小数),在Python中所有的小数都是float类型
对浮点数进行运算时,可能会得到一个不精确的结果
6、字符串(str)
字符串用来表示一段文本信息,字符串是程序中使用的最多的数据类型
在Python中字符串需要使用引号引起来,如果不引起来的话,会被当成是一个变量、例如:
- 引号可以是双引号,也可以是单引号,但是注意不要混着用(不能一个单引号和一个双引号,’ “ )
单引号里面可以使用双引号,双引号里面可以使用单引号,单双引号不会跨行找引号
1
2
3
4#此时系统会以为在abc为一个变量,而此时还没有定义abc,所以就会报异常
a=abc
#需要使用如下方式定义,使用单引号或双引号都可以给变量赋值为字符串
a='abc' 或 a="abc"
对于长字符串
长字符串
单引号和双引号不能跨行使用
使用三重引号来表示一个长字符串 ‘’’ “””
三重引号可以换行(单双都可以),并且会保留字符串中的格式
1
2
3
4s = '''锄禾日当午,
汗滴禾下土,
谁知盘中餐,
粒粒皆辛苦'''
转义字符
可以使用 \ 作为转义字符,通过转义字符,可以在字符串中使用一些特殊的内容
例子:
\ ‘ 表示’
\ “ 表示”
\t 表示制表符
\n 表示换行符
\ \ 表示反斜杠
\uxxxx 表示Unicode编码
7、格式化字符串
字符串之间也可以进行加法运算
如果将两个字符串进行相加,则会自动将两个字符串拼接为一个
字符串相加使用方法
1
2a = 'abc' + 'haha' + '哈哈'
print(a) #输出结果为:abchaha哈哈
字符串不能和其他的类型进行加法运算,如果做了会出现异常 TypeError: must be str, not int
+号连接字符串使用方法,如下代码会报异常:TypeError: can only concatenate str (not “int”) to str
1
2
3a = 123
print("a=" + a) #此方法不推荐使用
print("a=" , a) #此方法,每个逗号隔开的东西,最后输出的时候会有空格隔开的
在创建字符串时,可以在字符串中指定占位符,可以一次性填充多个占位符
%s :在字符串中表示任意类型字符,(int ,float 、等字符串都可以填充)
%f : 浮点数占位符( 保留位数使用:%(.x)f ,()中的数值为几,就保留几位小数,此时会使用四舍五入 )
%d:整数占位符,输入小数的话,会把小数后面的全部舍去,不会进行四舍五入
占位符使用方法:
1
2
3
4
5
6
7
8
9b = 'Hello %s' % '孙悟空'
c = 'hello %s 你好 %s' % ('tom', '孙悟空')
d = 'hello %3.5s' % 'abcdefg' # %3.5s字符串的长度限制在3-5之间
e = 'hello %s 你好 %s' % (123.456, 24)
f = 'hello %f' % 123
g = 'hello %d' % -123.95
h = '呵呵'
print('h=%s' % b) #输出:h=Hello 孙悟空
格式化字符串,可以通过在字符串前添加一个f来创建一个格式化字符串
在格式化字符串中可以直接嵌入变量
接嵌入变量 使用方法
1
2
3
4
5
6
7#{ 此处可以放入变量 },注意,放在{}中的变量必须是有效的,否则会报错
a = 'how'
b = 'are'
c = 'you'
d = f'hello :{a} {b} {c}' #先输出,赋给一个变量
print(d) #输出:hello :how are you
print(f'a={a} {b} {c}') #直接输出,输出结果:a=how are you
练习
1
2
3
4
5name = "孙悟空"
print("欢迎 " + name + " 光临")
print("欢迎", name, "光临")
print("欢迎 %s 光临" % name)
print(f'欢迎 {name} 光临')
8、字符串复制
将字符串和数字相乘
( * )在语言中表示乘法
如果将字符串和数字相乘,则解释器会将字符串重复指定的次数并返回
1
2a = 'abc'
print(a * 2) #输出结果为:abcabc
9、布尔值和空值
空值:None
- None专门用来表示不存在
布尔值主要用来做逻辑判断
布尔值一共有两个 True 和 False,True表示真 False表示假
布尔值实际上也属于整型,True就相当于1,False就相当于0
1
2
3
4a = True # 相当于1
b = False # 相当于0
print(a + 1) # 相当于1+1,结果为:2
print(b - 1) # 相当于0-1,结果为:-1
10、类型检查
通过类型检查,可以检查只能值(变量)的类型
type()用来检查值的类型
该函数会将检查的结果作为返回值返回,可以通过变量来接收函数的返回值
1
2
3
4
5
6
7
8
9
10
11
12a = 123
c = type('123')
print(type(c)) # <class 'type'>
c = type(a)
print(type(c)) # <class 'type'>
print(type(1)) # <class 'int'>
print(type(1.5)) # <class 'float'>
print(type(True)) # <class 'bool'>
print(type('hello')) # <class 'str'>
print(type(None)) # <class 'NoneType'>
11、对象
Python是一门面向对象的语言,一切皆对象!
程序运行当中,所有的数据都是存储到内存当中然后再运行的!
对象就是内存中专门用来存储指定数据的一块区域
对象实际上就是一个容器,专门用来存储数据
像我们之前学习的数值、字符串、布尔值、None都是对象
参考 下图:
12、对象的结构
每个对象中都要保存三种数据
id(标识)
id用来标识对象的唯一性,每一个对象都有唯一的id
对象的id就相当于人的身份证号一样
可以通过id()函数来查看对象的id。
从下面测试可以看出对于相同的数据的id是一样的,不同数据的id是不一样的
1
2
3
4
5
6
7a = 1
b = 2
c = 1
print(id(a))#140703480809120
print(id(b))#140703480809152
print(id(c))#140703480809120
print(id(a) == id(b))#False
- id是由解析器生成的,在CPython中,id就是对象的内存地址
- 对象一旦创建,则它的id永远不能再改变
- type(类型)
- 类型用来标识当前对象所属的类型
- 比如:int str float bool 。。。
- 类型决定了对象有哪些功能
- 通过type()函数来查看对象的类型
- Python是一门强类型的语言,对象一旦创建类型便不能修改
- value(值)
- 值就是对象中存储的具体的数据
- 对于有些对象值是可以改变的
- 对象分成两大类,可变对象 不可变对象
- 可变对象的值可以改变
- 不可变对象的值不能改变,之前学习的对象都是不可变对象
- 参考下图:
![image-20210309215018157](https://www.naste.top:9000/webp/image-20210309215018157.webp)
13、变量和对象
- 对象并没有直接存储到变量中,在Python中变量更像是给对象起了一个别名
- 变量中存储的不是对象的值,而是对象的id(内存地址),
- 当我们使用变量时,实际上就是在通过对象id在查找对象
- 变量中保存的对象,只有在为变量重新赋值时才会改变
- 变量和变量之间是相互独立的,修改一个变量不会影响另一个变量
- 参考下图
14、类型转换
- 类型转换实际是创建了一个新的对象,而不是修改之前的对象。Python是一门强类型的语言,对象一旦创建类型便不能修改
- 所谓的类型转换,将一个类型的对象转换为其他对象
- 类型转换不是改变对象本身的类型,而是根据当前对象的值创建一个新对象
- 类型转换四个函数 int() float() str() bool()
- 规则:
- 对于其他不可转换为整型的对象,直接抛出异常 ValueError
- str() 可以将对象转换为字符串
- bool() 可以将对象转换为布尔值,bool值可以转换为任何对象,任何对象都可以转换为布尔值
- 布尔值:True -> 1 False -> 0
- 对于所有表示空性的对象都会转换为False,其余的转换为True
- 哪些表示的空性:0 、 None 、 ‘’ 。。。
- int()函数不会对原来的变量产生影响,他是对象转换为指定的类型并将其作为返回值返回
- 浮点数:直接取整,省略小数点后的内容
- 字符串:合法的整数字符串,直接转换为对应的数字
- 如果不是一个合法的整数字符串,则报错 ValueError: invalid literal for int() with base 10: ‘11.5’
- float() 和 int()基本一致,不同的是它会将对象转换为浮点数
- 调用如上函数不会修改原来的变量,如果希望修改原来的变量,则需要对变量进行重新赋值
15、运算符(操作符)
- 运算符可以对一个值或多个值进行运算或各种操作
比如 + 、-、= 都属于运算符
运算符的分类:
1.算术运算符
(+) 加法运算符、- 减法运算符、* 乘法运算符、/ 除法运算符
// 整除,只会保留计算后的整数位,总会返回一个整型
** 幂运算,求一个值的几次幂
% 取模,求两个数相除的余数
1
2
3
4
5c = 25
print('c=', c // 2) # 输出:12
print('c=', c ** 2) # 输出:625
c = 4
print('c=', c ** 0.5) # 输出:2.0
- 2.赋值运算符
- 3.比较运算符(关系运算符)
- 4.逻辑运算符
- 5.条件运算符(三元运算符)
16、赋值运算符
- 赋值运算符
- 在对浮点数做算术运算时,结果也会返回一个浮点数
- = 可以将等号右侧的值赋值给等号左侧的变量
- += a += 5 相当于 a = a + 5
- -= a -= 5 相当于 a = a - 5
- = a = 5 相当于 a = a * 5
- **= a = 5 相当于 a = a 5
- /= a /= 5 相当于 a = a / 5
- //= a //= 5 相当于 a = a // 5
- %= a %= 5 相当于 a = a % 5
17、关系运算符
关系运算符用来比较两个值之间的关系,总会返回一个布尔值
关系运算符有:>、>=、<、<=、==、!=
is 比较两个对象是否是同一个对象,比较的是对象的id
1
2
3#使用方式:
print(1 is 1) #输出:True
print(1 is 2) #输出:Falseis not 比较两个对象是否不是同一个对象,比较的是对象的id
1
2
3#使用方式:
print(1 is 1) #输出:False
print(1 is 2) #输出:True在Python中可以对两个字符串进行大于(等于)或小于(等于)的运算
- 当对字符串进行比较时,实际上比较的是字符串的Unicode编码
- 比较两个字符串的Unicode编码时,是逐位比较的
- 利用该特性可以对字符串按照字母顺序进行排序,但是对于中文来说意义不是特别大
- 注意:如果不希望比较两个字符串的Unicode编码,则需要将其转换为数字然后再比较
逻辑运算符可以连着使用(全是和中间数字比较)
1
2result = 1 < 2 < 3 # 相当于 1 < 2 and 2 < 3
result = 10 < 20 > 15 # 相当于 20 >10 且 20 > 15
18、逻辑运算符
逻辑运算符主要用来做一些逻辑判断
not 逻辑非
not可以对符号右侧的值进行非运算
对于布尔值,非运算会对其进行取反操作,True变False,False变True
对于非布尔值,非运算会先将其转换为布尔值,然后再取反
哪些表示的空性:(0 、 None 、’’)(这些转换为bool类型的值为False,其他的全部转换为True)
1
2print(bool(1)) #True
print(not 1) #False
and 逻辑与
- and可以对符号两侧的值进行与运算
- 只有在符号两侧的值都为True时,才会返回True,只要有一个为False就返回False
- 与运算是找False的
- Python中的与运算是短路的与,如果第一个值为False,则不再看第二个值
or 逻辑或
- or 可以对符号两侧的值进行或运算
- 或运算两个值中只要有一个True,就会返回True
- 或运算是找True的
- Python中的或运算是短路的或,如果第一个值为True,则不再看第二个值
使用:
1
2True and print('你猜我出来吗?') #第一个值是True,会看第二个值,所以print()会执行
False and print('你猜我出来吗?') #第一个值是False,不会看第二个值,所以print()不会执行1
2False or print('你猜我出来吗?') #第一个值为False,继续看第二个,所以打印语句执行
True or print('你猜我出来吗?') #第一个值为True,不看第二个,所以打印语句不执行非布尔值的与(and)或(or)运算
当我们对非布尔值进行与或运算时,Python会将其当做布尔值运算,最终会返回原值
与(and)运算的规则
与运算是找False的,如果第一个值是False,则不看第二个值
如果第一个值是False,则直接返回第一个值,否则返回第二个值
1
2
3
4
5
6
7
8# True and True
result = 1 and 2 # 2
# True and False
result = 1 and 0 # 0
# False and True
result = 0 and 1 # 0
# False and False
result = 0 and None # 0
- 或(or)运算的规则
- 或运算是找True的,如果第一个值是True,则不看第二个值
- 如果第一个值是True,则直接返回第一个值,否则返回第二个值
1
2
3
4
5
6
7
8
# True or True
result = 1 or 2 # 1
# True or False
result = 1 or 0 # 1
# False or True
result = 0 or 1 # 1
# False or False
result = 0 or None # None
19、条件运算符(三元运算符)
语法: 语句1 if 条件表达式 else 语句2
执行流程:
条件运算符在执行时,会先对条件表达式进行求值判断
如果判断结果为True,则执行语句1,并返回执行结果
如果判断结果为False,则执行语句2,并返回执行结果
1
2
3
4
5a = 1
b = 2
c = 3
# 输出:最大值为c
print('最大值为a') if a > b else (print('最大值为b') if b > c else print('最大值为c'))
20、运算符的优先级
和数学中一样,在Python运算也有优先级,比如先乘除 后加减
运算符的优先级可以根据优先级的表格来查询,在表格中位置越靠下的运算符优先级越高,优先级越高的越优先计算,如果优先级一样则自左向右计算
在开发中如果遇到优先级不清楚的,则可以通过小括号来改变运算顺序
逻辑运算符可以连着使用(全是和中间数字比较)
1
2result = 1 < 2 < 3 # 相当于 1 < 2 and 2 < 3
result = 10 < 20 > 15 # 相当于 20 >10 且 20 > 15
二、流程控制语句
1、条件判断语句
语法:
1
2if 条件表达式:
代码块
代码块
- 代码块中保存着一组代码,同一个代码块中的代码,要么都执行要么都不执行
- 代码块就是一种为代码分组的机制
- 如果要编写代码块,语句就不能紧随在:后边,而是要写在下一行
- 代码块以缩进开始,直到代码恢复到之前的缩进级别时结束
if只能执行一条语句和执行多条语句的格式:
只能执行一条语句:将满足条件需要执行的语句放在表达式的后面,此时只会执行一条语句
1
2if False: print(10) #此时由于if条件不满足,此处不会执行
print("我不受if语句的控制") #次数会执行,输出为:我不受if语句的控制执行多条语句,语句不能在和if在同一行,且需要执行语句需要缩进,
缩进有两种方式,一种是使用tab键,一种是使用空格(四个)
- Python的官方文档中推荐我们使用空格来缩进
- Python代码中使用的缩进方式必须统一
执行多条语句的时候,恢复到之前的缩进就可以退出if的判断
1
2
3
4
5
6
7num = 10
if num > 10:
#如下两行代码都会受到if条件的限制
print("我受if语句的控制")
print("我受if语句的控制2")
print("我不受if语句的控制") #此处由于没有缩进,所以不受if语句的限制
执行的流程:if语句在执行时,会先对条件表达式进行求值判断,
可以使用逻辑运算符来连接多个条件,
- 如果希望所有条件同时满足,则需要使用and
- 如果希望只要有一个条件满足即可,则需要使用or
2、input函数
该函数用来获取用户的输入,输入的东西为str类型的
input()调用后,程序会立即暂停,等待用户输入,用户输入完内容以后,点击回车程序才会继续向下执行,用户输入完成以后,其所输入的的内容会以返回值得形式返回
注意:input()的返回值是一个字符串
input()函数中可以设置一个字符串作为参数,这个字符串将会作为提示文字显示
a = input(‘请输入任意内容:’)
print(‘用户输入的内容是:’,a)
- input()也可以用于暂时阻止程序结束
3、if-else
if-else:
1
2
3
4if 条件表达式:
代码块
else:
代码块
4、if-elif-else
if-elif-else
1
2
3
4
5
6
7
8
9#语法
if 条件表达式:
代码块
elif 条件表达式:
代码块
elif 条件表达式:
代码块
else:
代码块执行流程if-elif-else语句在执行时,会自上向下依次对条件表达式进行求值判断,
- 如果表达式的结果为True,则执行当前代码块,然后语句结束
- 如果表达式的结果为False,则继续向下判断,直到找到True为止
- 如果所有的表达式都是False,则执行else后的代码块
if-elif-else中只会有一个代码块会执行
实现顺序由上至下,上面有代码满足条件的话,下面的代码将不会执行
注意:写代码的时候不能写“ 死代码 ”:就是前面的条件包含下面的条件,此时下面的代码是永远不会执行的,这就称为“ 死代码,这是需要将代码修改”
5、if-(if-else)-else
格式
1
2
3
4
5
6
7
8
9
10if 条件表达式:
代码块
if 条件表达式:
代码块
elif 条件表达式:
代码块
else:
代码块
else:
代码块
三、循环语句
循环语句可以使指定的代码块重复指定的次数
循环语句分成两种,while循环 和 for循环
1、while循环
语法:
1
2
3
4
5
6
7
8
9
10
11
12while 条件表达式 :
代码块
else :
代码块
#代码示例:
a=1
while a<10:
print('a =',a)
a+=1
else:
print('else中的代码块')执行流程:
while语句在执行时,会先对while后的条件表达式进行求值判断,
- 如果判断结果为True,则执行循环体(代码块),
- 循环体执行完毕,继续对条件表达式进行求值判断,以此类推,
- 直到判断结果为False,则循环终止,如果循环有对应的else,则执行else后的代码块
条件表达式恒为True的循环语句,称为死循环,它会一直运行,慎用!
循环的三个要件(表达式)
- 初始化表达式,通过初始化表达式初始化一个变量
- 初始化表达式,通过初始化表达式初始化一个变量
- 更新表达式,修改初始化变量的值
2、循环嵌套
外层循环执行一次,内层循环执行一遍
1 | """ |
3、break、continue和pass
break可以用来立即退出循环语句(包括else),直接退出,不继续执行,直接终止该循环程序
continue可以用来跳过当次循环,结束本次循环,开始下次循环,该循环程序不结束
pass是用来在判断或循环语句中占位的
1
2
3
4
5#如使用了一个if语句:这个if语句里面不想执行其他语句,这时就可以使用pass来占位
#pass相当于占位的,程序不执行
#例如:
if 判断条件:
pass
4、模块
模块,通过模块可以对Python进行扩展
引入一个time模块,来统计程序执行的时间
1
2
3
4
5
6#先导入time 模块
from time import *
#使用方式
begin = time()
end = time()
print("程序运行的时间为",end-begin,"秒")
time()函数可以用来获取当前的时间,返回的单位是秒
获取程序开始的时间
四、序列
1、列表(list)的的简介
存取数据和Java中的lilst集合差不多
列表是有序的可变的序列
Java中的数组长度一旦定义了就不可变了,但是Python可以改变
1 | #Java创建数组,创建一个数组长度为10的object类型的数组 |
- 列表是Python中的一个对象
- 对象(object)就是内存中专门用来存储数据的一块区域
- 之前我们学习的对象,像数值,它只能保存一个单一的数据
- 列表中可以保存多个有序的数据
- 列表是用来存储对象的对象
- 列表的使用:
- 列表的创建
- 操作列表中的数据
- 练习:
- 创建一个列表,在列表中保存你最好的5个朋友的名字
- 然后分别通过索引来获取每一个朋友的名字
2、切片
具体使用可以上网搜索:python的切片规则
- 从现有的集合中截取一部分数据,变为一个新的集合,可以设置截取的步长值。
- 切片指从现有列表中,获取一个子列表
创建一个列表,一般创建列表时,变量的名字会使用复数
列表的索引可以是负数(-1:表示倒数第一个数,-2:表示倒数第二个数)
1
2
3
4my_list = [1, 2, 3]
print(my_list[-1]) # 3
print(my_list[-2]) # 2
print(my_list[-3]) # 1如果索引是负数,则从后向前获取元素,-1表示倒数第一个,-2表示倒数第二个 以此类推
通过切片来获取指定的元素
语法:列表[起始:结束]
1
2
3语法:列表[起始:结束]
my_list = [1, 2, 3]
print(my_list[0:2]) #输出:[1, 2]- 通过切片获取元素时,会包括起始
- 位置的元素,不会包括结束位置的元素
- 做切片操作时,总会返回一个新的列表,不会影响原来的列表
- 起始和结束位置的索引都可以省略不写
- 如果省略结束位置,则会一直截取到最后
- 如果省略起始位置,则会从第一个元素开始截取
- 如果起始位置和结束位置全部省略,则相当于创建了一个列表的副本
```
语法:列表[起始:结束:步长]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 步长:每隔多少个元素取一个元素
- 步长表示,每次获取元素的间隔,默认值是1
- 步长不能是0,但是可以是负数
- 如果是负数,则会从列表的后部向前边取元素
## 3、列表的通用操作
( + )和 ( * )
- +可以将两个列表拼接为一个列表
```python
my_list = [1, 2, 3] + [4, 5, 6]
print(my_list) # 输出条件:[1, 2, 3, 4, 5, 6]
( * )可以将列表重复指定的次数
1
2my_list = [1, 2]*3
print(my_list) #输出:[1, 2, 1, 2, 1, 2]
in 和 not in
in用来检查指定元素是否存在于列表中
- 如果存在,返回True,否则返回False
not in用来检查指定元素是否不在列表中
如果不在,返回True,否则返回False
1
2
3my_list = [1, 2] * 3
print(3 not in my_list) # 判断3不在my_list 集合中,输出:True
print(2 in my_list) # 判断2在my_list集合中 ,输出:True
len()获取列表中的元素的个数
1
2my_list = [1, 2] * 3
print(len(my_list)) # 判断集合中有几个元素 ,输出:6
min() 获取列表中的最小值
max() 获取列表中的最大值
1
2
3my_list = [1, 2] * 3
print(min(my_list)) # 输出集合中的最小值:输出:1
print(max(my_list)) # 输出集合中的最大值:输出:2
两个方法(method),方法和函数基本上是一样,只不过方法必须通过 对象.方法() 的形式调用
list.index(x[, start[, end]])
- 第一个参数:获取传入参数第一次出现的位置(索引)
- 第二个参数:从序列的第几个索引开始索引,到最后结束
- 第三个参数:需要前面两个参数都传入了值才能使用。表示,在哪个范围寻找传入参数的索引位置
index():获取指定元素在列表中第一次出现时的索引
- 例如:xxx.print(这是调用方法的形式)
1
#使用 index() 方法
- count():统计我们指定元素在列表中出现的次数
4、序列
- 序列是Python中最基本的一种数据结构
- 数据结构指计算机中数据存储的方式
- 序列用于保存一组有序的数据,所有的数据在序列当中都有一个唯一的位置(索引)
- 并且序列中的数据会按照添加的顺序来分配索引
- 序列的分类:
- 可变序列(序列中的元素可以改变)
- 列表(list)
- 不可变序列(序列中的元素不能改变):
- 字符串
- 元组
- 可变序列(序列中的元素可以改变)
5、通过索引和切片修改元素
通过索引和切片修改列表中的元素
- 直接通过索引来修改元素
- 通过del来删除元素
- 通过切片来修改列表
- 通过切片来删除元素
- 在给切片赋值时,只能使用序列
- 当设置了步长值时,序列中元素的个数必须和切片中元素个数一直
以上操作,只适用于可变序列(字符串的底层就是序列,但字符串是不可变的)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30stu = ['孙悟空', '猪八戒', '沙和尚', '唐僧', '蜘蛛精', '白骨精']
"""
- 直接通过索引来修改元素
"""
stu[2] = '金角大王'
print(stu) # 输出:['孙悟空', '猪八戒', '金角大王', '唐僧', '蜘蛛精', '白骨精']
"""
- 通过del来删除元素
"""
del stu[5]
print(stu) # 输出:['孙悟空', '猪八戒', '金角大王', '唐僧', '蜘蛛精']
"""
- 通过切片来修改列表
"""
stu[4:5] = ["白龙马", "白骨精"]
print(stu) # 输出:['孙悟空', '猪八戒', '金角大王', '唐僧', '白龙马', '白骨精']
"""
- 通过切片来删除元素
"""
del stu[4: 5]
print(stu) # 输出:['孙悟空', '猪八戒', '金角大王', '唐僧', '白骨精']
"""
- 以上操作,只适用于可变序列
"""
s = "hello"
s[1] = "2" # 会报错:TypeError: 'str' object does not support item assignment
print(s[1])
6、通过方法修改元素
使用方法修改列表的元素
- append() :向列表最后一行添加一个元素
- insert():向列表的指定位置插入一个元素
- 参数:
- 要插入的位置
- 要插入的元素
- 参数:
- extend():使用新的序列来扩展当前序列,在原有的基础上添加新的序列
- 需要一个序列作为参数,它会将该序列中的元素添加到当前列表中
- clear():清空序列
- pop():根据索引删除并返回被删除的元素
- 传入一个int类型的参数表示删除指定索引位置的元素
- remove():删除指定值的元素,如果相同值得元素有多个,只会删除第一个
- reverse():用来反转列表
sort():用来对列表中的元素进行排序,默认是升序排列,如果需要降序排列,则需要传递一个reverse=True作为参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55stu = ['孙悟空', '猪八戒', '沙和尚', '唐僧']
"""
- append() :向列表最后一行添加一个元素
"""
stu.append("白龙马")
print(stu) # 输出为:['孙悟空', '猪八戒', '沙和尚', '唐僧', '白龙马']
"""
- insert():向列表的指定位置插入一个元素
- 参数:
- 要插入的位置
- 要插入的元素
"""
stu.insert(0, "牛魔王")
print(stu) # 输出为:['牛魔王', '孙悟空', '猪八戒', '沙和尚', '唐僧', '白龙马']
"""
- extend():使用新的序列来扩展当前序列
- 需要一个序列作为参数,它会将该序列中的元素添加到当前列表中
"""
stu.extend(["金角大王", "银角大王"])
print(stu) # 输出为:['牛魔王', '孙悟空', '猪八戒', '沙和尚', '唐僧', '白龙马', '金角大王', '银角大王']
"""
- pop():根据索引删除并返回被删除的元素
"""
print(stu.pop(0)) # 输出为:牛魔王
print(stu) # 输出为:['孙悟空', '猪八戒', '沙和尚', '唐僧', '白龙马', '金角大王', '银角大王']
"""
- remove():删除指定值得元素,如果相同值得元素有多个,只会删除第一个
"""
stu.remove("白龙马")
print(stu) # 输出为:['孙悟空', '猪八戒', '沙和尚', '唐僧', '金角大王', '银角大王']
"""
- reverse():用来反转列表
"""
stu.reverse()
print(stu) # 输出为:['银角大王', '金角大王', '唐僧', '沙和尚', '猪八戒', '孙悟空']
"""
- sort():用来对列表中的元素进行排序,默认是升序排列
- 如果需要降序排列,则需要传递一个reverse=True作为参数
"""
stu.sort(reverse=True) # 不传入reverse=True的话是默认升序排列,传入reverse=True是降序排列
print(stu) # 输出为:['银角大王', '金角大王', '猪八戒', '沙和尚', '孙悟空', '唐僧']
"""
- clear():清空序列
"""
stu.clear()
print(stu) # 输出为:[]
exit()
8、for循环
1 | # 语法 |
- for循环的代码块会执行多次,序列中有几个元素就会执行几次
- 每执行一次就会将序列中的一个元素赋值给变量
- 所以我们可以通过变量,来获取列表中的元素
- 需要自己控制for循环的循环次数,可以使用 range()函数来指定一个佛如循环的次数
- range() 函数可以指定一个序列
7、遍历列表
通过while循环来遍历列表(序列)
1
2
3
4
5
6
7stu = ['孙悟空', '猪八戒', '沙和尚', '唐僧']
i = 0
while i < len(stu):
print(stu[i], end="\t")
i += 1
# 输出结果为:孙悟空 猪八戒 沙和尚 唐僧for循环来遍历列表(和Java中的foreach(增强for循环差不多使用方式))
- for循环的代码块会执行多次,序列中有几个元素就会执行几次
- 每执行一次就会将序列中的一个元素赋值给变量
所以我们可以通过变量,来获取列表中的元素
1
2
3for i in stu:
print(i, end=" ")
#执行结果为:孙悟空 猪八戒 沙和尚 唐僧 白骨精
8、range()函数
range()是一个函数,可以用来生成一个自然数的序列,可以和for循环配合一起使用
该函数需要三个参数:取范围的时候( begin <= i < end ) ,包括开始,不包括结束
- 起始位置(可以省略,默认是0)
- 结束位置
- 步长(可以省略,默认是1)
通过range()可以创建一个执行指定次数的for循环,for()循环除了创建方式以外,其余的都和while一样,
- 包括else、包括break continue都可以在for循环中使用
并且for循环使用也更加简单
1
2
3
4
5
6
7
8
9
10# 传入一个参数
a = range(3) # 成成一个[0,1,2]序列
for i in range(3):
print(i, end=" ") # 0 1 2
# 传入两个参数
range(1, 3) # 生成一个[1,2]序列
# 传入三个参数
range(1, 5, 2) # 生成一个[1,3]序列
8、元组(不可变序列)
元组是有序的不可变的序列
tuple类型
元组是一个不可变的序列,它的操作的方式基本上和列表是一致的
所以你在操作元组时,就把元组当成是一个不可变的列表就ok了
什么时候使用元组:一般当我们希望数据不改变时,就使用元组,其余情况都使用列表
创建元组
1
2
3
4
5
6
7
8
9
10
11
12# 创建一个空元组
my_tuple = ()
# 创建一个有元素的元组
my_tuple1 = (1, 2, 3, 4, 5)
# 当元组不是空元组时,括号可以省略
my_tuple2 = 1, 2, 3, 4, 5
# 如果元组不是空元组,里面至少要有一个 ,
my_tuple3 = 20,
元组的解包(解构):解包指就是将元组当中每一个元素都赋值给一个变量
变量的个数需要和元组中元素的个数一样,如果不一样会报错(ValueError: too many values to unpack)
1
2
3
4
5
6
7my_tuple2 = 1, 2, 3, 4
#此处变量的个数需要和元组中元素的个数一样
a, b, c, d= my_tuple2
print("a =", a)
print("b =", b)
print("c =", c)
print("d =", d)
- **当想获取部分变量时候,也可以变量的数量和元组中元素的个数的数量不一样,**
**也可以在变量前边添加一个*,这样变量将会获取元组中所有剩余的元素,(不能同时出现啷个 * 变量)**
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 当元组不是空元组时,括号可以省略
my_tuple2 = 1, 2, 3, 4
#如下为在不同位置的变量上添加 *
a, b, *c = my_tuple2
print("a =", a) # 输出:a = 1
print("b =", b) # 输出:b = 2
print("c =", c) # 输出:c = [3, 4]
a, *b, c = my_tuple2
print("a =", a) # 输出:a = 1
print("b =", b) # 输出:b = [2, 3]
print("c =", c) # 输出:c = 4
*a, b, c = my_tuple2
print("a =", a) # 输出:a = [1, 2]
print("b =", b) # 输出:b = 3
print("c =", c) # 输出:c = 4
- 使用场景:
- 场景一:交换两个变量的值的时候
1
2
3
4
5
6
7
a = 100
b = 200
print(a, b) # 输出:100,200
c = 200
d = 100
a, b = c, d
print(a, b) # 输出:200,100
9、可变对象
该对象不影响id值,只修改了值。改变量是把id和value都修改了
每个对象中都保存了三个数据:
- id(标识)
type(类型)
- value(值)
列表就是一个可变对象
a = [1,2,3]
a[0] = 10 (改对象)
这个操作是在通过变量去修改对象的值
这种操作不会改变变量所指向的对象
当我们去修改对象时,如果有其他变量也指向了该对象,则修改也会在其他的变量中体现
a = [4,5,6] (改变量)
- 这个操作是在给变量重新赋值
- 这种操作会改变变量所指向的对象
- 为一个变量重新赋值时,不会影响其他的变量
一般只有在为变量赋值时才是修改变量,其余的都是修改对象
当修改列表的某一个值的时候是改对象,当给一个列表重新赋值的时候是改变量
图解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25a = [1, 2, 3]
print("a", id(a), a) # a 2118911262400 [1, 2, 3]
# 改对象的值。id值没有改变。此时还是同一个变量
a[0] = 4
print("a", id(a), a) # a 2118911262400 [4, 2, 3]
# 这里是改变量,给变量重新赋值,id值已经改变了,这两个变量不是一个对象了
a = [5, 6, 7]
print("a", id(a), a) # a 2748994067584 [5, 6, 7]
# 将一个变量的值赋给另外一个变量,此时a和b是一个对象,
# 他们的id值都是相同的,此时改变一个变量中的值,另外一个变量的值也会修改
b = a
print("b", id(b), b) # b 2748994067584 [5, 6, 7]
# 因为此时变量a和变量b的id值都是相等的,此时修改了b变量中的值,a变量中的值也会修改
# 因为变量b的值是由变量a赋值的来的,此时修改的变量b中对象的值,而变量b的值没有修改
# 所以此时b的id值没有改变,所以变量a中的值也会变量b中对象的只修改而修改,
# 反之,变量b中对象的值也会随着变量a中对象的值的改变而改变
b[1] = 0
print("b", id(b), b) # b 2748994067584 [5, 0, 7]
print("a", id(a), a) # a 2748994067584 [5, 0, 7]
10、python中的==、!=、is、is not
== 和 != :比较的是对象的值是否相等
is 和 is not 比较的是对象的id是否相等(比较两个对象是否是同一个对象)
一般使用== 和 != 除非是比较严格的要求两个对象是否相等的时候才会使用 is 和 is not
1
2
3
4
5
6
7
8
9
10
11a = [1, 2]
b = [1, 2]
# a和b的id分别为
print(id(a)) # 2890673028800
print(id(b)) # 2890970504256
# ” == 和 != “ 比较的是两个变量的值
print("a和b是否相等:", a == b) # True
# ” is 和 is not “ 比较的是两个变量的id值,也就是内存地址
print("a和b是否相等:", a is b) # False
11、字典(dict)
和Java中的map集合差不多
字典属于一种新的数据结构,称为映射(mapping)
字典的作用和列表类似,都是用来存储对象的容器
列表存储数据的性能很好,但是查询数据的性能很差
在字典中每一个元素都有一个唯一的元素,通过这个唯一的名字可以快速的查找到指定的元素
查询元素时,字典的效率是非常快的
在字典中可以保存多个对象,每个对象都会有一个唯一的名字
在字典中可以保存多个对象,每个对象都会有一个唯一的名字
- 这个唯一的名字,我们称其为键(key),通过key可以快速查询value
- 这个对象,我们称其为值(value)
- 所以字典,我们也称其为键值对(key-value)结构
- 每个字典中都可以有多个键值对,而每一个键值对我们称其为一项(item)
使用方式:
创建方式:
1
2
3
4
5
6# 字典
# 使用{}来创建字典
my_dictionary = {} # 创建了一个空字典
# 创建一个包含有数据的字典
my_dictionary1 = {"name": "韩信", "age": "18", "gender": "男"}语法:
- {key:value,key:value,key:value}
字典的值可以是任意对象
字典的键可以是任意的不可变对象(int、str、bool、tuple …),但是一般我们都会使用str
字典的键是不能重复的,如果出现重复的后边的会替换到前边的
根据键来获取值 :字典变量名字[key]
如果使用了字段中不存在的key,会报错:KeyError
1
2
3
4
5
6
7# 创建一个包含有数据的字典
my_dictionary1 = {"name": "韩信", "age": "18", "gender": "男"}
# 获取数据的方式:字典变量名字[key]
print(my_dictionary1["name"])
print(my_dictionary1["age"])
print(my_dictionary1["gender"])
12、字典的使用
创建字典
直接创建字典
使用 dict () 函数来创建字典
也可以将一个包含有双值子序列的序列转换为字典,
- 双值序列,序列中只有两个值:[(“name”, “韩信”), (“age”, “18”), (“gender”, “男”)],每个序列有两个值
子序列,如果序列中的元素也是序列,那么我们就称这个元素为子序列
- 例如[(“name”, “韩信”), (“age”, “18”), (“gender”, “男”)]中的(”name”,”韩信”),就是一个子序列
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21# 1、直接创建字典
"""
# 字典
# 使用{}来创建字典
my_dictionary = {} # 创建了一个空字典
#语法:{key:value,key:value,key:value}
创建一个包含有数据的字典
my_dictionary1 = {"name": "韩信", "age": "18", "gender": "男"}
print(my_dictionary1["name"])
print(my_dictionary1["age"])
print(my_dictionary1["gender"])
"""
# 2、使用函数来创建字典
# 每一个参数都是一个键值对,参数名就是键,参数名就是值(这种方式创建的字典,key都是字符串)
dictionary2 = dict(name="韩信", age="18", gender="男")
{'name': '韩信', 'age': '18', 'gender': '男'} <class 'dict'>
print(dictionary, type(dictionary))
# 3、也可以将一个包含由双值子序列的序列转换为字典
# 双值子序列,序列中只有两个值,
字典中的一些方法
len():获取字典中键值对的个数
in 检查字典中是否包含指定的键
not in 检查字典中是否不包含指定的键
字典名字[key]:获取字典中的值,根据键来获取值,通过[]来获取值时,如果键不存在,会抛出异常 KeyError
get(key[, default]) :该方法用来根据键来获取字典中的值
- 如果获取的键在字典中不存在,会返回None
也可以指定一个默认值,来作为第二个参数,这样获取不到值时将会返回默认值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37dictionary2 = dict(name="韩信", age="18", gender="男")
"""
- len():获取字典中键值对的个数
"""
print(len(dictionary2)) # 3
"""
- in 检查字典中是否包含指定的键
格式:a = 'key' in 字典名字
"""
print("name" in dictionary2) # True
"""
- not in 检查字典中是否不包含指定的键
"""
print("name" not in dictionary2) # False
"""
- 字典名字[key]:获取字典中的值,根据键来获取值,通过[]来获取值时,如果键不存在,会抛出异常 KeyError
"""
print(dictionary2["name"]) # 韩信
"""
- get(key[, default]) :该方法用来根据键来获取字典中的值
"""
print(dictionary2.get("gender")) # 男
"""
- 如果获取的键在字典中不存在,会返回None
"""
print(dictionary2.get("name_gender")) # None
"""
- 也可以指定一个默认值,来作为第二个参数,这样获取不到值时将会返回默认值
"""
# 我是默认的值,key没有对应的value的时候,我会返回
print(dictionary2.get("name_gender", "我是默认的值,key没有对应的value的时候,我会返回"))
修改字典
d[key] = value:根据key来修改对应的value
- 如果key存在则覆盖,不存在则添加
setdefault(key[, default]):向字典中添加元素
- 可以用来向字典中添加key-value
- 如果key已经存在于字典中,则返回key的值,不会对字典做任何操作
- 如果key不存在,则向字典中添加这个key,并设置value
update([other]):添加一个字典,字典中有的元素会被更新,没有填追加到末尾
- 将其他的字典中的key-value添加到当前字典中
- 如果有重复的key,则后边的会替换到当前的
popitem():随机删除一个元素
- 随机删除字典中的一个键值对,一般都会删除最后一个键值对
- 删除之后,它会将删除的key-value作为返回值返回
- 返回的是一个元组,元组中有两个元素,第一个元素是删除的key,第二个是删除的value
- 当使用popitem()删除一个空字典时,会抛出异常 KeyError: ‘popitem(): dictionary is empty’
pop(key[, default]):根据key删除元素
- 根据key删除字典中的key-value,会将被删除的value返回!,如果删除不存在的key,会抛出异常
- 如果指定了默认值,再删除不存在的key时,不会报错,而是直接返回默认值
clear():用来清空字典
copy():
- 该方法用于对字典进行浅复制
- 复制以后的对象,和原对象是独立,修改一个不会影响另一个
- 注意,浅复制会简单复制对象内部的值,如果值也是一个可变对象,这个可变对象不会被复制
如果字典复制给另外一个字典的话是两个独立的对象,他们的id值是不一样的,但是字典里面还有字典的话,词典里面的字典的id值是一样的此时,修改一个字典里面的字典的值,复制的字典里面字典的值也会被修改,因为两者id是一样的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37dictionary = {"name": {"id": "1", "class": "计软19-1班"}, "age": "18"}
dictionary2 = dictionary.copy()
print("修改前dictionary的值:", dictionary, "\n整个字典的id=", id(dictionary))
print("===================================================================")
print("修改前dictionary2的值:", dictionary2, "\n整个字典的id=", id(dictionary2))
print("===================================================================")
# 此时修改dictionary中 key:name对应的字典中的id的值
dictionary["name"]["id"] = "2" # 修改字典中字典name中的id的值
dictionary["age"] = "20" # 修改字典中的age
print("修改后dictionary的值:", dictionary, "\n整个字典的id=", id(dictionary))
print("===================================================================")
print("修改后dictionary2的值:", dictionary2, "\n整个字典的id=", id(dictionary2))
print("===================================================================")
print("dictionary 字典中的字典的id的值为:", id(dictionary["name"]))
print("dictionary2字典中的字典的id的值为:", id(dictionary2["name"]))
"""
修改前dictionary的值: {'name': {'id': '1', 'class': '计软19-1班'}, 'age': '18'}
整个字典的id= 1480807631360
===================================================================
修改前dictionary2的值: {'name': {'id': '1', 'class': '计软19-1班'}, 'age': '18'}
整个字典的id= 1480807630976
===================================================================
修改后dictionary的值: {'name': {'id': '2', 'class': '计软19-1班'}, 'age': '20'}
整个字典的id= 1480807631360
===================================================================
修改后dictionary2的值: {'name': {'id': '2', 'class': '计软19-1班'}, 'age': '18'}
整个字典的id= 1480807630976
===================================================================
dictionary 字典中的字典的id的值为: 1480807552512
dictionary2字典中的字典的id的值为: 1480807552512
"""
字典方法的使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70dictionary2 = dict(name="韩信", age="18", gender="男")
"""
- **d[key] = value** 如果key存在则覆盖,不存在则添加
"""
dictionary2['name'] = "李白"
print(dictionary2)
"""
- **setdefault(key[, default]):** 可以用来向字典中添加key-value
- 如果key已经存在于字典中,则返回key的值,不会对字典做任何操作
- 如果key不存在,则向字典中添加这个key,并设置value,不设置默认值的话,自动会被设置为None
"""
dictionary2.setdefault("id", "1")
print(dictionary2)
"""
- update([other]):
- 没有对应的key就会添加到最后面,有key的话会更新当前key对应的value
- 将其他的字典中的key-value添加到当前字典中
- 如果有重复的key,则后边的会替换到当前的,当前的value将会被更新
"""
dictionary = {"id": "2", "class": "计软19-1班"}
dictionary2.update(dictionary)
print(dictionary2)
"""
- **popitem():**
- 随机删除字典中的一个键值对,一般都会删除最后一个键值对
- 删除之后,它会将删除的key-value作为返回值返回
- 返回的是一个元组,元组中有两个元素,第一个元素是删除的key,第二个是删除的value
- 当使用popitem()删除一个空字典时,会抛出异常 KeyError: 'popitem(): dictionary is empty'
"""
print(dictionary2.popitem()) # 被删除后返回的结果为:('class', '计软19-1班')
print(dictionary2)
"""
- **pop(key[, default]):**
- 根据key删除字典中的key-value,会将被删除的value返回!,如果删除不存在的key,会抛出异常
- 如果指定了默认值,再删除不存在的key时,不会报错,而是直接返回默认值
"""
print(dictionary2.pop("name", "这是默认值"))
print(dictionary2)
"""
- **clear():**用来清空字典
"""
dictionary2.clear()
print(dictionary2)
"""
- **copy():**
- 该方法用于对字典进行浅复制
- 复制以后的对象,和原对象是独立,修改一个不会影响另一个
- 注意,浅复制会简单复制对象内部的值,如果值也是一个可变对象,这个可变对象不会被复制
"""
dictionary = {"name": {"id": "1", "class": "计软19-1班"}, "age": "18"}
dictionary2 = dictionary.copy()
print("修改前dictionary的值:", dictionary, "\n整个字典的id=", id(dictionary))
print("===================================================================")
print("修改前dictionary2的值:", dictionary2, "\n整个字典的id=", id(dictionary2))
print("===================================================================")
# 此时修改dictionary中 key:name对应的字典中的id的值
dictionary["name"]["id"] = "2"
dictionary["age"] = "20"
print("修改后dictionary的值:", dictionary, "\n整个字典的id=", id(dictionary))
print("===================================================================")
print("修改后dictionary2的值:", dictionary2, "\n整个字典的id=", id(dictionary2))
print("===================================================================")
print("dictionary 字典中的字典的id的值为:", id(dictionary["name"]))
print("dictionary2字典中的字典的id的值为:", id(dictionary2["name"]))
13、字典的遍历
- keys()
- 该方法会返回一个序列,序列中保存有字典的所有的键
- 通过遍历keys()来获取所有的键
- values()
- 该方法会返回一个序列,序列中保存有字典的左右的值
- items()
- 该方法会返回字典中所有的项
- 它会返回一个序列,序列中包含有双值子序列
- 双值分别是,字典中的key和value
1 | dictionary = dict(name="韩信", age="18", gender="男") |
14、集合(set)
集合和列表非常相似
- 不同点:
- 集合中只能存储不可变对象(不可变对象:int,string,float,number,tuple(元组))
- 集合中存储的对象是无序(不是按照元素的插入顺序保存)
- 集合中不能出现重复的元素,出现重复的元素只会保存第一次出现的元素,之后出现的会被删除
- 不同点:
集合创建
使用 {} 来创建集合
1
2
3
4
5
6
7
8
9
10# 使用 {} 来创建空集合类型为dict,为字典类型
s = {}
print(type(s)) # <class 'dict'>
# 使用 {} 来创建有数据的集合,此时的集合的类型为set,为集合类型
s = {10, 1, 23, 1}
print(s, "type=", type(s)) # {1, 10, 23} type= <class 'set'>
s = {10, 1, 23, 1, {"name": "韩信"}}
print(s) # TypeError: unhashable type: 'dict' #只能存储不可变对象字典为可变对象
- 通过set()来将序列和字典转换为集合
- 使用set()将字典转换为集合时,只会包含字典中的键
1
2
3
4
5
6
7
8
9
s = set() # 空集合
print("type =", type(s)) # type = <class 'set'>
# 通过set(将序列和字典转换为集合)
s = set({"name", 1, "韩信"}) # 通过set将列表转化为集合
print(s, " type =", type(s)) # {'韩信', 1, 'name'} type = <class 'set'>
s = set({"name": "李白", "gender": "男"}) # 通过set将字典转化为集合
print(s, "type =", type(s)) # {'name', 'gender'} type = <class 'set'>
集合中的方法
- 使用in和not in来检查集合中的元素
- 使用len()来获取集合中元素的数量
- add() 向集合中添加元素
- update() 将一个集合中的元素添加到当前集合中
- update()可以传递序列或字典作为参数,字典只会使用键
- pop()随机删除并返回一个集合中的元素
- remove()删除集合中的指定元素
- clear()清空集合
copy()对集合进行浅复制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37my_list = {1, 23, 123, 890, 1230, 100, 23}
# - 使用in和not in来检查集合中的元素
print(123 in my_list) # True
# - 使用len()来获取集合中元素的数量
print(len(my_list)) # 6
# - add() 向集合中添加元素
my_list.add(190)
print(my_list, "len =", len(my_list)) # {1, 100, 1230, 23, 890, 123, 190}
# - update() 将一个集合中的元素添加到当前集合中
my_list.update({20, 300})
print(my_list, "len =", len(my_list)) # {1, 100, 300, 1230, 20, 23, 890, 123, 190} len = 9
# - update()可以传递序列或字典作为参数,字典只会使用键
my_list.update({"name": "李白", "gender": "男", "age": 18})
print(my_list, "len =", len(my_list))
# {1, 100, 300, 1230, 'gender', 'name', 'age', 20, 23, 890, 123, 190} len = 12
# - pop()随机删除并返回一个集合中的元素
print(my_list.pop()) # 删除一个随机元素
# - remove()删除集合中的指定元素
my_list.remove("age")
print(my_list, "len =", len(my_list))
# {100, 300, 1230, 'name', 'gender', 20, 23, 890, 123, 190} len = 10
# - copy()对集合进行浅复制
b = my_list.copy()
print(b, "len =", len(b))
# {1, 100, 'name', 300, 1230, 20, 23, 890, 123, 190} len = 10
# - clear()清空集合
my_list.clear()
print(my_list, "len =", len(my_list)) # set() len = 0
- 集合的运算,在对集合做运算时,不会影响原来的集合,而是返回一个运算结果
- & 交集运算:两个集合中共同的部分
- | 并集运算
- (-) 差集
- ^ 异或集 获取只在一个集合中出现的元素
- <= 检查一个集合是否是另一个集合的子集
- 如果a集合中的元素全部都在b集合中出现,那么a集合就是b集合的子集,b集合是a集合超集
- < 检查一个集合是否是另一个集合的真子集
- 如果超集b中含有子集a中所有元素,并且b中还有a中没有的元素,则b就是a的真超集,a是b的真子集
- (>=) 检查一个集合是否是另一个的超集
- (>) 检查一个集合是否是另一个的真超集
1 | # 在对集合做运算时,不会影响原来的集合,而是返回一个运算结果 |
15、可变对象和不可变对象分别有哪些
- 不可变(immutable):int、字符串(string)、float、(数值型number)、元组(tuple)
- 可变(mutable):字典型(dictionary)、列表型(list)
五、函数
1、函数简介
函数也是一个对象
对象是内存中专门用来存储数据的一块区域。
函数可以用来保存一些可执行的代码,并且可以在需要时,对这些语句进行多次的调用
2、创建函数
创建函数:
def 函数名([形参1,形参2,…形参n]) :
代码块
函数名必须要符号标识符的规范:(可以包含字母、数字、下划线、但是不能以数字开头)
函数中保存的代码不会立即执行,需要调用函数代码才会执行
调用函数:函数对象()
定义函数一般都是要实现某种功能的
1
2
3
4
5
6
7
8
9
10
11# - 练习1:定义一个函数,可以用来求任意三个数的乘积
def product(a, b, c):
print(a * b * c)
product(2, 2, 3) # 12
# 练习2:定义一个函数,可以根据不同的用户名显示不同的欢迎信息
def display(username):
print(f'欢迎{username}登录')
display("张三") # 欢迎张三登录
3、函数的参数
定义形参时,可以为形参指定默认值,指定了默认值以后,如果用户传递了参数则默认值没有任何作用,反之则默认值就会生效
1
2
3
4def my_sum(a, b=10): # 此时10为b的默认值
print(a + b)
my_sum(10) # ,没有输入参数足够的此时使用函数的默认值,输出为 20
实参的传递方式
- 位置参数
- 位置参数就是将对应位置的实参复制给对应位置的形参
- 第一个实参赋值给第一个形参,第二个实参赋值给第二个形参 。。。
关键字参数
- 关键字参数,可以不按照形参定义的顺序去传递,而直接根据参数名去传递参数
- 位置参数和关键字参数可以混合使用
- 合使用关键字和位置参数时,必须将位置参数写到前面
1
2
3
4
5
6
7
8
9
10
11
12def my_sum(a, b):
print("a + b =", a + b)
# 位置参数,根据传入参数的位置去赋值给函数赋值
my_sum(1, 2) # a + b = 3
# 关键字参数:根据函数的参数的名字来给参数赋值,位置参数和关键字参数可以混合使用
my_sum(2, b=10) # a + b = 12
# 位置参数找到对应的位置赋值之后,关键字参数不能再给位置参数赋值赋值的参数继续赋值
my_sum(2, a=10) # TypeError: my_sum() got multiple values for argument 'a'
- 位置参数
- 函数在调用时,解析器不会检查实参的类型,实参可以传递任意类型的对象,由于字符串可以传入任意类型,所以方法中可能会因为传入的参数报异常
在函数中对形参进行重新赋值,不会影响其他的变量,如果形参执行的是一个对象,当我们通过形参去修改对象时,会影响到所有指向该对象的变量
1
2
3
4
5
6
7
8
9
10
11
12
13# 创建一个函数,用于修改列表的值
#此时由于传入的是一个 ,当在函数中修改的时候,外面的传入的实参的值也会被修改
#想要值函数外面传入的值不被修改,可以使用浅复制,将列表浅复制的值传入函数中
def update(a):
a[0] = 200
a = [10, 20, 30]
update(a)
print(a) # [200, 20, 30]
b = [100, 300, 500]
update(b.copy())
print(b) # [100, 300, 500]
4、不定长参数(和Java中的可变参数差不多)
在定义函数时,可以在形参前边加上一个*,这样这个形参将会获取到所有的实参,它将会将所有的实参保存到一个元组中
1
2
3# 和元组的解包差不多
a, b, *c = (1, 2, 3, 4)
print(a, b, c) # 1 2 [3, 4]
*a会接受所有的位置实参,并且会将这些实参统一保存到一个元组中(装包)
1
2
3
4
5# *a会接受所有的位置实参,并且会将这些实参统一保存到一个元组中(装包)
def my_fn(*a):
print("a =", type(a)) # a = <class 'tuple'>
my_fn(1, 2, 3, 4)
带星号的形参只能有一个,带星号的参数,可以和其他参数配合使用,第一个参数给a,剩下的都保存到b的元组中
1
2
3
4
5
6
7# 带 * 号的形参只能有一个
def my_fn(a,*b):
print("a =", type(a)) # a = <class 'int'>
print("b =", type(b)) # b = <class 'tuple'>
print("a =", a) # a = 1
print("b =", b) # b = (2, 3, 4)
my_fn(1, 2, 3, 4)
可变参数不是必须写在最后,但是注意,带*的参数后的所有参数,必须以关键字参数的形式传递,不然会报错,最好把不定长参数放在最后面
1
2
3
4
5
6
7
8
9
10# 第一个参数给a,剩下的位置参数给b的元组,c必须使用关键字参数
def my_fn(a, *b, c):
print("a =", type(a)) # a = <class 'int'>
print("b =", type(b)) # b = <class 'tuple'>
print("c =", type(c)) # b = <class 'int'>
print("a =", a) # a = 1
print("b =", b) # b = (2, 3)
print("c =", c) # b = 4
my_fn(1, 2, 3, c=4)
如果在形参的开头直接写一个( , ),则要求我们的所有的参数必须以关键字参数的形式传递,如果需要用户在传入实参的时候指定形参的名字,可以在所有参数前面加上一个 ( , ), ( *)形参只能接收位置参数,而不能接收关键字参数
1
2
3
4
5
6
7
8
9
10# 如果在形参的开头直接写一个*,则要求我们的所有的参数必须以关键字参数的形式传递
def my_fn(*, a, b, c):
print("a =", type(a)) # a = <class 'int'>
print("b =", type(b)) # b = <class 'int'>
print("c =", type(c)) # b = <class 'int'>
print("a =", a) # a = 1
print("b =", b) # b = 2
print("c =", c) # b = 3
my_fn(a=1, b=2, c=3)
( )形参可以接收其他的关键字参数,它会将这些参数统一保存到一个字典中,字典的key就是参数的名字,字典的value就是参数的值,( )形参只能有一个,并且必须写在所有参数的最后
1
2
3
4
5
6def my_fn2(*, a, b, **c):
print("a =", a, "type =", type(a)) # b = 1 type = <class 'int'>
print("b =", b, "type =", type(b)) # b = 2 type = <class 'int'>
print("c =", c, "type =", type(c)) # c = {'c': 3, 'd': 4, 'f': 5} type = <class 'dict'>
my_fn2(a=1, b=2, c=3, d=4, f=5)
参数的解包(拆包)
【 元组名 】:通过( )来将元组作为参数传入,这里要求序列中元素的个数必须和形参的个数的一致
元组中元素的个数必须要和函数中形参的个数一样
【 *字典名 】:通过( )来将字典作为参数传入,字典中的key必须要和形参的名字对应
字典中参数的个数必须要和函数中参数的个数一样,且字典中的key必须要和参数对应
1
2
3
4
5
6
7
8
9
10def my_fn3(a, b, c):
print("a + b + c =", a + b + c)
#使用元组进行参数解包
t = (1, 2, 3)
my_fn3(*t)
#使用字典进行参数解包
t = {'a': 2, 'b': 3, 'c': 10}
my_fn3(**t)
5、返回值
- 返回值,返回值就是函数执行以后返回的结果,可以通过 return 来指定函数的返回值,可以之间使用函数的返回值,也可以通过一个变量来接收函数的返回值
- return 后边跟什么值,函数就会返回什么值,,return 后边可以跟任意的对象,返回值甚至可以是一个函数
- 如果仅仅写一个return 或者 不写return,则相当于return None
在函数中,return后的代码都不会执行,return 一旦执行函数自动结束,能达到break的效果
- break:用来退出当前循环
- continue:用来跳过当前循环
return:用来结束函数,在函数中,也能达到结束循环的效果,return一旦执行,return 之后的语句将不再执行
1
2
3
4
5
6
7
8def foreach_test(a):
for i in range(a):
if i == 5:
return
else:
print(i, end=" ") # 0 1 2 3 4
foreach_test(10)
函数调用的一个区别
1
2
3
4
5
6def test():
return
# test是函数对象,打印test实际是在打印函数对象 <function test at 0x000001EEF74B1D30>
# test()是在调用函数,打印test()实际上是在打印test()函数的返回值
print(test) # <function test at 0x000001EEF74B1D30>
print(test()) # None
6、文档字符串
help()是Python中的内置函数,通过help()函数可以查询python中的函数的用法
- 语法:help(函数对象)
help(print) # 获取print()函数的使用说明
1
2
3
4
5
6
7
8
9
10
11
12
13help(print)
"""
Help on built-in function print in module builtins:
print(...)
print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
Prints the values to a stream, or to sys.stdout by default.
Optional keyword arguments:
file: a file-like object (stream); defaults to the current sys.stdout.
sep: string inserted between values, default a space.
end: string appended after the last value, default a newline.
flush: whether to forcibly flush the stream.
"""
文档字符串(doc str):文档注释
- 在定义函数时,可以在函数内部编写文档字符串,文档字符串就是函数的说明
- 当我们编写了文档字符串时,就可以通过help()函数来查看函数的说明
- 文档字符串非常简单,其实直接在函数的第一行写一个字符串就是文档字符串
格式:
- 参数后面添加参数类型,只是为了提示该参数需要传入什么类型的值,不是强制要求传入参数类型,只是为了提示用户
- 后面写的返回值类型可以帮助用户了解该函数的返回值类型:
- 在参数后添加参数类型,函数后面添加返回值类型,是为了提示用户,这些类型只是提醒作用,不是强制的作用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def 函数名字(参数1: 参数类型, 参数2: 参数类型, 参数3) -> 函数返回值类型:
"""
函数的作用,用来求三个参数的和
函数的参数:
a:参数1
b:参数2
c:参数3
"""
help(my_sum_test)
'''
my_sum_test(a: int, b: str, c) -> str
函数的作用,用来求三个参数的和
函数的参数:
a:参数1
b:参数2
c:参数3
'''
7、作用域和命名空间
作用域(scope):作用域指的是变量生效的区域
全局作用域(全局变量):
全局作用域在程序执行时创建,在程序执行结束时销毁
所有函数以外的区域都是全局作用域
在全局作用域中定义的变量,都属于全局变量,全局变量可以在程序的任意位置被访问
函数作用域(局部变量):
函数作用域在函数调用时创建,在调用结束时销毁
函数每调用一次就会产生一个新的函数作用域
在函数作用域中定义的变量,都是局部变量,它只能在函数内部被访问
变量的查找:
- 当我们使用变量时,会优先在当前作用域中寻找该变量,如果有则使用,如果没有则继续去上一级作用域中寻找,如果有则使用,如果依然没有则继续去上一级作用域中寻找,以此类推直到找到全局作用域,依然没有找到,则会抛出异常
命名空间:
由于命名空间返回的是一个字典,保存的是(变量名字 :变量值),所以可以使用字典的相关方法来修改变量的值,单一般 不这样使用
命名空间指的是变量存储的位置,
每一个变量都需要存储到指定的命名空间当中,
每一个作用域都会有一个它对应的命名空间,全局命名空间,用来保存全局变量。
函数命名空间用来保存函数中的变量,命名空间实际上就是一个字典,是一个专门用来存储变量的字典
locals():
- 用来获取当前作用域的命名空间
- 如果在全局作用域中调用locals()则获取全局命名空间
- 如果在函数作用域中调用locals()则获取函数命名空间。
locals()返回的是一个字典
1
2
3
4
5
6
7
8
9
10
11# 命名空间
c = 100
# 在全局作用域中使用locals
a = locals()
# 函数中使用locals()
def my_locals():
c = 10
print(locals())
my_locals()
globals():
- 函数可以用来在任意位置获取全局命名空间
- 在函数中使用globals()返回的全局作用域,此时,函数中的局部作用域获取不到
1 | # 命名空间 |
8、递归
- 递归式的函数
- 递归简单理解就是自己去引用自己!
- 递归式函数,在函数中自己调用自己!
- 递归是解决问题的一种方式,它和循环很像,它的整体思想是,将一个大问题分解为一个个的小问题,直到问题无法分解时,再去解决问题
递归式函数的两个要件
- 基线条件:问题可以被分解为的最小问题,当满足基线条件时,递归就不在执行了
- 递归条件:将问题继续分解的条件
递归和循环类似,基本是可以互相代替的,
循环编写起来比较容易,阅读起来稍难
递归编写起来难,但是方便阅读
9、函数式编程
- 在Python中,函数是一等对象
- 一等对象一般都会具有如下特点:
- ① 对象是在运行时创建的
- ② 能赋值给变量或作为数据结构中的元素
- ③ 能作为参数传递
- ④ 能作为返回值返回
高阶函数
- 高阶函数至少要符合以下两个特点中的一个
- ① 接收一个或多个函数作为参数
- ② 将函数作为返回值返回
- 高阶函数至少要符合以下两个特点中的一个
装饰器
10、高阶函数
- 和Java中的比较器有点类似,当需要对集合或数组等进行自定义排序的时候,需要闯入一个匿名内部类来实现对集合或数组的自定义操作
高阶函数:接收函数作为参数,或者将函数作为返回值的函数是高阶函数
当我们使用一个函数作为参数时,实际上是将指定的代码传递进了目标函数
- 当传入的函数参数是一些简单的操作的时候,可以使用lambda表达式来实现。如果是对于一些复杂的操作,这时可以在外面写一个函数保存需要执行的操作,再将该函数传入进去,如下分别写了自定义一个函数参数和使用lambda表达式传入匿名函数的方式来实现高阶函数的操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 写一个筛选集合为偶数的方法
def find_even_number(i):
return i % 2 == 0
#筛选集合的方法
def filter_even_number(fn, list):
new_list = []
for i in list:
if fn(i):
new_list.append(i)
return new_list
#将find_even_number函数作为参数传入filter_even_number函数中
print(filter_even_number(find_even_number, [1, 2, 3, 4, 5, 6])) # [2, 4, 6]
匿名函数 lambda 函数表达式 (语法糖)
lambda函数表达式专门用来创建一些简单的函数,他是函数创建的又一种方式
1
语法:lambda 参数列表 : 返回值
匿名函数一般都是作为参数使用,其他地方一般不会使用
filter():可以从序列中过滤出符合条件的元素,保存到一个新的序列中
- 参数:
- 函数,根据该函数来过滤序列(可迭代的结构)
- 需要过滤的序列(可迭代的结构)
- 返回值:
- 过滤后的新序列(可迭代的结构)
- 参数:
- map():map()函数可以对可跌倒对象中的所有元素做指定的操作,然后将其添加到一个新的对象中返回
sort():该方法用来对列表中的元素进行排序、sort()方法默认是直接比较列表中的元素的大小、升序
- 使用sort()对列表进行排序,原有的列表将会被改变,sorted()函数和sort函数用法基本一致,但不会改变列表
- 该方法需要使用列表或集合 .sort()
- 参数:在sort()可以接收一个关键字参数 , key
- 对于列表中元素类型不一样的话,不传入参数指定什么排列的话,输出列表会和没有排序之前基本一样
- key需要一个函数作为参数,当设置了函数作为参数,每次都会以列表中的一个元素作为参数来调用函数,并且使用函数的返回值来比较元素的大小
- sorted():这个函数和sort()的用法基本一致,但是sorted()可以对任意的序列进行排序,并且使用sorted()排序不会影响原来的对象,而是返回一个新对象
1 | # 语法:lambda 参数列表 : 返回值 |
11、闭包函数
将函数作为返回值返回,也是一种高阶函数,可以将一些私有的数据藏到的闭包中,只有当接收到了返回值的时候才能访问一些变量
在函数内部定义内部函数将内部函数返回,这时在外部可以使用一个变量接收函数返回的内部函数,该返回值可以范围函数中定义的变量,因为返回的是函数的内部函数,作用域是由内到外的访问
- 形成闭包的要件
- 函数嵌套
- 将内部函数作为返回值返回
- 内部函数必须要使用到外部函数的变量
1 | # 闭包函数的使用 |
12、装饰器(生成代理类)
和Java中的动态代理类实效果差不多,需要在不修改源代码的前提下在原有的函数下添加新的功能
以下的静态代理、动态代理、使用注解都是我通过学习Java后为了更好理解python中的装饰器写的下·
静态代理
只能为一个函数使用
1 | def add(a, b): |
动态代理
可以给不同的函数使用,随着传入的函数分生成不同函数的装饰器,装饰器的决定由调用者决定
1 | def add(a, b): |
使用注解
使用注解会修改函数原来执行结果,但是不会修改函数的源代码,想要原来的执行结果只需要将注解删除即可
使用方法:@装饰器(装饰器是一个函数,使用:@函数名)
- 使用注解添加多个装饰器执行的先后顺序:
- 为一个函数指定多个装饰器,这样函数将会安装从内向外的顺序被装饰
- 谁的装饰器在前面,就谁的前置通知先执行,前置通知的顺序是装饰器在前面就先执行
- 谁的装饰器在前面,谁的后置通知就最后执行,后置通知的顺序是装饰器在前面就后执行
使用一个装饰器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21def proxy_function(old):
# 像Java中的代理类对象
def proxy(*a, **b):
print("前置通知", end=" ")
result = old(*a, **b)
print("后置通知", end=" ")
return result
return proxy
def delete(a, b):
"""
求任意两个数相减
"""
print("这里是delete函数的内部", end=" ")
return a - b
print(delete(15, 10)) # 前置通知 这里是delete函数的内部 后置通知 5
使用两个及以上装饰器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32def proxy_function(old):
# 像Java中的代理类对象
def proxy(*a, **b):
print("前置通知", end=" ")
result = old(*a, **b)
print("后置通知", end=" ")
return result
return proxy
def proxy_function2(old):
# 像Java中的代理类对象
def proxy2(*a, **b):
print("前置通知2", end=" ")
result = old(*a, **b)
print("后置通知2", end=" ")
return result
return proxy2
# 给函数添加两个装饰器
def delete(a, b):
"""
求任意两个数相减
"""
print("这里是delete函数的内部", end=" ")
return a - b
print(delete(15, 10)) # 前置通知 前置通知2 这里是delete函数的内部 后置通知2 后置通知 5
六、面向对象
1、什么是对象
- 对象是内存中专门用来存储数据的一块区域。
- 对象中可以存放各种数据(比如:数字、布尔值、代码)
- 对象由三部分组成:
- 对象的标识(id)
- 对象的类型(type)
- 对象的值(value)
2、面向对象
Python是一门面向对象的编程语言
所谓的面向对象的语言,简单理解就是语言中的所有操作都是通过对象来进行的
面向过程的编程的语言
- 面向过程指将我们的程序的逻辑分解为一个一个的步骤,通过对每个步骤的抽象,来完成程序
- 例子:
- 孩子上学
- 妈妈起床
- 妈妈上厕所
- 妈妈洗漱
- 妈妈做早饭
- 妈妈叫孩子起床
- 孩子上厕所
- 孩子要洗漱
- 孩子吃饭
- 孩子背着书包上学校
面向过程的编程思想将一个功能分解为一个一个小的步骤,我们通过完成一个一个的小的步骤来完成一个程序
- 这种编程方式,符合我们人类的思维,编写起来相对比较简单
- 但是这种方式编写代码的往往只适用于一个功能,如果要在实现别的功能,即使功能相差极小,也往往要重新编写代码,所以它可复用性比较低,并且难于维护
面向对象的编程语言
- 面向对象的编程语言,关注的是对象,而不关注过程
- 对于面向对象的语言来说,一切都是对象
- 例子:
- 孩他妈起床叫孩子上学
- 面向对象的编程思想,将所有的功能统一保存到对应的对象中,比如,妈妈功能保存到妈妈的对象中,孩子的功能保存到孩子对象中,要使用某个功能,直接找到对应的对象即可
- 这种方式编写的代码,比较容易阅读,并且比较易于维护,容易复用。
- 但是这种方式编写,不太符合常规的思维,编写起来稍微麻烦一点
- 简单归纳一下,面向对象的思想
- 找对象
- 搞对象
3、类
1 | 我们目前所学习的对象都是Python内置的对象 |
定义一个简单的类
1
2
3
4
5# 定义一个简单的类
class MyClass:
pass # 使用pass占位符
print(MyClass) # <class '__main__.MyClass'>使用类创建一个对象
1
2
3
4
5class MyClass:
pass # 使用pass占位符
mc = MyClass()
print(mc)#<__main__.MyClass object at 0x00000187AB4AF5E0>isinstance()用来检查一个对象是否是一个类的实例
1
2
3
4
5
6
7# 定义一个简单的类
class MyClass:
pass # 使用pass占位符
mc = MyClass()
print(isinstance(mc, MyClass)) # True
print(isinstance(mc, str)) # False可以向对象中添加变量,对象中的变量称为属性(不推荐使用,了解即可)
语法:对象.属性名 = 属性值
1
2
3
4
5
6
7# 定义一个简单的类
class MyClass:
pass # 使用pass占位符
mc = MyClass()
mc.name = "李白"
print(mc.name) # 李白
类创建对象的流程
类的定义
类和对象都是对现实生活中的事物或程序中的内容的抽象
实际上所有的事物都由两部分构成:
- 数据(属性)
.行为(方法)
在类的代码块中,我们可以定义变量和函数,变量会成为该类实例的公共属性,所有的该类实例都可以通过 对象.属性名 的形式访问 ,函数会成为该类实例的公共方法,所有该类实例都可以通过 对象.方法名() 的形式调用方法
注意:方法调用时,第一个参数由解析器自动传递,所以定义方法时,至少要定义一个形参!
实例为什么能访问到类中的属性和方法
类中定义的属性和方法都是公共的,任何该类实例都可以访问属性和方法查找的流程
当我们调用一个对象的属性时,解析器会先在当前对象中寻找是否含有该属性,
- 如果有,则直接返回当前的对象的属性值,
如果没有,则去当前对象的类对象中去寻找,如果有则返回类对象的属性值
- 如果类对象中依然没有,则报错!
类对象和实例对象中都可以保存属性(方法)
- 如果这个属性(方法)是所有的实例共享的,则应该将其保存到类对象中
- 如果这个属性(方法)是某个实例独有,则应该保存到实例对象中
一般情况下,属性保存到实例对象中,而方法需要保存到类对象中
1 | # 定义一个表示人的类 |
4、对象初始化
- p1 = Person()的运行流程
- 创建一个变量
- 在内存中创建一个新对象
- ( init )(self)方法执行,和Java中的构造函数类似,只要对象一创建该方法就会被执行
- 将对象的id赋值给变量
5、1-5总结
以下总结对比Java来理解的
类的基本结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class 类名([父类]):
公共的属性...
# 对象的初始化方法,类似Java中的构造函数
def __init__(self, ...):
...
# 其他的方法
def method_1(self, ...):
...
def method_2(self, ...):
...
...
属性:
- 在Java中定义公共属性,这个属性是属于每个对象所独有的,Java中定义属性可以不赋初始值
- 属性
- 在python中,对于一个类所共有的属性可以定义在类的内部
- 以下情况为定义一个Person类来解释的
- 一般可以直接定义在类内部的属性有:
- 比如一个人有两条腿可以定义一个属性为(leg = 2)
- 一般不可以在内部的属性有,对于一些属性是不一样的却又是共有的属性,只是属性值不一样
- 比如每个人的名字都不一样,所以一般不在类的内部定义人的名字这个属性
- 这时可以添加定义一些特殊方法
- 特殊方法格式:以( 开头, )结尾的方法,两个下划线开头的函数是声明该属性为私有
- 特殊方法不需要我们自己调用,特殊方法将会在特殊的时刻自动调用
- 一般可以直接定义在类内部的属性有:
方法:
- 在Java中方法可以没有参数
- 在python中只要定义了方法一定要定义一个形参,方法在被调用的时候不需要往该方法中注入实参,这个参数是由解析器自动传入的,传入每个对象的id,用于区分每个对象
init方法 :和Java中的构造函数差不多,在 init 方法中有一个self形参,系统会自动将创建的对象的id的值传入进去
- 在Java中可以定义多个构造函数,有参构造和无参构造
- 在python中只能定义一个init方法
python方法的self参数
init 方法中的self参数
- 在init方法中可以使用self参数定义属性给属性赋值,这时候定义的属性和值由于是self参数定义的,所以这个参数的属性和值可以属于不同的属性
- init方法中定义的属性可以在类外面定义的对象通过(对象.属性名)调用
- 类中其他方法中的self参数
- init以外的方法使用self参数也可以定义属性,但和init方法中有区别,在除init方法使用self参数定义的属性来定义的属性,除了该方法内部可以使用定义的属性,其他方法不能访问到,类外面定义对象也不能调用,**所以在除了init方法的其他方法通过self参数定义属性的属性都是没有意义的**
6、封装
封装1
6.1 — 6.5中定义的属性和方法相当于Java中的public,是公开的
下面的封装实现的是要将一些不希望被外界访问的方法和属性封装起来,设置这些属性和方法的访问权限
封装是面向对象的三大特性
封装是指隐藏对象中一些不希望被外部所访问到的属性或方法
- 如何隐藏一个对象中的属性?
- 如何获取(修改)对象中的属性?
- 需要提供一个getter和setter方法使外部可以访问到属性
- getter 获取对象中的指定属性(get_属性名)
- setter 用来设置对象的指定属性(set_属性名)
- 需要提供一个getter和setter方法使外部可以访问到属性
- 使用封装,确实增加了类的定义的复杂程度,但是它也确保了数据的安全性
- 隐藏了属性名,使调用者无法随意的修改对象中的属性
- 增加了getter和setter方法,很好的控制的属性是否是只读的
- 如果希望属性是只读的,则可以直接去掉setter方法
- 如果希望属性不能被外部访问,则可以直接去掉getter方法
- 使用setter方法设置属性,可以增加数据的验证,确保数据的值是正确的
- 使用getter方法获取属性,使用setter方法设置属性
- 可以在读取属性和修改属性的同时做一些其他的处理
- 使用getter方法可以表示一些计算的属性
1 | # 使用定义的属性名和init方法的形参名不一样的方法来创建属性,达到封装的一部分效果 |
封装2
封装1中解决属性的问题虽然得到了部分解决,但是要是调用者知道了开发者内部自定义的属性名字。调用者还是能修改其代码。
可以为对象的属性使用双下划线开头,__xxx
双下划线开头的属性,是对象的隐藏属性,隐藏属性只能在类的内部访问,无法通过对象访问
其实隐藏属性只不过是Python自动为属性改了一个名字
- 实际上是将名字修改为了,类名 _ 属性名 比如 name -> _Personname
使用__开头的属性,实际上依然可以在外部访问,所以这种方式我们一般不用
一般我们会将一些私有属性(不希望被外部访问的属性)以_开头
一般情况下,使用_开头的属性都是私有属性,没有特殊需要不要修改私有属性,只是设提示作用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class Person:
def __init__(self, name):
self.__name = name
def person_print(self):
print(self.__name)
def set_name(self, name):
self.__name = name
def get_name(self):
return self.__name
person = Person("韩信")
print(person._Person__name) # 韩信
print(person.get_name()) # 韩信
封装3
使用property装饰器可以为属性生成get方法,在创建对象调用属性的时候直接( 对象名.属性名 )就会调用对应的get方法
property装饰器,用来将一个get方法,转换为对象的属性
- 添加为property装饰器以后,我们就可以像调用属性一样使用get方法
- 使用property装饰的方法,必须和属性名是一样的
setter方法的装饰器:@属性名.setter
- 使用setter装饰器可以为属性生成对应set方法,用创建的对象给属性修改值的时候直接( 对象名.属性名 = 值 )就可以修改属性的值了
注意:在属性使用setter装饰器之前需要对属性使用property装饰器,不然setter装饰器不能使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17class Person:
def __init__(self, name):
self.__name = name
def name(self):
return self.__name
def name(self, name):
self.__name = name
person = Person("韩信")
print(person.name) # 韩信
person.name = "李白"
print(person.name) # 李白
7、继承
有一个类,能够实现我们需要的大部分功能,但是不能实现全部功能,如何能让这个类来实现全部的功能呢?
直接修改这个类,在这个类中添加我们需要的功能
- 修改起来会比较麻烦,并且会违反OCP原则
直接创建一个新的类
- 创建一个新的类比较麻烦,并且需要大量的进行复制粘贴,会出现大量的重复性代码
直接从Animal类中来继承它的属性和方法
继承是面向对象三大特性之一
通过继承我们可以使一个类获取到其他类中的属性和方法
定义类时,可以在类名后的括号中指定当前类的父类(超类、基类、super)
子类(衍生类)可以直接继承父类中的所有的属性和方法
- 通过继承可以直接让子类获取到父类的方法或属性,避免编写重复性的代码,并且也符合OCP原则
- 所以我们经常需要通过继承来对一个类进行扩展
- 在创建类时,如果省略了父类,则默认父类为object,object是所有类的父类,所有类都继承自object
issubclass():检查一个类是否是另一个类的子类
- issubclass(类A, 类B):如果类A为类B的子类返回True,否则返回False
- 所有对象都是Object的实例
父类中的所有方法都会被子类继承,包括特殊方法,也可以重写特殊方法(从最开始的object类下来,只要是往上的类都会被继承)
super() 可以用来获取当前类的父类,并且通过super()返回对象调用父类方法时,不需要传递self,但需要传递父类init方法中的其他参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18class Animal:
def __init__(self, id):
self._id = id
def say(self):
print("动物会叫")
# 写一个狗类继承动物类
class Dog(Animal):
def __init__(self, age, id):
super().__init__(id)
self._age = age
def say(self):
print("狗:汪汪汪")
blackDog = Black_Dog(19, 1)
重写
- 如果在子类中如果有和父类同名的方法,则通过子类实例去调用方法时,会调用子类的方法而不是父类的方法,这个特点我们成为叫做方法的重写(覆盖,override)
当我们调用一个对象的方法时,会优先去当前对象中寻找是否具有该方法,如果有则直接调用,
如果没有,则去当前对象的父类中寻找,如果父类中有则直接调用父类中的方法,
如果没有,则去父类的父类中寻找,以此类推,直到找到object,如果依然没有找到,则报错
8、多重继承(了解下)
一个子类继承多个父类
在Python中是支持多重继承的,也就是我们可以为一个类同时指定多个父类
可以在类名的()后边添加多个类,来实现多重继承
多重继承,会使子类同时拥有多个父类,并且会获取到所有父类中的方法
在开发中没有特殊的情况,应该尽量避免使用多重继承,因为多重继承会让我们的代码过于复杂
如果多个父类中有同名的方法,则会现在第一个父类中寻找,然后找第二个,然后找第三个。。。
前边父类的方法会覆盖后边父类的方法
9、多态
多态是面向对象的三大特征之一,多态从字面上理解是多种形态,一个对象可以以不同的形态去呈现
当由一个方法,脸面需要传入一个类,这个形参需要调用里面的一个属性进行输出,这时使用多态,只要传入的参数的类中有该属性,该方法里面就能输出属性
和Java中的反射有点类似,对于一个方法传入的是哪个类就执行哪个类中的方法
1 | class A: |
10、类中的属性和方法
- 属性:
- 类属性:
- 直接在类中定义的属性是类属性(一般不适用,除非有一些公共的属性,值一样的属性)
- 类属性可以通过类或类的实例访问到
- 但是类属性只能通过类对象来修改,无法通过实例对象修改
- 实例属性:
- 通过实例对象添加的属性属于实例属性
- 实例属性只能通过实例对象来访问和修改,类对象无法访问修改
- 类属性:
方法
- 实例方法:
- 在类中定义,以self为第一个参数的方法都是实例方法
- 实例方法在调用时,Python会将调用对象作为self传入
- 实例方法可以通过实例和类去调用
- 当通过实例调用时,会自动将当前调用对象作为self传入
- 当通过类调用时,不会自动传递self,此时我们必须手动传递self
- 实例方法:
- 类方法:
- 在类内部使用 @classmethod 来修饰的方法属于类方法
- 类方法的第一个参数是cls,也会被自动传递,cls就是当前的类对象
- 类方法和实例方法的区别,实例方法的第一个参数是self,而类方法的第一个参数是cls
- **类方法可以通过类去调用,也可以通过实例调用,**没有区别
- 静态方法:
- 在类中使用 @staticmethod 来修饰的方法属于静态方法
- 静态方法不需要指定任何的默认参数,静态方法可以通过类和实例去调用
- 静态方法,基本上是一个和当前类无关的方法,它只是一个保存到当前类中的函数
- 静态方法一般都是一些工具方法,和当前类无关
1 | """ |
11、垃圾回收
- 就像我们生活中会产生垃圾一样,程序在运行过程当中也会产生垃圾
- 程序运行过程中产生的垃圾会影响到程序的运行的运行性能,所以这些垃圾必须被及时清理
- 没用的东西就是垃圾
- 在程序中没有被引用的对象就是垃圾,这种垃圾对象过多以后会影响到程序的运行的性能
- 所以我们必须进行及时的垃圾回收,所谓的垃圾回收就是讲垃圾对象从内存中删除
- 在Python中有自动的垃圾回收机制,它会自动将这些没有被引用的对象删除,
- 所以我们不用手动处理垃圾回收
- 当垃圾回收的时候会自动执行—del—方法,del是一个特殊方法,它会在对象被垃圾回收前调用
12、特殊方法
1 | # object.__add__(self, other) |
特殊方法,也称为魔术方法
特殊方法都是使用__开头和结尾的
特殊方法一般不需要我们手动调用,需要在一些特殊情况下自动执行
- gt会在对象做大于比较的时候调用,和Java中的比较器差不多,可以比较自定义类的大小
- object. bool(self),可以通过bool来指定对象转换为布尔值的情况
- str(self):和Java中的toString()方法效果差不多
repr()这个特殊方法会在对当前对象使用repr()函数时调用,它的作用是指定对象在 ‘交互模式’中直接输出的效果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35# __len__()获取对象的长度
class Person:
def __init__(self, id, name):
self._name = name
self._id = id
def name(self):
return self._name
def name(self, name):
self._name = name
# __gt__会在对象做大于比较的时候调用,该方法的返回值将会作为比较的结果
# 他需要两个参数,一个self表示当前对象,other表示和当前对象比较的对象
# self > other : >左边的为self,>右边的为other
def __gt__(self, other):
# 该方法和Java中的比较器类似,可以比较自定义类的大小
return self._id > other._id
# object.__bool__(self),可以通过bool来指定对象转换为布尔值的情况
def __bool__(self):
# 当id>18返回True,否则返回False
return self._id > 18
# 和Java中的toString()方法差不多
def __str__(self):
return 'Person [name=%s , id=%d]' % (self._name, self._id)
# __repr__()这个特殊方法会在对当前对象使用repr()函数时调用
# 它的作用是指定对象在 ‘交互模式’中直接输出的效果
def __repr__(self):
return 'Person [name=%s , id=%d]' % (self._name, self._id)
剩余方法的使用可以上网查询使用
13、模块
模块(module)
- 模块化,模块化指将一个完整的程序分解为一个一个小的模块,通过将模块组合,来搭建出一个完整的程序
- 模块化和不模块化的区别
- 不采用模块化,统一将所有的代码编写到一个文件中
- 采用模块化,将程序分别编写到多个文件中
模块化的有点:
- 方便开发
- 方便维护
- 模块可以复用!
在Python中一个py文件就是一个模块,要想创建模块,实际上就是创建一个python文件
- 注意:模块名要符号标识符的规范
- 在一个模块中引入外部模块
- import 模块名 (模块名,就是python文件的名字,注意不要py)
- import 模块名 as 模块别名
- 可以引入同一个模块多次,但是模块的实例只会创建一个,模块中代码只会执行一次
- import可以在程序的任意位置调用,但是一般情况下,import语句都会统一写在程序的开头
- 在每一个模块内部都有一个name属性,通过这个属性可以获取到模块的名字
- name 属性值为 main的模块是主模块,一个程序中只会有一个主模块,主模块就是我们直接通过 python 执行的模块,谁在执行,谁就是主模块
14、模块的使用
1 | import xxx # 引入一个模块 |
1 | # 可以在模块中定义变量,在模块中定义的变量,在引入模块后,就可以直接使用了 |
15、包
1 | #导入包下面的init方法中的变量和方法 |
包 Package:包也是一个模块
当我们模块中代码过多时,或者一个模块需要被分解为多个模块时,这时就需要使用到包
普通的模块就是一个py文件,而包是一个文件夹
包中必须要一个一个 init.py 这个文件,这个文件中可以包含有包中的主要内容
16、Python标准库
思想:开箱即用
- 为了实现开箱即用的思想,Python中为我们提供了一个模块的标准库,在这个标准库中,有很多很强大的模块我们可以直接使用,并且标准库会随Python的安装一同安装
sys模块:提供了一些变量和函数,使我们可以获取到Python解析器的信息,或者通过函数来操控python解析器
sys.argv:获取执行代码时,命令行中所包含的参数,
返回的是一个列表,列表中保存了当前命令的所有参数
sys.module:获取当前执行文件引入的所有模块
返回的是一个字典,字典的key是模块的名字,字典的value是模块的对象
sys.path:返回一个列表,列表中保存模块的搜索路径
sys.exit:函数用来退出程序
pprint模块:
- pprint():对打印的数据输出进行简单的格式化输出
os模块(operator):可以让我们对系统进行访问
- os.environ:这个可以获取到系统的环境变量(os.environ[‘path’])
- os.system:可以用来执行操作系统的名字(可以和cmd一样执行命令行)
- 具体的模块使用可以去标准库寻找使用方法
七、异常
1、异常
- 程序在运行过程当中,不可避免的会出现一些错误,比如:
- 使用了没有赋值过的变量
- 使用了不存在的索引
- 除0
- …
- 这些错误在程序中,我们称其为异常。
- 程序运行过程中,一旦出现异常将会导致程序立即终止,异常以后的代码全部都不会执行!
2、处理异常
程序运行时出现异常,目的并不是让我们的程序直接终止!
Python是希望在出现异常时,我们可以编写代码来对异常进行处理!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18try语句
try:
代码块(可能出现错误的语句)
except 异常类型 as 异常名:
代码块(出现错误以后的处理方式)
except 异常类型 as 异常名:
代码块(出现错误以后的处理方式)
except 异常类型 as 异常名:
代码块(出现错误以后的处理方式)
else:
代码块(没出错时要执行的语句)
finally:
代码块(该代码块总会执行)
"""
try是必须的 else语句有没有都行
except和finally至少有一个
"""可以将可能出错的代码放入到try语句,这样如果代码没有错误,则会正常执行,如果出现错误,则会执行expect子句中的代码,这样我们就可以通过代码来处理异常避免因为一个异常导致整个程序的终止
2、异常传播
当在函数中出现异常时,如果在函数中对异常进行了处理,则异常不会再继续传播,
如果函数中没有对异常进行处理,则异常会继续向函数调用处传播
如果函数调用处处理了异常,则不再传播
如果没有处理则继续向调用处传播, 直到传递到全局作用域(主模块)如果依然没有处理,则程序终止,并且显示异常信息
当程序运行过程中出现异常以后,所有的异常信息会被保存一个专门的异常对象中
而异常传播时,实际上就是异常对象抛给了调用处
- 比如 :
- ZeroDivisionError类的对象专门用来表示除0的异常
- NameError类的对象专门用来处理变量错误的异常
- ….
- 比如 :
- 在Python为我们提供了多个异常对象
3、抛出异常和自定义异常
可以使用 raise 语句来抛出异常:raise语句后需要跟一个异常类 或 异常的实例
也可以自定义异常类,只需要创建一个类继承Exception即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19# 也可以自定义异常类,只需要创建一个类继承Exception即可
class MyError(Exception):
pass
def add(a, b):
# 如果a和b中有负数,就向调用处抛出异常
if a < 0 or b < 0:
# raise用于向外部抛出异常,后边可以跟一个异常类,或异常类的实例
# raise Exception
# 抛出异常的目的,告诉调用者这里调用时出现问题,希望你自己处理一下
# raise Exception('两个参数中不能有负数!')
raise MyError('自定义的异常')
# 也可以通过if else来代替异常的处理
# return None
r = a + b
return r
print(add(123, 456))
八、文件
- 通过Python程序来对计算机中的各种文件进行增删改查的操作
- I/O(Input / Output)
- 操作文件的步骤:
- 打开文件
- 对文件进行各种操作(读、写),然后保存
- 关闭文件