Python基础(一)
第一章 计算机基础知识
计算机是什么
在现实生活中,越来越无法离开计算机了
电脑、笔记本、手机、游戏机、汽车导航、智能电视 。。。
计算机就是一个用来计算的机器!
目前来讲,计算机只能根据人类的指令来完成各种操作,人让它干嘛他就得干嘛
所以我们学习计算机,就是学习如何控制计算机!
计算机的组成
计算机由两部分组成:硬件 和 软件
硬件包含:键盘、鼠标、显示器、CPU、主板、内存、硬盘 。。。类比人的各种器官
硬件是看的见摸得着的
软件包含:系统软件(windows、macOS、Linux)和应用软件(office 、QQ、绝地求生)
软件是看的见摸不着的,软件负责控制计算机中的硬件
计算机的使用方式
我们必须要通过软件来对计算机完成各种操作,
但是注意,软件中并不是所有的功能都会对用户开放,
用户需要调用软件提供的接口(Interface 交互界面)来操作计算机
软件很大,里面功能很多,有能让人看的,又不能让人看到的。接口就是暴露能给人看到功能。
接口就是给我们一种操作方式。软件的接口,就是使用这个软件。编程的接口,就是通过接口来调用程序,使用程序。
接口有时,也称为交互界面。
用户界面分成两种:TUI(文本交互界面 text user interface)和 GUI(图形化交互界面 操作图形)
文本交互界面是直接向计算机发送指定的,运行速度会快一些。linux就是文本操作界面
windows的命令行
命令行就是文本交互界面,通过命令行可以使用一个一个的指令来操作计算机
任何的计算机的操作系统中都包含有命令行(windows、linux、macOS)
命令行有多个不同的名字:
命令行、命令行窗口、DOS窗口、命令提示符、CMD窗口、Shell、终端、Terminal
练习1:
通过搜索引擎来搜索一下,各个操作系统当中的命令行窗口的样式
1.如何进入到命令行
win键 + R 出现运行窗口,输入cmd,然后回车
2.命令行的结构
- 版本及版权声明(一般没有什么用)
Microsoft Windows [版本 10.0.16299.431]
(c) 2017 Microsoft Corporation。保留所有权利。
- 命令提示符
C:\\Users\\lilichao>
C:
- 当前所在的磁盘根目录
- 可以通过 x: 来切换盘符(x表示你的盘符)
\\Users\\lilichao
- 所在磁盘的路径,当前所在的文件夹
- cd 来切换目录
>
- 命令提示符,在大于号后边可以直接输入指令
练习2:尝试使用多种方式进入到你的系统的命令行,并且观察你的和我的有什么区别?
3.常用的dos命令
dir 查看当前目录下的所有文件(夹)
cd 进入到指定的目录
. 表示当前目录
.. 表示上一级目录
md 创建一个目录
rd 删除一个目录
del 删除一个文件
cls 清除屏幕
命令的语法
命令 [参数] [选项]
练习3:通过搜索引擎搜索一下其他的一些常用DOS命令,并尝试使用一些命令的选项
4.小技巧
- 方向键上下,查看命令的历史记录
- tab键自动补全命令
环境变量(environment variable)
环境变量指的就是操作系统当中的一些变量。
可以通过修改环境变量,来对计算机进行配置(主要是来配置一些路径的)
1.查看环境变量
> 右键 计算机(此电脑),选择属性
> 系统界面左侧选择 高级系统设置
> 选择环境变量
环境变量界面分成了两个部分,上边是用户环境变量,下边是系统环境变量
建议只修改用户的环境变量,不要修改系统的环境变量
2.添加环境变量
> 通过新建按钮添加环境变量
> 一个环境变量可以由多个值,值与值之间使用;(英文)隔开
3.修改环境变量
> 通过编辑按钮来修改环境变量
4.删除环境变量
> 通过删除按钮来删除环境变量
练习4:进入到环境变量的界面,创建一个USERNAME环境变量,
修改USERNAME环境变量,并尝试添加多个值,然后删除USERNAME环境变量
path环境变量
path环境变量中保存的是一个一个的路径。
当我们在命令行中输入一个命令(或访问一个文件时),
系统会**首先**在当前目录下寻找,如果找到了则直接执行或打开
如果**没有找到**,则会依次去path环境变量的路径中去寻找,直到找到为止
如果path环境变量中的路径都没有找到,则报错
'xxx' 不是内部或外部命令,也不是可运行的程序或批处理文件。
我们可以将一些经常需要访问到的文件会程序的路径,添加到path环境变量中,
这样我们就可以**在任意的位置**访问到这些文件了
注意事项:
1.如果环境变量中没有path,可以手动添加
2.path环境变量不区分大小写 PATH Path path
3.修改完环境变量必须重新启动命令行窗口
4.多个路径之间使用;隔开
练习5:在桌面创建一个hello文件夹,文件中创建一个abc.txt输入随意的内容
然后将hello的路径添加到path环境变量中,尝试在任意位置访问abc.txt
最后将hello路径从path环境变量中删除。
进制
- 十进制(最常用的进制)
- 十进制就是满十进一的进制
- 十进制当中一共有10个数字
0 1 2 3 4 5 6 7 8 9
- 十进制如何计数
0 1 2 3 4 5 6 7 8 9 10 11 12 。。。 19 20 。。。29 30
- 二进制(计算机底层使用的进制)
- 满二进一
- 二进制中一共有2个数字
0 1
- 二进制如何计数
0 1 10 11 100 101 110 111 1000
- **所有的数据在计算机底层都是以二进制的形式**保存的,计算机只认二进制
- 可以将硬盘、内存想象为一个一个的小格子组成,小格子中可以存储一个0或一个1
- 内存中的每一个小格子,我们称为1bit(位)
bit是计算机中的最小的单位,我们不能去改
byte是我们最小的可操作的单位
8bit = 1byte(字节)
1024byte = 1kb(千字节)
1024kb = 1mb(兆字节)
1024mb = 1gb(吉字节)
1024gb = 1tb(太字节)
。。。
- 八进制(一般不用)
- 满八进一
- 八进制中一共有8个数字
0 1 2 3 4 5 6 7
- 八进制如何计数
0 1 2 3 4 5 6 7 10 11 。。。 17 20 。。。27 30
- 十六进制
- 满十六进一
- 十六进制中一共有16个数字
由于十六进制是满16才进位,
所以十六进制中引入了a b c d e f来表示 10 11 12 13 14 15
0 1 2 3 4 5 6 7 8 9 a b c d e f
- 十六进制如何计数
0 1 2 3 4 5 6 7 8 9 a b c d e f 10 11 12 。。。 1a 1b 1c 1d 1e 1f
20 21 22 。。。 2a 2b 2c 2d 2e 2f 30
- 我们在查看二进制数据时,一般会以十六进制的形式显示。
有时我们想看下文件的编码、计算机中的数据等直接看二进制数据时,由于会显示太多,所以不会直接以二进制形式展现出来,以16进制形式展示,更清晰。
文本文件和字符集
- 文本分成两种,一种叫做纯文本,还有一种叫做富文本
- 纯文本中只能保存单一的文本内容,无法保存内容无关的东西(字体、颜色、图片。。。)
- 富文本中可以保存文本以外的内容(word文档)
- 在开发时,编写程序使用的全都是纯文本!
- 纯文本在计算机底层也会转换为二进制保存,
将字符转换为二进制码的过程,我们称为编码
将二进制码转换为字符的过程,我们称为解码
编码和解码时所采用的规则,我们称为字符集
- 常见的字符集:
ASCII
- 美国人编码,使用7位来对美国常用的字符进行编码
- 包含128个字符
ISO-8859-1
- 欧洲的编码,使用8位
- 包含256个字符
GB2312
GBK
国标码,中国的编码
Unicode
万国码,包含世界上所有的语言和符号,编写程序时一般都会使用Unicode编码
Unicode编码有多种**实现**,UTF-8(使用1-5个字节表示字符) UTF-16(使用2-4个字节来表示字符) UTF-32(固定)
最常用的就是UTF-8
- 乱码
编写程序时,如果发现程序代码出现乱码的情况,就要马上去检查字符集是否正确
第二章 Python入门
什么是计算机语言
计算机就是一台用来计算机的机器,人让计算机干什么计算机就得干什么!
需要通过计算机的语言来控制计算机(编程语言)!
计算机语言其实和人类的语言没有本质的区别,不同点就是交流的主体不同!
计算机语言发展经历了三个阶段:
机器语言
- 机器语言通过二进制编码来编写程序
- 执行效率好,编写起来太麻烦
符号语言(汇编)
- 使用符号来代替机器码
- 编写程序时,不需要使用二进制,而是直接编写符号
- 编写完成后,需要将符号转换为机器码,然后再由计算机执行
符号转换为机器码的过程称为汇编
- 将机器码转换为符号的过程,称为反汇编
- 汇编语言一般只适用于某些硬件,兼容性比较差
高级语言
- 高级语言的语法基本和现在英语语法类似,并且和硬件的关系没有那么紧密了
- 也就是说我们通过高级语言开发程序可以在不同的硬件系统中执行
- 并且高级语言学习起来也更加的容易,现在我们知道的语言基本都是高级语言
- C、C++、C#、Java、JavaScript、Python 。。。
编译型语言和解释型语言
计算机只能识别二进制编码(机器码),所以任何的语言在交由计算机执行时必须要先转换为机器码,
也就是像 print('hello') 必需要转换为类似 1010101 这样的机器码
根据转换时机的不同,语言分成了两大类:
编译型语言
- C语言
- 编译型语言,会在代码执行前将代码编译为机器码,然后将机器码交由计算机执行
- a(源码) --编译--> b(编译后的机器码)
- 特点:
执行速度特别快
跨平台性比较差
解释型语言
- Python JS Java
- 解释型语言,不会在执行前对代码进行编译,而是在执行的同时一边执行一边编译
- a(源码)--解释器--> 解释执行
- 特点:
执行速度比较慢
跨平台性比较好
几个概念
1.表达式
表达式就是一个类似于数学公式的东西
比如:10 + 5 8 - 4
表达式一般仅仅用计算一些结果,不会对程序产生实质性的影响
如果在**交互模式**中输入一个表达式,解释器会自动将表达式的结果输出
2.语句
在程序中语句一般需要完成某种功能,比如打印信息、获取信息、为变量赋值。。。
比如:
print()
input()
a = 10
语句的执行一般会对程序产生一定的影响
在交互模式中不一定会输出语句的执行结果
3.程序(program)
程序就是由一条一条的语句和一条一条的表达式构成的。
4.函数(function)
函数就是一种语句,函数专门用来完成特定的功能
函数长的形如:xxx()
函数的分类:
内置函数
- 由Python解释器提供的函数,可以在Python中直接使用
自定义函数
- 由程序员自主的创建的函数
当我们需要完成某个功能时,就可以去调用内置函数,或者自定义函数
函数的两个要素:
参数
- ()中的内容就是函数的参数
- 函数中可以没有参数,也可以有多个参数,多个参数之间使用,隔开
返回值
- 返回值是函数的返回结果,不是所有的函数都有返回值。返回值需要有变量来接收。
基本语法
1.在Python中严格区分大小写
2.Python中的每一行就是一条语句,每条语句以换行结束
3.Python中每一行语句不要过长(规范中建议每行不要超过80个字符)
"rulers":[80],
4.一条语句可以分多行编写,多行编写时语句后边以\结尾
5.Python是缩进严格的语言,所以在Python中不要随便写缩进
6.在Python中使用#来表示注释,#后的内容都属于注释,注释的内容将会被解释器所忽略
我们可以通过注释来对程序进行解释说明,一定要养成良好的编写注释的习惯
注释要求简单明了,一般习惯上#后边会跟着一个空格
字面量和变量
字面量就是一个一个的值,比如:1,2,3,4,5,6,‘HELLO’
字面量所表示的意思就是它的字面的值,在程序中可以直接使用字面量
变量(variable)变量可以用来保存字面量,并且变量中保存的字面量是不定的
变量本身没有任何意思,它会根据不同的字面量表示不同的意思
一般我们在开发时,很少直接使用字面量,都是将字面量保存到变量中,通过变量来引用字面量
数据类型
数据类型指的就是变量的值的类型,也就是可以为变量赋哪些值。
数值
整型
布尔值
浮点型
复数
字符串
空值(就是null)
类型检查
通过类型检查,可以检查指定值(或变量对应值)的类型
# 通过类型检查,可以检查指定值(或变量对应值)的类型
a = 123 # 数值
b = '123' # 字符串
# print('a =',a) 打印出来:a = 123
# print('b =',b) 打印出来:b = 123 所以我们打印出来发现结果是一模一样。但是两个变量的类型是不一样的。
通过打印结果,我们没法直观的区分这两个变量到底是什么类型。
#type()用来检查值的类型。**要完成特定的功能就要去找对应的函数。函数是用来完成特定功能的。**
该函数会将检查的结果作为返回值返回,**要通过变量来接收函数的返回值**
c = type('123')
c = type(a)
# print(type(b))
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'>
对象(object)
Python是一门面向对象的语言。
万物皆对象!
程序运行当中,所有的数据都是存储到内存当中然后再运行的!
对象就是内存中专门用来存储指定数据的一块区域。
对象实际上就是一个容器,专门用来存储数据。
像我们之前学习的数值、字符串、布尔值、None都是对象
下面的每一块都是一个对象。所以物理上,对象就是内存中的一小块空间。
对象的结构
每个对象中都要保存三种数据
id(标识)
- id用来标识对象的唯一性,每一个对象都有唯一的id。对象的id就相当于人的身份证号。
- 可以通过id()函数来查看对象的id
- id是由解析器生成的,在CPython中,id就是对象的内存地址
- 对象一旦创建,则它的id永远不能再改变
type(类型)
- 类型用来标识当前对象所属的类型。可以类比自然世界中不同的物种。
- 比如:int str float bool 。。。
- 类型决定了对象有哪些功能。人有吃喝拉撒,自行车可以骑可以跑。
- 通过type()函数来查看对象的类型。要通过变量来接收函数的返回值。
- Python是一门强类型的语言,对象一旦创建类型便不能修改
value(值)
- 值就是对象中存储的具体的数据
- 对于有些对象值是可以改变的
- 对象分成两大类,可变对象 不可变对象
- 可变对象的值可以改变
- 不可变对象的值不能改变,整型、字符型、数字、none都是不可变对象。
变量和对象
a=3 这是什么意思?不是把3这个值存储到变量中! 3在内存中也是一个完整的区域,所以3也是一个对象。
变量如何存储一个对象呢?
对象并没有直接存储到变量中,在Python中变量更像是**对象的别名。**
a = 123 变量a既然是一个东西,也得存到内存里。
所以内存里也有个专门的区域来存储变量。
在内存中,一定是先执行表达式的**右边**123的**对象创建**,然后再执行左边变量a。
变量中存储的不是对象的值,而是对象的id(内存地址),当我们使用变量时,实际上就是在通过对象id在查找对象
变量中保存的对象(地址),只有在为变量重新赋值时才会改变
变量和变量之间是相互独立的,修改一个变量不会影响另一个变量
类型转换
所谓的类型转换,将一个类型的对象转换为其他对象
类型转换不是改变对象本身的类型,而是根据当前对象的值创建一个新对象
类型转换四个函数 int() float() str() bool()
int() 可以用来将其他的对象转换为整型
规则:
布尔值:True -> 1 False -> 0
浮点数:直接取整,省略小数点后的内容
字符串:合法的整数字符串,直接转换为对应的数字
如果不是一个合法的整数字符串,则报错 ValueError: invalid literal for int() with base 10: '11.5'
对于其他不可转换为整型的对象,直接抛出异常 ValueError
float() 和 int()基本一致,不同的是它会将对象转换为浮点数
str() 可以将对象转换为字符串
True -> 'True'
False -> 'False'
123 -> '123'
。。。
bool() 可以将对象转换为布尔值,任何对象都可以转换为布尔值
规则:对于所有表示空性的对象都会转换为False,其余的转换为True
哪些表示的空性:0 、 None 、 '' 。。。
------------------------------------------------------------------------------
a = True
# 调用int()来将a转换为整型
# int()函数不会对原来的变量a产生影响,他是对象转换为指定的类型并将其作为返回值返回
# 如果希望修改原来的变量,则需要对变量进行重新赋值
a = int(a)
print(a) #1
a = False
a = int(a)
print(a) # 0
a = '123'
a = int(a)
print(a) # 123
a = 11.6
a = int(a)
print(a) #11
# a = '11.5'
# a = int(a)
# # print(a) #V alueError: invalid literal for int() with base 10: '11.5'
# a = None
# a = int(a)
# print(a) #TypeError: int() argument must be a string, a bytes-like object or a number, not 'NoneType'
a = 1
a = float(a)
print(a) #1.0
a = False
a = float(a)
print(a) # 0.0
a = 123
a = str(a)
print(a) #123
a = None
a = bool(a)
print(a) #False
print('a =',a)
print('a的类型是',type(a)) #a的类型是 <class 'bool'>
b = 456
# print('hello'+b) # 会报错的
print('hello'+str(b)) # hello456
运算符(操作符)
- 运算符可以对一个值或多个值进行运算或各种操作
- 比如 + 、-、= 都属于运算符
- 运算符的分类:
1.算术运算符
2.赋值运算符
3.比较运算符(关系运算符)
4.逻辑运算符
5.条件运算符(三元运算符)
第四章 序列
列表(list)
- 列表是Python中的一个对象。
- 对象(object)就是内存中专门用来存储数据的一块区域。
- 之前我们学习的对象,像数值,它只能保存一个单一的数据。
- 列表中可以保存多个有序的数据
- **列表是用来存储对象的对象**
- 列表的使用:
1.列表的创建
2.操作列表中的数据
# 创建列表,通过[]来创建列表
my_list = [] # 创建了一个空列表
print(my_list , type(my_list)) # [] <class 'list'>
# 列表存储的数据,我们称为元素。
# 一个列表中可以存储多个元素,也可以在创建列表时,来指定列表中的元素
my_list = [10] # 创建一个只包含一个元素的列表
print(my_list) # [10]
# 当向列表中添加多个元素时,多个元素之间使用,隔开
my_list = [10,20,30,40,50] # 创建了一个保护有5个元素的列表。这个列表就相当于是一个容器,里面可以存储多个对象。
print(my_list) #[10, 20, 30, 40, 50]
# 列表中可以保存任意的对象,是个百宝箱
my_list = [10,'hello',True,None,[1,2,3],print] #[10, 'hello', True, None, [1, 2, 3], <built-in function print>]
print(my_list)
# 列表中的对象都会按照插入的顺序存储到列表中,
# 第一个插入的对象保存到第一个位置,第二个保存到第二个位置
# 我们可以通过索引(index)来获取列表中的元素
# 索引是元素在列表中的位置,列表中的每一个元素都有一个索引
# 索引是从0开始的整数,列表第一个位置索引为0,第二个位置索引为1,第三个位置索引为2,以此类推
my_list = [10,20,30,40,50]
# 通过索引获取列表中的元素
# 语法:my_list[索引] my_list[0]
# print(my_list[4])
# 如果使用的索引超过了最大的范围,会抛出异常
# print(my_list[5]) IndexError: list index out of range
#
# 获取列表的长度,列表中元素的个数
# len()函数,通过该函数可以获取列表的长度
# 获取到的长度的值,是列表的最大索引 + 1
print(len(my_list)) # 5
序列(sequence)
- 序列是Python中最基本的一种数据结构
- 数据结构指计算机中**数据存储**的方式(直白的方式)
- 序列用于**保存**一组**有序的**数据,所有的数据在序列当中都有一个**唯一**的位置(**索引**)
并且序列中的数据会按照添加的顺序来分配索引
- 序列的分类:
可变序列(序列中的元素可以改变):
> 列表(list)
不可变序列(序列中的元素不能改变):
> 字符串(str)
> 元组(tuple)
- 序列的通用操作,只要是序列就能操作:切片[0]、len()
序列的通用操作
**+ 和 ***
# +可以将两个列表拼接为一个列表
my_list = [1,2,3] + [4,5,6]
# * 可以将列表重复指定的次数
my_list = [1,2,3] * 5
# print(my_list)
# 创建一个列表
stus = ['孙悟空','猪八戒','沙和尚','唐僧','蜘蛛精','白骨精','沙和尚','沙和尚']
**in 和 not in**
# in用来检查指定元素是否存在于列表中
# 如果存在,返回True,否则返回False
# not in用来检查指定元素是否不在列表中
# 如果不在,返回True,否则返回False
# print('牛魔王' not in stus)
# print('牛魔王' in stus)
len()获取列表中的元素的个数
min() 获取列表中的最小值
max() 获取列表中的最大值
arr = [10,1,2,5,100,77]
# print(min(arr) , max(arr))
# 两个方法(method),方法和函数基本上是一样,只不过方法必须通过 对象.方法() 的形式调用
# xxx.print() 方法实际上就是和对象关系紧密的函数
s.index() 获取指定元素在列表中的第一次出现时索引
# print(stus.index('沙和尚'))
# index()的第二个参数,表示查找的起始位置 , 第三个参数,表示查找的结束位置
# print(stus.index('沙和尚',3,7))
# 如果要获取列表中没有的元素,会抛出异常
# print(stus.index('牛魔王')) ValueError: '牛魔王' is not in list
s.count() 统计指定元素在列表中出现的次数
print(stus.count('牛魔王'))
可变对象
可变是指 可变对象的值可变。
列表就是一个可变对象
a = [1,2,3]
每个对象中都保存了三个数据:
id(标识)
type(类型)
value(值)
a =[1,2,3]
a[0] = 10 (**改对象**)
- 这个操作是在通过变量去修改对象的值
- 这种操作不会改变变量所指向的对象,就是计算机里的一个内存地址。
- 当我们去修改对象时,如果有其他变量也指向了该对象,则修改也会在其他的变量中体现
- a = [4,5,6] (**改变量**)
- 这个操作是在给变量重新赋值,也就是把原来变量a所指向的内存地址从A改成了B。如下图。
- 这种操作会改变变量所指向的对象
- 为一个变量重新赋值时,不会影响其他的变量。
- 一般只有**在为变量赋值时才是修改变量,其余的都是修改对象**
字典(dict)
- 字典属于一种新的数据结构,称为映射(mapping)
- 字典的作用和列表类似,都是用来存储对象的容器
- 列表存储数据的性能很好,但是查询数据的性能的很差
- 在字典中每一个元素都有一个唯一的名字,通过这个唯一的名字可以快速的查找到指定的元素
- 在查询元素时,字典的效率是非常快的
- 在字典中可以保存多个对象,每个对象都会有一个唯一的名字
这个唯一的名字,我们称其为键(key),通过key可以快速的查询value
这个对象,我们称其为值(value)
所以字典,我们也称为叫做键值对(key-value)结构
每个字典中都可以有多个键值对,而每一个键值对我们称其为一项(item)
字典.py
# 字典
# 使用 {} 来创建字典
d = {} # 创建了一个空字典
# 创建一个保护有数据的字典
# 语法:
# {key:value,key:value,key:value}
# 字典的值可以是任意对象
# 字典的键可以是任意的不可变对象(int、str、bool、tuple ...),但是一般我们都会使用str
# 字典的键是不能重复的,如果出现重复的后边的会替换到前边的
# d = {'name':'孙悟空' , 'age':18 , 'gender':'男' , 'name':'sunwukong'}
d = {
'name':'孙悟空' ,
'age':18 ,
'gender':'男' ,
'name':'sunwukong'
}
print(d , type(d)) # {'name': 'sunwukong', 'age': 18, 'gender': '男'} <class 'dict'>
# 需要根据键来获取值
print(d['name'],d['age'],d['gender']) # sunwukong 18 男
# 如果使用了字典中不存在的键,会报错
# print(d['hello']) KeyError: 'hello'
字典的使用.py
# 创建字典
# 使用{}
# 语法:{k1:v1,k2:v2,k3:v3}
# 使用 dict()函数来创建字典
# 每一个参数都是一个键值对,参数名就是键,参数名就是值(这种方式创建的字典,key都是字符串)
d = dict(name='孙悟空',age=18,gender='男')
print(d,type(d)) # {'name': '孙悟空', 'age': 18, 'gender': '男'} <class 'dict'>
# 也可以将一个包含有双值子序列的序列转换为字典
# 双值序列,序列中只有两个值,[1,2] ('a',3) 'ab'
# 子序列,如果序列中的元素也是序列,那么我们就称这个元素为子序列
# [(1,2),(3,5)]
d = dict([('name','孙悟饭'),('age',18)])
print(d , type(d)) # {'name': '孙悟饭', 'age': 18} <class 'dict'>
d = dict(name='孙悟空',age=18,gender='男')
# len() 获取字典中键值对的个数,我个人理解也就是item的个数
print(len(d)) # 3
# in 检查字典中是否包含指定的键
# not in 检查字典中是否不包含指定的键
print('hello' in d) # False
# 获取字典中的值,根据键来获取值
# 语法:d[key]
print(d['age']) #18
n = 'name'
print(d[n]) # 孙悟空
# 通过[]来获取值时,如果键不存在,会抛出异常 KeyError
# get(key[, default]) 该方法用来根据键来获取字典中的值
# 如果获取的键在字典中不存在,会返回None
# 也可以指定一个默认值,来作为第二个参数,这样获取不到值时将会返回默认值
print(d.get('name')) # 孙悟空
print(d.get('hello','默认值')) # 默认值
# 修改字典
# d[key] = value 如果key存在则覆盖,不存在则添加
d['name'] = 'sunwukong' # 修改字典的key-value
d['address'] = '花果山' # 向字典中添加key-value
print(d) # {'name': 'sunwukong', 'age': 18, 'gender': '男', 'address': '花果山'}
# setdefault(key[, default]) 可以用来向字典中添加key-value
# 如果key已经存在于字典中,则返回key的值,不会对字典做任何操作
# 如果key不存在,则向字典中添加这个key,并设置value
result = d.setdefault('name','猪八戒')
result = d.setdefault('hello','猪八戒')
print('result =',result) # result = 猪八戒
print(d) #{'name': 'sunwukong', 'age': 18, 'gender': '男', 'address': '花果山', 'hello': '猪八戒'}
# update([other])
# 将其他的字典中的key-value添加到当前字典中
# 如果有重复的key,则后边的会替换到当前的
d = {'a':1,'b':2,'c':3}
d2 = {'d':4,'e':5,'f':6, 'a':7}
d.update(d2)
print(d) # {'a': 7, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6}
# 删除,可以使用 del 来删除字典中的 key-value
del d['a']
del d['b']
# popitem()
# 随机删除字典中的一个键值对,一般都会删除最后一个键值对
# 删除之后,它会将删除的key-value作为返回值返回
# 返回的是一个元组,元组中有两个元素,第一个元素是删除的key,第二个是删除的value
# 当使用popitem()删除一个空字典时,会抛出异常 KeyError: 'popitem(): dictionary is empty'
# d.popitem()
# result = d.popitem()
# pop(key[, default])
# 根据key删除字典中的key-value
# 会将被删除的value返回!
# 如果删除不存在的key,会抛出异常
# 如果指定了默认值,再删除不存在的key时,不会报错,而是直接返回默认值
result = d.pop('d')
result = d.pop('z','这是默认值')
# del d['z'] z不存在,报错
# result = d.popitem()
# result = d.popitem()
# result = d.popitem()
# result = d.popitem()
# clear()用来清空字典
d.clear()
print('result =',result) # result = 这是默认值
print(d) # {}
# copy()
# 该方法用于对字典进行浅复制
# 复制以后的对象,和原对象是独立,修改一个不会影响另一个
# 注意,浅复制会简单复制对象内部的值,如果值也是一个可变对象,这个可变对象不会被复制
d = {'a':1,'b':2,'c':3}
d2 = d.copy()
# d['a'] = 100
d = {'a':{'name':'孙悟空','age':18},'b':2,'c':3}
d2 = d.copy()
d2['a']['name'] = '猪八戒'
print(d2) # {'a': {'name': '猪八戒', 'age': 18}, 'b': 2, 'c': 3}
print('d = ',d , id(d)) # d = {'a': {'name': '猪八戒', 'age': 18}, 'b': 2, 'c': 3} 2521779629656
print('d2 = ',d2 , id(d2)) # d2 = {'a': {'name': '猪八戒', 'age': 18}, 'b': 2, 'c': 3} 2521780537744
遍历字典.py
# 遍历字典
# keys() 该方法会返回字典的所有的key
# 该方法会返回一个序列,序列中保存有字典的所有的键
d = {'name':'孙悟空','age':18,'gender':'男'}
# 通过遍历keys()来获取所有的键
for k in d.keys() :
print(k , d[k])
# name 孙悟空
# age 18
# gender 男
# name = 孙悟空
# age = 18
# gender = 男
print("--------------------------")
# values()
# 该方法会返回一个序列,序列中保存有字典的左右的值
for v in d.values():
print(v)
# 孙悟空
# 18
# 男
print("--------------------------")
# items()
# 该方法会返回字典中所有的项
# 它会返回一个序列,序列中包含有双值子序列
# 双值分别是,字典中的key和value
# print(d.items())
for k,v in d.items() :
print(k , '=' , v)
# name = 孙悟空
# age = 18
# gender = 男
集合(set)
- 集合和列表非常相似
- 不同点:
1.集合中只能存储不可变对象
2.集合中存储的对象是无序(不是按照元素的插入顺序保存)
3.集合中不能出现重复的元素
集合.py
# 集合
# 使用 {} 来创建集合
s = {10,3,5,1,2,1,2,3,1,1,1,1}
print(type(s))# <class 'set'>
print(s) # {1, 2, 3, 5, 10}
# s = {[1,2,3],[4,6,7]}
# print(s) # TypeError: unhashable type: 'list'
# 使用 set() 函数来创建集合
s = set() # 空集合
# 可以通过set()来将序列和字典转换为集合
s = set([1,2,3,4,5,1,1,2,3,4,5])
print(s) #{1, 2, 3, 4, 5}
s = set('hello')
print(s) # {'l', 'e', 'h', 'o'}
s = set({'a':1,'b':2,'c':3}) # 使用set()将字典转换为集合时,只会包含字典中的键
print(s) # {'b', 'c', 'a'}
# 创建集合
s = {'a' , 'b' , 1 , 2 , 3 , 1}
print(s) #{1, 2, 3, 'b', 'a'}
# 使用in和not in来检查集合中的元素
print('c' in s) # False
# 使用len()来获取集合中元素的数量
print(len(s)) # 5
# add() 向集合中添加元素
s.add(10)
s.add(30)
print(s) # {1, 2, 3, 'b', 10, 'a', 30}
# update() 将一个集合中的元素添加到当前集合中
# update()可以传递序列或字典作为参数,字典只会使用键
s2 = set('hello')
s.update(s2)
s.update((10,20,30,40,50))
s.update({10:'ab',20:'bc',100:'cd',1000:'ef'})
print(s) # {'e', 1, 2, 3, 100, 40, 'b', 'a', 10, 1000, 'o', 'h', 50, 20, 30, 'l'}
# pop()随机删除并返回一个集合中的元素
# result = s.pop()
# remove()删除集合中的指定元素
s.remove(100)
s.remove(1000)
# clear()清空集合
s.clear()
# copy()对集合进行浅复制
# print(result)
print(s , type(s)) # set() <class 'set'>
集合的运算.py
# 在对集合做运算时,不会影响原来的集合,而是返回一个运算结果
# 创建两个集合
s = {1,2,3,4,5}
s2 = {3,4,5,6,7}
# & 交集运算
result = s & s2
print(result) # {3, 4, 5}
# | 并集运算
result = s | s2 # {1,2,3,4,5,6,7}
# - 差集
result = s - s2 # {1, 2}
# ^ 异或集 获取只在一个集合中出现的元素
result = s ^ s2 # {1, 2, 6, 7}
# <= 检查一个集合是否是另一个集合的子集
# 如果a集合中的元素全部都在b集合中出现,那么a集合就是b集合的子集,b集合是a集合超集
a = {1,2,3}
b = {1,2,3,4,5}
result = a <= b # True
result = {1,2,3} <= {1,2,3} # True
result = {1,2,3,4,5} <= {1,2,3} # False
# < 检查一个集合是否是另一个集合的真子集
# 如果超集b中含有子集a中所有元素,并且b中还有a中没有的元素,则b就是a的真超集,a是b的真子集
result = {1,2,3} < {1,2,3} # False
result = {1,2,3} < {1,2,3,4,5} # True
# >= 检查一个集合是否是另一个的超集
# > 检查一个集合是否是另一个的真超集
print('result =',result) #result = True
第五章 函数
函数简介(function)
- 函数也是一个对象
- 对象是内存中专门用来存储数据的一块区域
- 函数可以**用来保存**一些可执行的代码,并且可以**在未来需要时**,对这些语句进行多次的调用
- 创建函数:
def 函数名([形参1,形参2,...形参n]) :
代码块
- 函数名必须要符号标识符的规范
(可以包含字母、数字、下划线、但是不能以数字开头)
- 函数中保存的代码不会立即执行,需要**调用函数代码才会执行**
- 调用函数:
函数对象()
- 定义函数一般都是要实现某种功能的
函数简介.py
# 比如有如下三行代码,这三行代码是一个完整的功能
# print('Hello')
# print('你好')
# print('再见')
# 定义一个函数
def fn() :
print('这是我的第一个函数!')
print('hello')
print('今天天气真不错!')
# 打印fn
print(fn) #<function fn at 0x00000214057D6158> 这个输出的意思是,函数fn在计算机的内存地址是0x00000214057D6158
print(type(fn)) # <class 'function'>
需要做一个区分。
**fn是函数对象 fn()调用函数。 仅有fn是不行的,必须加上(),表示调用函数**
# print是函数对象 print()调用函数
# fn()
# 定义一个函数,可以用来求任意两个数的和
# def sum() :
# a = 123
# b = 456
# print(a + b)
# sum()
# 定义函数时指定形参
def fn2(a , b) :
# print('a =',a)
# print('b =',b)
print(a,"+",b,"=",a + b)
# 调用函数时,来传递实参
fn2(10,20) #10 + 20 = 30
fn2(123,456) #123 + 456 = 579
函数的参数
- 在定义函数时,可以在函数名后的()中定义数量不等的形参,
多个形参之间使用,隔开
- 形参(形式参数),定义形参就相当于在函数内部声明了变量,但是并不赋值
- 实参(实际参数)
- 如果函数定义时,指定了形参,那么在调用函数时也必须传递实参,
实参将会赋值给对应的形参,简单来说,有几个形参就得传几个实参
函数的参数.py
# 求任意三个数的乘积
def mul(a,b,c):
print(a*b*c)
# 根据不同的用户名显示不同的欢迎信息
def welcome(username):
print('欢迎',username,'光临')
mul(1,2,3) # 6
welcome('孙悟空') # 欢迎 孙悟空 光临
# 定义一个函数
# 定义形参时,可以为形参指定默认值
# 指定了默认值以后,如果用户传递了参数则默认值没有任何作用
# 如果用户没有传递,则默认值就会生效
def fn(a = 5 , b = 10 , c = 20):
print('a =',a)
print('b =',b)
print('c =',c)
fn(1 , 2 , 3)
# a = 1
# b = 2
# c = 3
fn(1 , 2)
# a = 1
# b = 2
# c = 20
fn()
# a = 5
# b = 10
# c = 20
# 实参的传递方式
# 位置参数
# 位置参数就是将对应位置的实参复制给对应位置的形参
# 第一个实参赋值给第一个形参,第二个实参赋值给第二个形参 。。。
# fn(1 , 2 , 3)
# 关键字参数
# 关键字参数,可以不按照形参定义的顺序去传递,而直接根据参数名去传递参数
# fn(b=1 , c=2 , a=3)
# print('hello' , end='')
# 位置参数和关键字参数可以混合使用
# 混合使用关键字和位置参数时,必须将位置参数写到前面
# fn(1,c=30)
def fn2(a):
print('a =',a)
**实参的类型**
# 函数在调用时,解析器不会检查实参的类型
# 实参可以传递任意类型的对象
b = 123
b = True
b = 'hello'
b = None
b = [1,2,3]
# fn2(b)
fn2(fn) # a = <function fn at 0x0000017C42468048>
def fn3(a , b):
print(a+b)
# fn3(123,"456") # TypeError: unsupported operand type(s) for +: 'int' and 'str'
print("--------------------------------")
def fn4(a):
# 在函数中对形参进行重新赋值,不会影响其他的变量
# a = 20 # 输出 a = 20 1406758560
# a是一个列表,尝试修改列表中的元素
# 如果形参执行的是一个对象,当我们通过形参去修改对象时
# 会影响到所有指向该对象的变量
a[0] = 30
print('a =',a,id(a))
c = 10
c = [1,2,3]
fn4(c) # a = [30, 2, 3] 2932583679368
fn4(c.copy()) #a = [30, 2, 3] 2397075697032
fn4(c[:]) # a = [30, 2, 3] 2568554966408
print('c =',c,id(c)) #c = [1, 2, 3] 2343688390024
03不定长参数.py
# 不定长的参数
# 定义一个函数,可以求任意个数字的和
def sum(*nums):
# 定义一个变量,来保存结果
result = 0
# 遍历元组,并将元组中的数进行累加
for n in nums :
result += n
print(result)
sum(123,456,789,10,20,30,40) # 1468
# 在定义函数时,可以在**形参前边加上一个***,这样这个形参将会获取到所有的实参
# 它将会将所有的实参保存到一个元组中
# a,b,*c = (1,2,3,4,5,6)
# ***a会接受所有的位置实参**,并且会**将这些实参统一保存到一个元组中(这个操作,我们也称 装包。把一个个零散的参数统一放入到元组里)**
def fn(*a):
print("a =",a,type(a))
fn() # a = () <class 'tuple'>
fn(1,2,3,4,5) # a = (1, 2, 3, 4, 5) <class 'tuple'>
# 带星号的形参只能有一个
# 带星号的参数,可以和其他参数配合使用
# 第一个参数给a,第二个参数给b,剩下的都保存到c的元组中
def fn2(a,b,*c):
print('a =',a)
print('b =',b)
print('c =',c)
fn2(1,2,3,4,5,6,7)
# a = 1
# b = 2
# c = (3, 4, 5, 6, 7)
# 可变参数不是必须写在最后,但是注意,带*的参数后的所有参数,必须以关键字参数的形式传递
# 第一个参数给a,剩下的位置参数给b的元组,c必须使用关键字参数
# def fn2(a,*b,c):
# print('a =',a)
# print('b =',b)
# print('c =',c)
#
# 所有的位置参数都给a,b和c必须使用关键字参数
# def fn2(*a,b,c):
# print('a =',a)
# print('b =',b)
# print('c =',c)
#
# 如果在形参的开头直接写一个*后面加上参数,则要求我们的所有的参数必须以关键字参数的形式传递
def fn2(*,a,b,c):
print('a =',a)
print('b =',b)
print('c =',c)
fn2(a=3,b=4,c=5)
# a = 3
# b = 4
# c = 5
# ***形参**只能接收**位置参数**,而不能接收关键字参数
# def fn3(*a) :
# print('a =',a)
# fn3(b=1,d=2,c=3) # TypeError: fn3() got an unexpected keyword argument 'b'
# ****形参**可以接收其他的**关键字参数**,它会将这些参数统一保存到一个字典中
# 字典的key就是参数的名字,字典的value就是参数的值
# **形参只能有一个,并且必须写在所有参数的最后
def fn3(**a):
print('a=',a,type(a))
fn3(b=1,d=2,c=3) # a= {'b': 1, 'd': 2, 'c': 3} <class 'dict'>
def fn3(b,c,**a) :
print('a =',a,type(a))
print('b =',b)
print('c =',c)
fn3(b=1,d=2,c=3,e=10,f=20) #a = {'d': 2, 'e': 10, 'f': 20} <class 'dict'> 没有形参的那个实参
**# 参数的解包(拆包)**
def fn4(a,b,c):
print('a =',a)
print('b =',b)
print('c =',c)
# 创建一个元组
t = (10,20,30)
fn4(t[0],t[1],t[2]) #这样比较麻烦,最好的方式直接把元组里的元素赋值给函数对应的形参
# 传递实参时,也可以在序列类型的参数前添加星号,这样他会自动将序列中的元素依次作为参数传递
# 这里要求序列中元素的个数必须和形参的个数的一致
fn4(*t)
# a = 10
# b = 20
# c = 30
# 创建一个字典
d = {'a':100,'b':200,'c':300}
# 通过 **来对一个字典进行**解包**操作
fn4(**d)
# a = 100
# b = 200
# c = 300
04返回值.py
# 返回值,返回值就是函数执行以后返回的结果。类比f(x) = x+5
# 可以通过 return 来指定函数的返回值
# 可以直接使用函数的返回值,也可以通过一个变量来接收函数的返回值
def sum(*nums):
# 定义一个变量,来保存结果
result = 0
# 遍历元组,并将元组中的数进行累加
for n in nums :
result += n
print(result)
# sum(123,456,789) 这样带来一个问题,我使用函数计算结果,就一定要输出吗。有时我需要拿函数做一些别的操作。
#最好的方式是,你把结果给我,而不是你替我做输出,由我来做其他的操作。
# return 后边跟什么值,函数就会返回什么值
# return 后边可以跟任意的对象,返回值甚至可以是一个函数
def fn():
# return 100
# return 'Hello'
# return [1,2,3]
# return {'k':'v'}
def fn2() :
print('hello')
return fn2 # 返回值也可以是一个函数
fn() # 这个函数的执行结果就是它的返回值
r = fn() # 用变量来接收
print(r)# <function fn.<locals>.fn2 at 0x0000012B0FC58048>
print(fn())# <function fn.<locals>.fn2 at 0x0000012B0FC58048>
**# 如果仅仅写一个return 或者 不写return,则相当于return None**
def fn2() :
a = 10
return
print(fn2()) **# None**
# 在函数中,return后的代码都不会执行,return 一旦执行函数自动结束
def fn3():
print('hello')
return
print('abc')
r = fn3()
print(r) #hello
def fn4() :
for i in range(5):
if i == 3 :
# break 用来退出当前循环
# continue 用来跳过当次循环
return # return 用来结束函数
print(i)
print('循环执行完毕!')
fn4()
# 0
# 1
# 2
def sum(*nums):
# 定义一个变量,来保存结果
result = 0
# 遍历元组,并将元组中的数进行累加
for n in nums :
result += n
return result
r = sum(123,456,789)
print(r + 778) #2146
def fn5():
return 10
# fn5 和 fn5()的区别
print(fn5) # fn5是函数对象,打印fn5实际是在打印函数对象 <function fn5 at 0x000002758A336158>
print(fn5()) # fn5()是在调用函数,打印fn5()实际上是在打印fn5()函数的返回值 10
05文档字符串.py
# help()是Python中的内置函数
# 通过help()函数可以查询python中的函数的用法
# 语法:help(函数对象)
# help(print) # 获取print()函数的使用说明
# 文档字符串(doc str)
# 在定义函数时,可以在函数内部编写文档字符串,文档字符串就是函数的说明
# 当我们编写了文档字符串时,就可以通过help()函数来查看函数的说明
# 文档字符串非常简单,其实直接在函数的第一行写一个字符串就是文档字符串
def fn(a:int,b:bool,c:str='hello') -> int: # 这个表示函数的返回值是int类型。这些值都不是强制性的,都是描述性的。a:int 表示a这个变量应该写int类型。这是好的习惯,不是强制要求。
'''
这是一个文档字符串的示例
函数的作用:。。。。。
函数的参数:
a,作用,类型,默认值。。。。
b,作用,类型,默认值。。。。
c,作用,类型,默认值。。。。
'''
return 10
help(fn)
06作用域与命名空间.py
# 作用域(scope)
# 作用域指的是变量生效的区域
b = 20 # 全局变量
def fn():
a = 10 # a定义在了函数内部,所以他的作用域就是函数内部,函数外部无法访问
print('函数内部:','a =',a)
print('函数内部:','b =',b)
fn()
# 输出:
# 函数内部: a = 10
# 函数内部: b = 20
#
# print('函数外部:','a =',a) # NameError: name 'a' is not defined
# print('函数外部:','b =',b) #函数外部: b = 20
**# 在Python中一共有两种作用域**
# 全局作用域
# - 全局作用域在程序执行时创建,在程序执行结束时销毁
# - 所有函数以外的区域都是全局作用域
# - 在全局作用域中定义的变量,都属于全局变量,全局变量可以在程序的任意位置被访问
#
# 函数作用域
# - 函数作用域在函数调用时创建,在调用结束时销毁
# - 函数每调用一次就会产生一个新的函数作用域
# - 在函数作用域中定义的变量,都是局部变量,它只能在函数内部被访问
#
# 变量的查找
# - 当我们使用变量时,会优先在当前作用域中寻找该变量,如果有则使用,
# 如果没有则继续去上一级作用域中寻找,如果有则使用,
# 如果依然没有则继续去上一级作用域中寻找,以此类推
# 直到找到全局作用域,依然没有找到,则会抛出异常
# NameError: name 'a' is not defined
def fn2():
def fn3():
print('fn3中:','a =',b) # 写b不报错,写a报错。
fn3()
fn2()
a = 20
def fn3():
# a = 10 # 在函数中为变量赋值时,默认都是为局部变量赋值
# 如果希望在函数内部修改全局变量,则需要使用global关键字,来声明变量
global a # 声明在函数内部的使用a是全局变量,此时再去修改a时,就是在修改全局的a
a = 30 # 修改**全局变量**
print('函数内部:','a =',a)
fn3() # 函数内部: a = 30
print('函数外部:','a =',a)# 函数外部: a = 30
**# 命名空间(namespace) 由作用域衍生而来,为什么变量在不同作用域里可以看见。**
# 命名空间指的是变量存储的位置,每一个变量都需要存储到指定的命名空间当中
# 每一个作用域都会有一个它对应的命名空间
# 全局命名空间,用来保存全局变量。函数命名空间用来保存函数中的变量
# 命名空间实际上就是一个字典,是一个专门用来存储变量的字典
# locals()用来获取当前作用域的命名空间
# 如果在全局作用域中调用locals()则获取全局命名空间,如果在函数作用域中调用locals()则获取函数命名空间
# 返回的是一个字典
scope = locals() # 当前命名空间
print(scope)
print(type(scope)) #<class 'dict'>
print(a) # 10
print(scope['a'])#10 和上面语句一样。因为a就是scope里面键值对的键。
# 向scope中添加一个key-value
scope['c'] = 1000 # 向字典中添加key-value就相当于在全局中创建了一个变量(一般不建议这么做)
print(c) # 1000
def fn4():
a = 10
scope = locals() # 在函数内部调用locals()会获取到****函数**的命名空间**
scope['b'] = 20 # 可以通过scope来操作函数的命名空间,但是也是不建议这么做
# globals() 函数可以用来在任意位置获取****全局**命名空间**
global_scope = globals()
print(global_scope['a']) #30
global_scope['a'] = 10
print(scope) #{'a': 10, 'b': 20}
fn4()
07递归.py
# 尝试求10的阶乘(10!)
# 1! = 1
# 2! = 1*2 = 2
# 3! = 1*2*3 = 6
# 4! = 1*2*3*4 = 24
# print(1*2*3*4*5*6*7*8*9*10)
# 创建一个变量保存结果
n = 10
for i in range(1,10):
n *= i
print(n) # 3628800
# 创建一个函数,可以用来求任意数的阶乘。函数是要编写一个通用的方法。
def factorial(n):
'''
该函数用来求任意数的阶乘
参数:
n 要求阶乘的数字
'''
# 创建一个变量,来保存结果
result = n
for i in range(1,n):
result *= i
return result
# 求10的阶乘
print(factorial(10)) # 3628800
# 递归式的函数
# 从前有座山,山里有座庙,庙里有个老和尚讲故事,讲的什么故事呢?
# 从前有座山,山里有座庙,庙里有个老和尚讲故事,讲的什么故事呢?....
# 递归简单理解就是自己去****引用****自己!
# 递归式函数,在函数中自己****调用****自己!
# 无穷递归,如果这个函数被调用,程序的内存会溢出,效果类似于死循环
# def fn():
# fn()
# fn()
# 递归是解决问题的一种方式,它和循环很像
# 它的整体思想是,将一个大问题分解为一个个的小问题,直到问题无法分解时,再去解决问题
# 递归式函数的两个要件
# 1.基线条件
# - 问题可以被分解为的最小问题,当满足基线条件时,递归就不在执行了
# 2.递归条件
# - 将问题继续分解的条件
# 递归和循环类似,基本是可以互相代替的,
# 循环编写起来比较容易,阅读起来稍难
# 递归编写起来难,但是方便阅读 **下面这个案例特别形象!**
# 10! = 10 * 9!
# 9! = 9 * 8!
# 8! = 8 * 7!
# ...
# 1! = 1
def factorial(n):
'''
该函数用来求任意数的阶乘
参数:
n 要求阶乘的数字
'''
# 基线条件 判断n是否为1,如果为1则此时不能再继续递归
if n == 1 :
# 1的阶乘就是1,直接返回1
return 1
# 递归条件
return n * factorial(n-1)
# print(factorial(10))
# 练习
# 创建一个函数 power 来为任意数字做幂运算 n ** i
# 10 ** 5 = 10 * 10 ** 4
# 10 ** 4 = 10 * 10 ** 3
# ...
# 10 ** 1 = 10
def power(n , i):
'''
power()用来为任意的数字做幂运算
参数:
n 要做幂运算的数字
i 做幂运算的次数
'''
# 基线条件
if i == 1:
# 求1次幂
return n
# 递归条件
return n * power(n , i-1)
# print(power(8,6))
#
# 练习
# 创建一个函数,用来检查一个任意的字符串是否是回文字符串,如果是返回True,否则返回False
# 回文字符串,字符串从前往后念和从后往前念是一样的
# abcba
# abcdefgfedcba
# 先检查第一个字符和最后一个字符是否一致,如果不一致则不是回文字符串
# 如果一致,则看剩余的部分是否是回文字符串
# 检查 abcdefgfedcba 是不是回文
# 检查 bcdefgfedcb 是不是回文
# 检查 cdefgfedc 是不是回文
# 检查 defgfed 是不是回文
# 检查 efgfe 是不是回文
# 检查 fgf 是不是回文
# 检查 g 是不是回文
def hui_wen(s):
'''
该函数用来检查指定的字符串是否回文字符串,如果是返回True,否则返回False
参数:
s:就是要检查的字符串
'''
# 基线条件
if len(s) < 2 :
# 字符串的长度小于2,则字符串一定是回文
return True
elif s[0] != s[-1]:
# 第一个字符和最后一个字符不相等,不是回文字符串
return False
# 递归条件
return hui_wen(s[1:-1])
#也可以是下面的写法
# def hui_wen(s):
# '''
# 该函数用来检查指定的字符串是否回文字符串,如果是返回True,否则返回False
# 参数:
# s:就是要检查的字符串
# '''
# # 基线条件
# if len(s) < 2 :
# # 字符串的长度小于2,则字符串一定是回文
# return True
# # 递归条件
# return s[0] == s[-1] and hui_wen(s[1:-1])
print(hui_wen('abcdefgfedcba'))
函数式编程
说白了就是把一个一个的功能分解成一个一个的函数来执行。Python支持函数式编程,但不是函数式编程语言。
- 在Python中,函数是一等对象。对比其他语言,并不是所有函数都是一等对象。
- 一等对象一般都会具有如下特点:
① 对象是在运行时创建的。Python中所有对象都是这样的。对比其他语言有的是编译时创建。
② 能赋值给变量或作为数据结构中的元素。我的函数或者对象可以赋值给一个变量,或者可以存入到数据结构中,成为一个元素。
③ 能作为参数传递。任何对象都可以作为参数。
④ 能作为返回值返回。返回值也可以是一个函数。
- 高阶函数
- 高阶函数至少要符合以下两个特点中的一个
① 接收一个或多个函数作为参数。需要函数作为参数。
② 将函数作为返回值返回。
- 装饰器
08高阶函数.py
# 高阶函数
# **接收函数作为参数**,或者**将函数作为返回值的函数**是高阶函数
# 当我们使用一个函数作为参数时,实际上是**将指定的代码**传递进了目标函数
# 创建一个列表
l = [1,2,3,4,5,6,7,8,9,10]
# 定义一个函数
# 可以将指定列表中的所有的偶数,保存到一个新的列表中返回
# 定义一个函数,用来检查一个任意的数字是否是偶数
def fn2(i) :
if i % 2 == 0 :
return True
return False
# 这个函数用来检查指定的数字是否大于5
def fn3(i):
if i > 5 :
return True
return False
def fn(func , lst) : #func这里就是个参数。所以这里就是高阶函数。
'''
fn()函数可以将指定列表中的所有偶数获取出来,并保存到一个新列表中返回
参数:
lst:要进行筛选的列表
'''
# 创建一个新列表
new_list = []
# 对列表进行筛选
for n in lst :
# 判断n的奇偶
if func(n) :
new_list.append(n)
# if n > 5 :
# new_list.append(n)
# 返回新列表
return new_list
#可以看见通过传入不同的函数作为参数,排序规则就会改变
print(fn(fn2,l)) #[2, 4, 6, 8, 10]
print(fn(fn3,l)) # [6, 7, 8, 9, 10]
# def fn4(i):
# if i % 3 == 0:
# return True
# return False
def fn4(i):
return i % 3 == 0
# print(fn(fn4 , l))
# filter()
# filter()可以从序列中过滤出符合条件的元素,保存到一个新的序列中。可以把可迭代的结构暂时理解为序列。
# 参数:
# 1.函数,根据该函数来过滤序列(可迭代的结构)
# 2.需要过滤的序列(可迭代的结构)
# 返回值:
# 过滤后的新序列(可迭代的结构)。所以不会对原变量产生影响。
print(filter(fn4,l)) # <filter object at 0x0000025732DE2F98> 因为是个可迭代对象,所以返回是个filter object
print(list(filter(fn4,l))) # [3, 6, 9] 通过list()来转一下。
# fn4是作为参数传递进filter()函数中。千万不能是fn4(),这样就是调用函数,传入的就是函数的返回值。我要传入的是函数对象
# 而fn4实际上只有一个作用,就是作为filter()的参数。
# filter()调用完毕以后,fn4就已经没用了。而这样在全局创建个函数是有点划不来的。
# **匿名函数 lambda 函数表达式** (语法糖),就是正常函数的一种简写方式。只能写一些简单的函数。
# lambda函数表达式专门用来创建一些简单的函数,他是函数创建的又一种方式
# **语法:lambda 参数列表 : 返回值**
# 匿名函数一般都是作为**参数**使用,其他地方一般不会使用
def fn5(a , b):
return a + b
print(fn5(123,456)) #579
print(lambda a,b : a + b) # <function <lambda> at 0x00000284E84881E0>
(lambda a,b : a + b)(10,20) #这样才是调用函数
print((lambda a,b : a + b)(10,20)) # 30
# 也可以将匿名函数赋值给一个变量,一般不会这么做
fn6 = lambda a,b : a + b
print(fn6) # <function <lambda> at 0x000002253524B1E0>
print(fn6(10,30)) # 40
r = filter(lambda i : i > 5 , l)
print(list(r)) # [6, 7, 8, 9, 10]
# map()
# map()函数可以对可迭代对象中的所有元素做指定的操作,然后将其添加到一个新的对象中返回
l = [1,2,3,4,5,6,7,8,9,10]
r = map(lambda i : i ** 2 , l)
print(list(r)) # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
# sort()
# 该方法用来对列表中的元素进行排序
# sort()方法默认是直接比较**列表中的元素**的大小
# 在sort()可以接收一个关键字参数 , key
# key需要一个函数作为参数,当设置了函数作为参数
# 每次都会以列表中的一个元素**作为参数来调用函数**,并且使用**函数的返回值**来比较元素的大小
l = ['bb','aaaa','c','ddddddddd','fff']
l.sort()
print(l) # ['aaaa', 'bb', 'c', 'ddddddddd', 'fff'] 其实就是比较字符串的Unicode编码
l.sort(key=len) # 使用了高阶函数
print(l) # ['c', 'bb', 'fff', 'aaaa', 'ddddddddd']
l = [2,5,'1',3,'6','4']
l.sort(key=int) # 做了一个类型转换。并没有影响到原来列表里的元素
print(l) # ['1', 2, 3, '4', 5, '6']
# sorted()
# 这个函数和sort()的用法基本一致,但是sorted()可以对任意的序列进行排序。对比sort()只能对列表进行排序
# 并且使用sorted()排序不会影响原来的对象,而是返回一个新对象
l = [2,5,'1',3,'6','4']
# l = "123765816742634781"
#
print('排序前:',l) # 排序前: [2, 5, '1', 3, '6', '4']
print(sorted(l,key=int)) # ['1', 2, 3, '4', 5, '6']
print('排序后:',l) # 排序后: [2, 5, '1', 3, '6', '4']
l = "123765816742634781" # 下面就是对字符串按顺序进行排序
print(sorted(l,key=int)) # ['1', '1', '1', '2', '2', '3', '3', '4', '4', '5', '6', '6', '6', '7', '7', '7', '8', '8']
# 高阶函数的用法都是相似的,**把函数作为参数进行传递**
下面讲另一种类型的高阶函数。将函数作为返回值返回。
09闭包.py
# **将函数作为返回值返回**,也是一种高阶函数
# 这种高阶函数我们也称为叫做闭包,通过闭包可以创建一些只有当前函数能访问的变量
# 可以将一些私有的数据藏到的闭包中。备注:当有些见不得人的东西要藏起来的时候可以使用闭包
def fn():
a = 10
# 函数内部再定义一个函数
def inner():
print('我是fn2' , a)
# 将内部函数 inner作为返回值返回
return inner #记住,不要加()。加()是调用inner。我们这里不需要调用,只是作为返回值。
print(fn()) # <function fn.<locals>.inner at 0x0000022BB805BF28> #可以看见返回的是inner
# r是一个函数,是调用fn()后返回的函数
# 这个函数实在fn()内部定义,并不是全局函数
# 所以这个函数总是能访问到fn()函数内的变量,形成闭包
r = fn() #创建个变量保存下
print(r) # <function fn.<locals>.inner at 0x0000023AD6E68048> 返回的就是这个内部函数
r() # 我是fn2 10 既然是函数,自然是可以调用。
# 闭包有什么用呢?需求:求多个数的平均值
# nums = [50,30,20,10,77]
# Python内置sum()用来求一个列表中所有元素的和
# print(sum(nums)/len(nums))
**# 形成闭包的要件**
# ① 函数嵌套
# ② 将内部函数作为返回值返回
# ③ 内部函数必须要使用到外部函数的变量
#假设我 需要求的多个数是经常变化的呢?
def make_averager():
# 创建一个列表,用来保存数值
nums = []
# 创建一个函数,用来计算平均值
def averager(n) :
# 将n添加到列表中
nums.append(n)
# 求平均值
return sum(nums)/len(nums)
return averager
averager = make_averager()
print(averager) # <function make_averager.<locals>.averager at 0x000001D5ED3BA0D0> #返回的是内部函数
print(averager(10))
print(averager(20))
print(averager(30))
print(averager(40))
# 这样好处是看不到nums变量,因为它是内部变量
10装饰器.py
# 创建几个函数
def add(a , b):
'''
求任意两个数的和
'''
r = a + b
return r
def mul(a , b):
'''
求任意两个数的积
'''
r = a * b
return r
# 希望函数可以在计算前,打印开始计算,计算结束后打印计算完毕
# 我们可以直接通过修改函数中的代码来完成这个需求,但是会产生以下一些问题
# ① 如果要修改的函数过多,修改起来会比较麻烦
# ② 并且不方便后期的维护
# ③ 并且这样做会违反开闭原则(OCP)
# 程序的设计,要求开发对程序的扩展,要关闭对程序的修改
# r = add(123,456)
# print(r)
# 我们希望在不修改原函数的情况下,来对函数进行扩展
def fn():
print('我是fn函数....')
# 只需要根据现有的函数,来创建一个新的函数
def fn2():
print('函数开始执行~~~')
fn()
print('函数执行结束~~~')
# fn2()
def new_add(a,b):
print('计算开始~~~')
r = add(a,b) # 用变量接受内部函数的返回结果
print('计算结束~~~')
return r # 输出变量
r = new_add(111,222) # 这里是接受new_add()的返回值
print(r) # 333
# 上边的方式,已经可以在不修改源代码的情况下对函数进行扩展了
# 但是,这种方式要求我们每扩展一个函数就要手动创建一个新的函数,实在是太麻烦了
# 为了解决这个问题,我们创建一个函数,让这个函数可以自动的帮助我们生成函数
def begin_end(old): #每调用一次这个函数,就会在内部生成一个新的函数
'''
用来对其他函数进行扩展,使其他函数可以在执行前打印开始执行,执行后打印执行结束
参数:
old 要扩展的函数对象
'''
# 创建一个新函数
def new_function(*args , **kwargs):**#有没有一种方式可以接受所有的参数。*args接收所有的位置参数。**kwargs接收所有的关键字参数。应用到所有的情况,有参数也行,没有也行**
# 上行代码*args , **kwargs表示的意义:把传入的参数装包成一个元组或者一个字典。
print('开始执行~~~~')
# 调用被扩展的函数
result = old(*args , **kwargs) **#此行代码*args , **kwargs表示的意义:把元组拆分成位置参数传递进去;把字段拆分成字典参数传入进去**
print('执行结束~~~~')
# 返回函数的执行结果
return result
# 返回新函数
return new_function
f = begin_end(fn)
f2 = begin_end(add)
f3 = begin_end(mul)
# r = f()
# r = f2(123,456)
# r = f3(123,456)
# print(r)
# **像begin_end()这种函数我们就称它为装饰器**。举例:有个房子,想对它进行装饰,就是把内部功能增强点。
# 通过装饰器,可以在不修改原来函数的情况下来对函数进行扩展。简单点理解,传入一个旧函数作为参数,返回一个新函数,对旧函数升级改造,以后就只用调用新函数,多了扩展。
# 在开发中,我们都是通过装饰器来扩展函数的功能的
# 在定义函数时,可以通过@装饰器,来使用指定的装饰器,来装饰当前的函数
# 可以同时为一个函数指定多个装饰器,这样函数将会按照从内向外的顺序被装饰
def fn3(old):
'''
用来对其他函数进行扩展,使其他函数可以在执行前打印开始执行,执行后打印执行结束
参数:
old 要扩展的函数对象
'''
# 创建一个新函数
def new_function(*args , **kwargs):
print('fn3装饰~开始执行~~~~')
# 调用被扩展的函数
result = old(*args , **kwargs)
print('fn3装饰~执行结束~~~~')
# 返回函数的执行结果
return result
# 返回新函数
return new_function
@fn3
@begin_end #表示我用begin_end装饰器来装饰say_hello这个函数。省大量代码。
def say_hello(): # 给这个函数装了两个装饰器。 从内向外装饰。
print('大家好~~~')
say_hello()
# fn3装饰~开始执行~~~~
# 开始执行~~~~
# 大家好~~~
# 执行结束~~~~
# fn3装饰~执行结束~~~~
有了装饰器,我修改功能时,只用把装饰器的代码给改了,避免修改多个函数代码;关于开闭原则,有了装饰器,我们压根就没有修改函数的代码