Python基础(一)

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(标识)

  1. id用来标识对象的唯一性,每一个对象都有唯一的id。对象的id就相当于人的身份证号。
  2. 可以通过id()函数来查看对象的id
  3. id是由解析器生成的,在CPython中,id就是对象的内存地址
  4. 对象一旦创建,则它的id永远不能再改变

type(类型)

  1. 类型用来标识当前对象所属的类型。可以类比自然世界中不同的物种。
  2. 比如:int str float bool 。。。
  3. 类型决定了对象有哪些功能。人有吃喝拉撒,自行车可以骑可以跑。
  4. 通过type()函数来查看对象的类型。要通过变量来接收函数的返回值。
  5. Python是一门强类型的语言,对象一旦创建类型便不能修改

value(值)

  1. 值就是对象中存储的具体的数据
  2. 对于有些对象值是可以改变的
  3. 对象分成两大类,可变对象 不可变对象
  4. 可变对象的值可以改变
  5. 不可变对象的值不能改变,整型、字符型、数字、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
       哪些表示的空性:0None'' 。。。

------------------------------------------------------------------------------

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 = ['孙悟空','猪八戒','沙和尚','唐僧','蜘蛛精','白骨精','沙和尚','沙和尚']

**innot 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装饰~执行结束~~~~

有了装饰器,我修改功能时,只用把装饰器的代码给改了,避免修改多个函数代码;关于开闭原则,有了装饰器,我们压根就没有修改函数的代码
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值