Python

解释器

  1. 运⾏⽂件
                                                                                                        Python 程序执行原理

Python解释器种类:

    CPython,C语⾔开发的解释器[官⽅],应⽤⼴泛的解释器。
    IPython,基于CPython的⼀种交互式解释器。
其他解释器
    PyPy,基于Python语⾔开发的解释器。
    Jython,运⾏在Java平台的解释器,直接把Python代码编译成Java字节码执⾏。
    IronPython,运⾏在微软.Net平台上的Python解释器,可以直接把Python代码编译成.Net的字节码

注释

注释分为两类:单⾏注释 和 多⾏注释。
单⾏注释:
    # 注释内容
多⾏注释:
    """
         第⼀⾏注释
         第⼆⾏注释
         第三⾏注释
    """
    '''
         注释1
         注释2
         注释3
    '''

变量

变量名 = 值
变量名⾃定义,要满⾜标识符命名规则。
标识符:
    标识符命名规则是Python中定义各种名字的时候的统⼀规范,具体如下:
        由数字、字⺟、下划线组成
        不能数字开头
        不能使⽤内置关键字
        严格区分⼤⼩写
命名习惯:
    ⻅名知义。
    ⼤驼峰:即每个单词⾸字⺟都⼤写,例如: MyName 。
    ⼩驼峰:第⼆个(含)以后的单词⾸字⺟⼤写,例如: myName 。
    下划线:例如: my_name 。
数据类型:
    数值: int(整型) float(浮点型) true/false(布尔型) str(字符串) list(列表) tuple(元组) set(集合0 dict(字典)
    检测数据类型的⽅法: type()
        案例:
            a = 1
            print(type(a)) # <class 'int'> -- 整型
            b = 0.1
            print(type(b)) # <class 'float'> -- 浮点型
            c = True
            print(type(c)) # <class 'bool'> -- 布尔型
            d = '12345'
            print(type(d)) # <class 'str'> -- 字符串
            e = [10, 20, 30]
            print(type(e)) # <class 'list'> -- 列表
            f = (10, 20, 30)
            print(type(f)) # <class 'tuple'> -- 元组
            h = {10, 20, 30}
            print(type(h)) # <class 'set'> -- 集合
            g = {'name': '张三', 'age': 20}
            print(type(g)) # <class 'dict'> -- 字典

输出

作⽤:程序输出内容给⽤户
print('hello Python')
⼀. 格式化输出
    格式符号          转换
    %s              字符串
    %d              有符号的⼗进制整数
    %f              浮点数
    %c              字符
    %u              ⽆符号⼗进制整数
    %o              ⼋进制整数
    %x              ⼗六进制整数(⼩写ox)
    %X              ⼗六进制整数(⼤写OX)
    %e              科学计数法(⼩写'e')
    %E              科学计数法(⼤写'E')
    %g              %f和%e的简写
    %G              %f和%E的简写
注意:
    %06d,表示输出的整数显示位数,不⾜以0补全,超出当前位数则原样输出
    %.2f,表示⼩数点后显示的⼩数位数。
二、转义字符
    \n :换⾏。
    \t :制表符,⼀个tab键(4个空格)的距离。
三、结束符
    print('输出的内容', end="\n")
    在Python中,print(), 默认⾃带 end="\n" 这个换⾏结束符,所以导致每两个 print 直接会换⾏展示,⽤户可以按需求更改结束符。

输⼊

语法:
    input("提示信息")
特点:
    当程序执⾏到 input ,等待⽤户输⼊,输⼊完成之后才继续向下执⾏。
    在Python中, input 接收⽤户输⼊后,⼀般存储到变量,⽅便使⽤。
    在Python中, input 会把接收到的任意⽤户输⼊的数据都当做字符串处理。
例子:
    password = input('请输⼊您的密码:')
    print(f'您输⼊的密码是{password}') # <class 'str'>
    print(type(password))
    1234
注意:
    input接收的任何数据默认都是字符串数据类型。

数据类型转换

函数                      说明
int(x [,base ])             将x转换为⼀个整数
float(x )                   将x转换为⼀个浮点数
complex(real [,imag ])      创建⼀个复数,real为实部,imag为虚部
str(x )                     将对象 x 转换为字符串
repr(x )                    将对象 x 转换为表达式字符串
eval(str )                  ⽤来计算在字符串中的有效Python表达式,并返回⼀个对象
tuple(s )                   将序列 s 转换为⼀个元组
list(s )                    将序列 s 转换为⼀个列表
chr(x )                     将⼀个整数转换为⼀个Unicode字符
ord(x )                     将⼀个字符转换为它的ASCII整数值
hex(x )                     将⼀个整数转换为⼀个⼗六进制字符串
oct(x )                     将⼀个整数转换为⼀个⼋进制字符串
bin(x )                     将⼀个整数转换为⼀个⼆进制字符串

案例:
    # 1. 接收⽤户输⼊
    num = input('请输⼊您的数字:')
    # 2. 打印结果
    print(f"您的数字是{num}")
    # 3. 检测接收到的⽤户输⼊的数据类型 -- str类型
    print(type(num))
    # 4. 转换数据类型为整型 -- int类型
    print(type(int(num)))

运算符

算数运算的优先级
        混合运算优先级顺序: () ⾼于 ** ⾼于 * / // % ⾼于 + -
赋值运算符
        =
复合赋值运算符
        +=
        -=
优先级
        1. 先算复合赋值运算符右侧的表达式
        2. 再算复合赋值运算的算数运算
        3. 最后算赋值运算
⽐较运算符
        判断相等: ==
        ⼤于等于: >=
        ⼩于等于:<=
        不等于: !=
逻辑运算符
        与: and
        或:or
        ⾮:not

01. 算数运算符

  • 是完成基本的算术运算使用的符号,用来处理四则运算 | 运算符 | 描述 | 实例 | | —- | —- | —- | | + | 加 | 10 + 20 = 30 | | - | 减 | 10 - 20 = -10 | | | 乘 | 10 20 = 200 | | / | 除 | 10 / 20 = 0.5 | | // | 取整除 | 返回除法的整数部分(商) 9 // 2 输出结果 4 | | % | 取余数 | 返回除法的余数 9 % 2 = 1 | | | 幂 | 又称次方、乘方,2 3 = 8 |
  • 在 Python 中 * 运算符还可以用于字符串,计算结果就是字符串重复指定次数的结果
In [1]: "-" * 50
Out[1]: '----------------------------------------'

02. 比较(关系)运算符

运算符 描述
== 检查两个操作数的值是否 相等,如果是,则条件成立,返回 True
!= 检查两个操作数的值是否 不相等,如果是,则条件成立,返回 True
> 检查左操作数的值是否 大于 右操作数的值,如果是,则条件成立,返回 True
< 检查左操作数的值是否 小于 右操作数的值,如果是,则条件成立,返回 True
>= 检查左操作数的值是否 大于或等于 右操作数的值,如果是,则条件成立,返回 True
<= 检查左操作数的值是否 小于或等于 右操作数的值,如果是,则条件成立,返回 True

Python 2.x 中判断 不等于 还可以使用 <> 运算符

!= 在 Python 2.x 中同样可以用来判断 不等于

03. 逻辑运算符

运算符 逻辑表达式 描述
and x and y 只有 x 和 y 的值都为 True,才会返回 True
否则只要 x 或者 y 有一个值为 False,就返回 False
or x or y 只要 x 或者 y 有一个值为 True,就返回 True
只有 x 和 y 的值都为 False,才会返回 False
not not x 如果 x 为 True,返回 False
如果 x 为 False,返回 True

04. 赋值运算符

  • 在 Python 中,使用 = 可以给变量赋值
  • 在算术运算时,为了简化代码的编写,Python 还提供了一系列的 与 算术运算符 对应的 赋值运算符
  • 注意:赋值运算符中间不能使用空格 | 运算符 | 描述 | 实例 | | —- | —- | —- | | = | 简单的赋值运算符 | c = a + b 将 a + b 的运算结果赋值为 c | | += | 加法赋值运算符 | c += a 等效于 c = c + a | | -= | 减法赋值运算符 | c -= a 等效于 c = c - a | | = | 乘法赋值运算符 | c = a 等效于 c = c a | | /= | 除法赋值运算符 | c /= a 等效于 c = c / a | | //= | 取整除赋值运算符 | c //= a 等效于 c = c // a | | %= | 取 (余数)赋值运算符 | c %= a 等效于 c = c % a | | = | 幂赋值运算符 | c = a 等效于 c = c * a |

05. 运算符的优先级

  • 以下表格的算数优先级由高到最低顺序排列 | 运算符 | 描述 | | —- | —- | | | 幂 (最高优先级) | | / % // | 乘、除、取余数、取整除 | | + - | 加法、减法 | | <= < > >= | 比较运算符 | | == != | 等于运算符 | | = %= /= //= -= += = = | 赋值运算符 | | not or and | 逻辑运算符 |

条件语句

 1.if语句语法
         if 条件:
            条件成⽴执⾏的代码
 2.多重判断
          if 条件1:
             条件1成⽴执⾏的代码
          elif 条件2:
             条件2成⽴执⾏的代码
          else:
             以上条件都不成⽴执⾏的代码
 3.if嵌套
          if 条件1:
                条件1成⽴执⾏的代码
          if 条件2:
                条件2成⽴执⾏的代码
                ....
 4.三⽬运算符
          条件成⽴执⾏的表达式 if 条件 else 条件不成⽴执⾏的表达式
          a = 1
          b = 2
          c = a if a > b else b
          print(c) # 2

循环

程序的三大流程

在程序开发中,一共有三种流程方式:

  • 顺序 —— 从上向下,顺序执行代码
  • 分支 —— 根据条件判断,决定执行代码的 分支
  • 循环 —— 让 特定代码 重复 执行
循环分为 while 和 for 两种
1.while的语法
    while 条件:
         条件成⽴重复执⾏的代码1
         条件成⽴重复执⾏的代码2
         ......
    案例:
        # ⽅法⼀:条件判断和2取余数为0则累加计算
        i = 1
        result = 0
        while i <= 100:
             if i % 2 == 0:
                    result += i
             i += 1
        print(result)

        # 99乘法表
        j = 1
        while j <= 9:
             # 打印⼀⾏⾥⾯的表达式 a * b = a*b
             i = 1
             while i <= j:
                    print(f'{i}*{j}={j*i}', end='\t')
                    i += 1
             print()
             j += 1
2.for循环语法
    for 临时变量 in 序列:
         重复执⾏的代码1
         重复执⾏的代码2
         ......
    案例:
    str1 = 'python'
    for i in str1:
     print(i)

3.while...else
    while 条件:
         条件成⽴重复执⾏的代码
        else:
         循环正常结束之后要执⾏的代码
    案例:
        i = 1
        while i <= 5:
             print('媳妇⼉,我错了')
             i += 1
        else:
              print('媳妇原谅我了,真开⼼,哈哈哈哈')

4.for...else语法
    for 临时变量 in 序列:
         重复执⾏的代码
         ...
        else:
         循环正常结束之后要执⾏的代码
    案例:
        str1 = 'python'
        for i in str1:
         print(i)
        else:
         print('循环正常结束之后执⾏的代码')

5.退出循环的⽅式
    1. break:
        退出整个循环
    2. continue:
        continue是退出当前⼀次循环,继续下⼀次循环
        案例:
            str1 = 'python'
            for i in str1:
                 if i == 'o':
                    print('遇到n不打印')
                    continue  # break
                 print(i)
            else:
                print('循环正常结束之后执⾏的代码')

字符串

 特征:
    ⼀对引号字符串 name = 'hello'
    三引号字符串   name = ''' hello '''
 下标:
    “下标” ⼜叫 “索引” ,就是编号。 作⽤即是通过下标快速找到对应的数据。
     如:name = "abcdef"  a位置索引是0  从左往右 索引以此+1
     print(name[0]) #a
     print(name[1]) #b
 切⽚:
    指对操作的对象截取其中⼀部分的操作。字符串、列表、元组都⽀持切⽚操作。
    语法: 序列[开始位置下标:结束位置下标:步⻓]
    注意
        1. 不包含结束位置下标对应的数据, 正负整数均可;
        2. 步⻓是选取间隔,正负整数均可,默认步⻓为1。
    案例:
        name = "abcdefg"
        print(name[2:5:1]) # cde
        print(name[::2]) # aceg
        print(name[-4:-1]) # def
        print(name[:-1]) # abcdef, 负1表示倒数第⼀个数据
        print(name[::-1]) # gfedcba
 常⽤操作⽅法:
     查询:
        一、find():检测某个⼦串是否包含在这个字符串中,如果在返回这个⼦串开始的位置下标,否则则返回-1
            字符串序列.find(⼦串, 开始位置下标, 结束位置下标)
            注意:开始和结束位置下标可以省略,表示在整个字符串序列中查找
            mystr = "hello"
            print(mystr.find('o')) # 4
        二、index():检测某个⼦串是否包含在这个字符串中,如果在返回这个⼦串开始的位置下标,否则则报异常。
            字符串序列.index(⼦串, 开始位置下标, 结束位置下标)
            注意:开始和结束位置下标可以省略,表示在整个字符串序列中查找
        三、其他方法
            rfind(): 和find()功能相同,但查找⽅向为右侧开始。
            rindex():和index()功能相同,但查找⽅向为右侧开始。
            count():返回某个⼦串在字符串中出现的次数
     修改:
        一、replace():替换
            字符串序列.replace(旧⼦串, 新⼦串, 替换次数)
            注意:替换次数如果查出⼦串出现次数,则替换次数为该⼦串出现次数。
            注意:数据按照是否能直接修改分为可变类型和不可变类型两种。字符串类型的数据修改的时候不能改变原有字符串,属于不能直接修改数据的类型即是不可变类型。
        二、split():按照指定字符分割字符串。
            字符串序列.split(分割字符, num)
            注意:如果分割字符是原有字符串中的⼦串,分割后则丢失该⼦串。
        三、join():⽤⼀个字符或⼦串合并字符串,即是将多个字符串合并为⼀个新的字符串。
            字符或⼦串.join(多字符串组成的序列)
        四、capitalize():将字符串第⼀个字符转换成⼤写。
            注意:capitalize()函数转换后,只字符串第⼀个字符⼤写,其他的字符全都⼩写。
        五、title():将字符串每个单词⾸字⺟转换成⼤写。
        六、lower():将字符串中⼤写转⼩写。
        七、upper():将字符串中⼩写转⼤写。
        八、lstrip():删除字符串左侧空⽩字符。
        九、rstrip():删除字符串右侧空⽩字符。
        十、strip():删除字符串两侧空⽩字符

     判断:
        一、startswith():检查字符串是否是以指定⼦串开头,是则返回 True,否则返回 False。如果设置开始和结束位置下标,则在指定范围内检查。
            字符串序列.startswith(⼦串, 开始位置下标, 结束位置下标) 1
        二、endswith()::检查字符串是否是以指定⼦串结尾,是则返回 True,否则返回 False。如果设置开始和结束位置下标,则在指定范围内检查。
            字符串序列.endswith(⼦串, 开始位置下标, 结束位置下标)
        三、isalpha():如果字符串⾄少有⼀个字符并且所有字符都是字⺟则返回 True, 否则返回 False。
        四、isdigit():如果字符串只包含数字则返回 True 否则返回 False。
        五、isalnum():如果字符串⾄少有⼀个字符并且所有字符都是字⺟或数字则返 回 True,否则返回False。

1) 判断类型

方法 说明
string.isspace() 如果 string 中只包含空格,则返回 True
string.isalnum() 如果 string 至少有一个字符并且所有字符都是字母或数字则返回 True
string.isalpha() 如果 string 至少有一个字符并且所有字符都是字母则返回 True
string.isdecimal() 如果 string 只包含数字则返回 True,全角数字
string.isdigit() 如果 string 只包含数字则返回 True,全角数字

\\u00b2
string.isnumeric() 如果 string 只包含数字则返回 True,全角数字
汉字数字
string.istitle() 如果 string 是标题化的(每个单词的首字母大写)则返回 True
string.islower() 如果 string 中包含至少一个区分大小写的字符,并且所有这些(区分大小写的)字符都是小写,则返回 True
string.isupper() 如果 string 中包含至少一个区分大小写的字符,并且所有这些(区分大小写的)字符都是大写,则返回 True

2) 查找和替换

方法 说明
string.startswith(str) 检查字符串是否是以 str 开头,是则返回 True
string.endswith(str) 检查字符串是否是以 str 结束,是则返回 True
string.find(str, start=0, end=len(string)) 检测 str 是否包含在 string 中,如果 start 和 end 指定范围,则检查是否包含在指定范围内,如果是返回开始的索引值,否则返回 -1
string.rfind(str, start=0, end=len(string)) 类似于 find(),不过是从右边开始查找
string.index(str, start=0, end=len(string)) 跟 find() 方法类似,不过如果 str 不在 string 会报错
string.rindex(str, start=0, end=len(string)) 类似于 index(),不过是从右边开始
string.replace(old_str, new_str, num=string.count(old)) 把 string 中的 old_str 替换成 new_str,如果 num 指定,则替换不超过 num 次

3) 大小写转换

方法 说明
string.capitalize() 把字符串的第一个字符大写
string.title() 把字符串的每个单词首字母大写
string.lower() 转换 string 中所有大写字符为小写
string.upper() 转换 string 中的小写字母为大写
string.swapcase() 翻转 string 中的大小写

4) 文本对齐

方法 说明
string.ljust(width) 返回一个原字符串左对齐,并使用空格填充至长度 width 的新字符串
string.rjust(width) 返回一个原字符串右对齐,并使用空格填充至长度 width 的新字符串
string.center(width) 返回一个原字符串居中,并使用空格填充至长度 width 的新字符串

5) 去除空白字符

方法 说明
string.lstrip() 截掉 string 左边(开始)的空白字符
string.rstrip() 截掉 string 右边(末尾)的空白字符
string.strip() 截掉 string 左右两边的空白字符

6) 拆分和连接

方法 说明
string.partition(str) 把字符串 string 分成一个 3 元素的元组 (str前面, str, str后面)
string.rpartition(str) 类似于 partition() 方法,不过是从右边开始查找
string.split(str=””, num) 以 str 为分隔符拆分 string,如果 num 有指定值,则仅分隔 num + 1 个子字符串,str 默认包含 ‘\r’, ‘\t’, ‘\n’ 和空格
string.splitlines() 按照行(‘\r’, ‘\n’, ‘\r\n’)分隔,返回一个包含各行作为元素的列表
string.join(seq) 以 string 作为分隔符,将 seq 中所有的元素(的字符串表示)合并为一个新的字符串

7) 字符串的切片

  • 切片 方法适用于 字符串列表元组
    • 切片 使用 索引值 来限定范围,从一个大的 字符串切出 小的 字符串
    • 列表元组 都是 有序 的集合,都能够 通过索引值 获取到对应的数据
    • 字典 是一个 无序 的集合,是使用 键值对 保存数据
字符串[开始索引:结束索引:步长]

注意

  1. 指定的区间属于 左闭右开[开始索引, 结束索引) => 开始索引 >= 范围 < 结束索引
    • 起始 位开始,到 **结束**位的前一位 结束(不包含结束位本身)
  2. 从头开始,开始索引 数字可以省略,冒号不能省略
  3. 到末尾结束,结束索引 数字可以省略,冒号不能省略
  4. 步长默认为 1,如果连续切片,数字和冒号都可以省略

索引的顺序和倒序

  • 在 Python 中不仅支持 顺序索引,同时还支持 倒序索引
  • 所谓倒序索引就是 从右向左 计算索引
    • 最右边的索引值是 -1,依次递减

列表(可变类型数据)

⼀. 列表的应⽤场景
    思考:有⼀个⼈的姓名(TOM)怎么书写存储程序?
    答:变量。
    思考:如果⼀个班级100位学⽣,每个⼈的姓名都要存储,应该如何书写程序?声明100个变量吗?
    答:列表即可, 列表⼀次性可以存储多个数据。
⼆. 列表的格式
    [数据1, 数据2, 数据3, 数据4......]
三. 列表的常⽤操作
    列表的作⽤是⼀次性存储多个数据,程序员可以对这些数据进⾏的操作有:增、删、改、查。
    操作:
        一、查找
           1.下标
                [数据1, 数据2, 数据3, 数据4......] 1
                name_list = ['Tom', 'Lily', 'Rose']
                print(name_list[0]) # Tom
                print(name_list[1]) # Lily
                print(name_list[2]) # Rose
           2.函数
                 1.index():返回指定数据所在位置的下标 。
                    列表序列.index(数据, 开始位置下标, 结束位置下标)
                    name_list = ['Tom', 'Lily', 'Rose']
                    print(name_list.index('Lily', 0, 2)) # 1
                    注意:如果查找的数据不存在则报错。

                 2.count():统计指定数据在当前列表中出现的次数。
                    name_list = ['Tom', 'Lily', 'Rose']
                    print(name_list.count('Lily')) # 1

                 3.len():访问列表⻓度,即列表中数据的个数
                    name_list = ['Tom', 'Lily', 'Rose']
                    print(len(name_list)) # 3
           3.判断是否存在
                1.in:判断指定数据在某个列表序列,如果在返回True,否则返回False
                    name_list = ['Tom', 'Lily', 'Rose']
                    # 结果:True
                    print('Lily' in name_list)
                    # 结果:False
                    print('Lilys' in name_list)
                2.not in:判断指定数据不在某个列表序列,如果不在返回True,否则返回False
                    name_list = ['Tom', 'Lily', 'Rose']
                    # 结果:False
                    print('Lily' not in name_list)
                    # 结果:True
                    print('Lilys' not in name_list)
        二、增加
                作⽤:增加指定数据到列表中。
                1.append():列表结尾追加数据。
                    列表序列.append(数据)
                        name_list = ['Tom', 'Lily', 'Rose']
                        name_list.append('xiaoming')
                        # 结果:['Tom', 'Lily', 'Rose', 'xiaoming']
                        print(name_list)
                    注意:
                        1.列表追加数据的时候,直接在原列表⾥⾯追加了指定数据,即修改了原列表,故列表为可变类型数据。
                        2.注意点如果append()追加的数据是⼀个序列,则追加整个序列到列表
                            name_list = ['Tom', 'Lily', 'Rose']
                            name_list.append(['xiaoming', 'xiaohong'])
                            # 结果:['Tom', 'Lily', 'Rose', ['xiaoming', 'xiaohong']]
                            print(name_list)
                2.extend():列表结尾追加数据,如果数据是⼀个序列,则将这个序列的数据逐⼀添加到列表。
                    列表序列.extend(数据)
                        单个数据:
                            name_list = ['Tom', 'Lily', 'Rose']
                            name_list.extend('xiaoming')
                            # 结果:['Tom', 'Lily', 'Rose', 'x', 'i', 'a', 'o', 'm', 'i', 'n', 'g']
                            print(name_list)
                        序列数据:
                            name_list = ['Tom', 'Lily', 'Rose']
                            name_list.extend(['xiaoming', 'xiaohong'])
                            # 结果:['Tom', 'Lily', 'Rose', 'xiaoming', 'xiaohong']
                            print(name_list)
                3.insert():指定位置新增数据。
                    列表序列.insert(位置下标, 数据) 
                        name_list = ['Tom', 'Lily', 'Rose']
                        name_list.insert(1, 'xiaoming')
                        # 结果:['Tom', 'xiaoming', 'Lily', 'Rose']
                        print(name_list)
        三、删除
                1.del: del ⽬标
                    删除所有数据:
                            name_list = ['Tom', 'Lily', 'Rose']
                            # 结果:报错提示:name 'name_list' is not defined
                            del name_list
                            print(name_list)
                    删除指定数据:
                            name_list = ['Tom', 'Lily', 'Rose']
                            del name_list[0]
                            # 结果:['Lily', 'Rose']
                            print(name_list)

                2.pop():删除指定下标的数据(默认为最后⼀个),并返回该数据。
                    列表序列.pop(下标)
                        name_list = ['Tom', 'Lily', 'Rose']
                        del_name = name_list.pop(1)
                        # 结果:Lily
                        print(del_name)
                        # 结果:['Tom', 'Rose']
                        print(name_list)
                3.remove():移除列表中某个数据的第⼀个匹配项。
                    列表序列.remove(数据)
                        name_list = ['Tom', 'Lily', 'Rose']
                        name_list.remove('Rose')
                        # 结果:['Tom', 'Lily']
                        print(name_list)
                3.clear():清空列表
                        name_list = ['Tom', 'Lily', 'Rose']
                        name_list.clear()
                        print(name_list) # 结果: []
        四、修改
                1.修改指定下标数据
                        name_list = ['Tom', 'Lily', 'Rose']
                        name_list[0] = 'aaa'
                        # 结果:['aaa', 'Lily', 'Rose']
                        print(name_list)
                2.逆置:reverse()
                        num_list = [1, 5, 2, 3, 6, 8]
                        num_list.reverse()
                        # 结果:[8, 6, 3, 2, 5, 1]
                        print(num_list)
                3.排序:sort()
                    列表序列.sort( key=None, reverse=False) 
                    注意:reverse表示排序规则,reverse = True 降序, reverse = False 升序(默认)
                         num_list = [1, 5, 2, 3, 6, 8]
                        num_list.sort()
                        # 结果:[1, 2, 3, 5, 6, 8]
                        print(num_list)       

        五、复制
                1.copy()
                    name_list = ['Tom', 'Lily', 'Rose']
                    name_li2 = name_list.copy()
                    # 结果:['Tom', 'Lily', 'Rose']
                    print(name_li2)

四.列表的循环遍历
    1.while
        name_list = ['Tom', 'Lily', 'Rose'] i = 0
        while i < len(name_list):
             print(name_list[i])
             i += 1
    2.for
        name_list = ['Tom', 'Lily', 'Rose']
        for i in name_list:
         print(i)
五.列表嵌套
    列表嵌套指的就是⼀个列表⾥⾯包含了其他的⼦列表。
    应⽤场景:要存储班级⼀、⼆、三三个班级学⽣姓名,且每个班级的学⽣姓名在⼀个列表。
            name_list = [['⼩明', '⼩红', '⼩绿'], ['Tom', 'Lily', 'Rose'], ['张三', '李四', '王五']]
        思考: 如何查找到数据"李四"?
            # 第⼀步:按下标查找到李四所在的列表
            print(name_list[2])
            # 第⼆步:从李四所在的列表⾥⾯,再按下标找到数据李四
            print(name_list[2][1])
六、案例随机分配八个学生到三个班级
    import random
    # 1. 准备数据
    students = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
    offices = [[], [], []]

    # 2. 分配学生到班级 -- 取到每个学生放到班级列表 -- 遍历学生列表数据
    for name in students:
        # 列表追加数据 -- append(选中) extend insert
        # xx[0] -- 不能指定是具体某个下标 -- 随机
        num = random.randint(0, 2)
        offices[num].append(name)
    print(offices)

元组(不可变类型数据)

⼀. 元组的应⽤场景
    思考:如果想要存储多个数据,但是这些数据是不能修改的数据,怎么做?
    答:列表?列表可以⼀次性存储多个数据,但是列表中的数据允许更改。
    num_list = [10, 20, 30]
    num_list[0] = 100
⼀个元组可以存储多个数据,元组内的数据是不能修改的。
⼆. 定义元组
    元组特点:定义元组使⽤⼩括号,且逗号隔开各个数据,数据可以是不同的数据类型。
        # 多个数据元组
        t1 = (10, 20, 30)
        # 单个数据元组
        t2 = (10,)
    注意:如果定义的元组只有⼀个数据,那么这个数据后⾯也要添加逗号,否则数据类型为唯⼀的这个数据的数据类型
        t2 = (10,)
        print(type(t2)) # tuple
        t3 = (20)
        print(type(t3)) # int
        t4 = ('hello')
        print(type(t4)) # st
三. 元组的常⻅操作
    元组数据不⽀持修改,只⽀持查找,具体如下:
        1.按下标查找数据
            tuple1 = ('aa', 'bb', 'cc', 'bb')
            print(tuple1[0]) # aa
        2.index():查找某个数据,如果数据存在返回对应的下标,否则报错,语法和列表、字符串的index⽅法相同。
            tuple1 = ('aa', 'bb', 'cc', 'bb')
            print(tuple1.index('aa')) # 0
        3.count():统计某个数据在当前元组出现的次数。
            tuple1 = ('aa', 'bb', 'cc', 'bb')
            print(tuple1.count('bb')) # 2
        4.len():统计元组中数据的个数。
            tuple1 = ('aa', 'bb', 'cc', 'bb')
            print(len(tuple1)) # 4
            注意:元组内的直接数据如果修改则⽴即报错
                tuple1 = ('aa', 'bb', 'cc', 'bb')
                tuple1[0] = 'aaa'
                但是如果元组⾥⾯有列表,修改列表⾥⾯的数据则是⽀持的,故这点很重要。
                    tuple2 = (10, 20, ['aa', 'bb', 'cc'], 50, 30)
                    print(tuple2[2]) # 访问到列表
                    # 结果:(10, 20, ['aaaaa', 'bb', 'cc'], 50, 30)
                    tuple2[2][0] = 'aaaaa'
                    print(tuple2)

集合

⼀. 创建集合
        创建集合使⽤ {} 或 set() , 但是如果要创建空集合只能使⽤ set() ,因为 {} ⽤来创建空字典。
            s1 = {10, 20, 30, 40, 50}
            print(s1)
            s2 = {10, 30, 20, 10, 30, 40, 30, 50}
            print(s2)
            s3 = set('abcdefg')
            print(s3)
            s4 = set()
            print(type(s4)) # set
            s5 = {}
            print(type(s5)) # dict
⼆. 集合常⻅操作⽅法
    1.增加数据
        1.add()
                s1 = {10, 20}
                s1.add(100)
                s1.add(10)
                print(s1) # {100, 10, 20}
            因为集合有去重功能,所以,当向集合内追加的数据是当前集合已有数据的话,则不进⾏任何操作。
        2.update(), 追加的数据是序列。
                s1 = {10, 20}
                # s1.update(100) # 报错
                s1.update([100, 200])
                s1.update('abc')
                print(s1)
    2 删除数据
        1.remove(),删除集合中的指定数据,如果数据不存在则报错。
                s1 = {10, 20}
                s1.remove(10)
                print(s1)
                s1.remove(10) # 报错
                print(s1)
        2.discard(),删除集合中的指定数据,如果数据不存在也不会报错。
                s1 = {10, 20}
                s1.discard(10)
                print(s1)
                s1.discard(10)
                print(s1)
        3.pop(),随机删除集合中的某个数据,并返回这个数据。
                s1 = {10, 20, 30, 40, 50}
                del_num = s1.pop()
                print(del_num)
                print(s1) 12345

    3 查找数据
        in:判断数据在集合序列
        not in:判断数据不在集合序列
        s1 = {10, 20, 30, 40, 50}
        print(10 in s1)
        print(10 not in s1)

字典(可变类型)

⼀. 字典的应⽤场景
    list1 = ['Tom', '男', 20]
    思考1: 如果有多个数据,例如:'Tom', '男', 20,如何快速存储?
    答:列表
    思考2:如何查找到数据'Tom'?
    答:查找到下标为0的数据即可
    思考3:如果将来数据顺序发⽣变化,如下所示,还能⽤ list1[0] 访问到数据'Tom'吗?。
    答:不能,数据'Tom'此时下标为2。
    思考4:数据顺序发⽣变化,每个数据的下标也会随之变化,如何保证数据顺序变化前后能使⽤同⼀的标准查找数据呢?
    答:字典,字典⾥⾯的数据是以键值对形式出现,字典数据和数据顺序没有关系,即字典不⽀持下标,后期⽆论数据如何变化,只需要按照对应的键的名字查找数据即可。
⼆. 创建字典的语法
    1.字典特点:
        符号为⼤括号
        数据为键值对形式出现
        各个键值对之间⽤逗号隔开
        # 有数据字典
        dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
        # 空字典
        dict2 = {}
        dict3 = dict()    
        注意:⼀般称冒号前⾯的为键(key),简称k;冒号后⾯的为值(value),简称v。

三. 字典常⻅操作
    1.增加
        写法:字典序列[key] = 值
        注意:如果key存在则修改这个key对应的值;如果key不存在则新增此键值对。
        dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
        dict1['name'] = 'Rose'
        # 结果:{'name': 'Rose', 'age': 20, 'gender': '男'}
        print(dict1)
        dict1['id'] = 110
        # {'name': 'Rose', 'age': 20, 'gender': '男', 'id': 110}
        print(dict1)

    2.删除
        1.del() / del:删除字典或删除字典中指定键值对。
            dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
            del dict1['gender']
            # 结果:{'name': 'Tom', 'age': 20}
            print(dict1)
        2.clear():清空字典
            dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
            dict1.clear()
            print(dict1) # {}
            1234
    3.改    
        写法:字典序列[key] = 值
        注意:如果key存在则修改这个key对应的值 ;如果key不存在则新增此键值对。
    4.查
        1.key值查找
            dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
            print(dict1['name']) # Tom
            print(dict1['id']) # 报错 如果当前查找的key存在,则返回对应的值;否则则报错。
        2.get()     
            字典序列.get(key, 默认值)
                注意:如果当前查找的key不存在则返回第⼆个参数(默认值),如果省略第⼆个参数,则返回None。
            dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
            print(dict1.get('name')) # Tom
            print(dict1.get('id', 110)) # 110
            print(dict1.get('id')) # None
        3.keys()
            dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
            print(dict1.keys()) # dict_keys(['name', 'age', 'gender'])
        4.values()
            dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
            print(dict1.values()) # dict_values(['Tom', 20, '男'])
        5.items()
            dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
            print(dict1.items()) # dict_items([('name', 'Tom'), ('age', 20), ('gender','男')])
    5.字典的循环遍历
        1.遍历字典的key
            dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
            for key in dict1.keys():
                print(key)
        2.遍历字典的value
            dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
            for value in dict1.values():
                    print(value)
        3.遍历字典的元素
            dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
            for item in dict1.items():
                    print(item)
        4.遍历字典的键值对
            dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
            for key, value in dict1.items():
                print(f'{key} = {value}')

公共操作

⼀. 运算符        
    运算符     描述                 ⽀持的容器类型
     +        合并                  字符串、列表、元组
     *        复制                  字符串、列表、元组
     in       元素是否存在           字符串、列表、元组、字典
    not in    元素是否不存在         字符串、列表、元组、字典

案例:
    1.+
        # 1. 字符串
        str1 = 'aa'
        str2 = 'bb'
        str3 = str1 + str2
        print(str3) # aabb
        # 2. 列表
        list1 = [1, 2]
        list2 = [10, 20]
        list3 = list1 + list2
        print(list3) # [1, 2, 10, 20]
        # 3. 元组
        t1 = (1, 2)
        t2 = (10, 20)
        t3 = t1 + t2
        print(t3) # (10, 20, 100, 200)

    2.*
        # 1. 字符串
        print('-' * 10) # ----------
        # 2. 列表
        list1 = ['hello']
        print(list1 * 4) # ['hello', 'hello', 'hello', 'hello']
        # 3. 元组
        t1 = ('world',)
        print(t1 * 4) # ('world', 'world', 'world', 'world')
    3. in或not in
        # 1. 字符串
        print('a' in 'abcd') # True
        print('a' not in 'abcd') # False
        # 2. 列表
        list1 = ['a', 'b', 'c', 'd']
        print('a' in list1) # True
        print('a' not in list1) # False
        # 3. 元组
        t1 = ('a', 'b', 'c', 'd')
        print('aa' in t1) # False
        print('aa' not in t1) # True

⼆. 公共⽅法
        函数      描述
        len()       计算容器中元素个数
        del 或 del() 删除
        max()       返回容器中元素最⼤值
        min()       返回容器中元素最⼩值
        range(start,end, step)  ⽣成从start到end的数字,步⻓为 step,供for循环使⽤
        enumerate()     函数⽤于将⼀个可遍历的数据对象(如列表、元组或字符串)组合为⼀个索引序列,同时列出数据和数据下标,⼀般⽤在 for 循环当中。
三. 容器类型转换
        1 tuple()
            作⽤:将某个序列转换成元组
                list1 = [10, 20, 30, 40, 50, 20]
                s1 = {100, 200, 300, 400, 500}
                print(tuple(list1))
                print(tuple(s1))
        2 list()
            作⽤:将某个序列转换成列表
                t1 = ('a', 'b', 'c', 'd', 'e')
                s1 = {100, 200, 300, 400, 500}
                print(list(t1))
                print(list(s1))
        3 set()
            作⽤:将某个序列转换成集合
                 list1 = [10, 20, 30, 40, 50, 20]
                 t1 = ('a', 'b', 'c', 'd', 'e')
                 print(set(list1))
                 print(set(t1))   

        注意:
           1. 集合可以快速完成列表去重
           2. 集合不⽀持下标

推导式(简化代码)

⼀. 列表推导式
        作⽤:⽤⼀个表达式创建⼀个有规律的列表或控制⼀个有规律列表。
        列表推导式⼜叫列表⽣成式。
    1 快速体验
        需求:创建⼀个0-10的列表。
            1.while循环实现
                # 1. 准备⼀个空列表
                list1 = []
                # 2. 书写循环,依次追加数字到空列表list1中 i = 0
                while i < 10:
                     list1.append(i)
                     i += 1
                print(list1)
            2.for循环实现
                list1 = []
                for i in range(10):
                    list1.append(i)
                print(list1)
            3.列表推导式实现
                list1 = [i for i in range(10)]
                print(list1)
    2 带if的列表推导式
        需求:创建0-10的偶数列表
            ⽅法⼀:range()步⻓实现
                list1 = [i for i in range(0, 10, 2)]
                print(list1)
            ⽅法⼆:if实现
                list1 = [i for i in range(10) if i % 2 == 0]
                print(list1)
    3 多个for循环实现列表推导式        
        需求:创建列表如下:    
            [(1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
            list1 = [(i, j) for i in range(1, 3) for j in range(3)]
            print(list1)
⼆. 字典推导式
        思考:如果有如下两个列表:
            list1 = ['name', 'age', 'gender']
            list2 = ['Tom', 20, 'man']
        如何快速合并为⼀个字典?
        答:字典推导式
        字典推导式作⽤:快速合并列表为字典或提取字典中⽬标数据。
    1 快速体验
        1. 创建⼀个字典:字典key是1-5数字,value是这个数字的2次⽅。
         dict1 = {i: i**2 for i in range(1, 5)}
         print(dict1) # {1: 1, 2: 4, 3: 9, 4: 16}   
    2. 将两个列表合并为⼀个字典
        list1 = ['name', 'age', 'gender']
        list2 = ['Tom', 20, 'man']
        dict1 = {list1[i]: list2[i] for i in range(len(list1))}
        print(dict1)
    3. 提取字典中⽬标数据
        counts = {'MBP': 268, 'HP': 125, 'DELL': 201, 'Lenovo': 199, 'acer': 99}
        # 需求:提取上述电脑数量⼤于等于200的字典数据
        count1 = {key: value for key, value in counts.items() if value >= 200}
        print(count1) # {'MBP': 268, 'DELL': 201}
三. 集合推导式
    需求:创建⼀个集合,数据为下⽅列表的2次⽅。
        list1 = [1, 1, 2]
    代码如下:
        list1 = [1, 1, 2]
        set1 = {i ** 2 for i in list1}
        print(set1) # {1, 4}
    注意:集合有数据去重功能。

函数一

⼀. 函数的作⽤
    函数就是将⼀段具有独⽴功能的代码块 整合到⼀个整体并命名,在需要的位置调⽤这个名称即可完成对应的需求。
    函数在开发过程中,可以更⾼效的实现代码重⽤。
⼆. 函数的使⽤步骤
    1 定义函数
        def 函数名(参数):
             代码1
             代码2
             ......
    2 调⽤函数
        函数名(参数)
            注意:
               1. 不同的需求,参数可有可⽆。
               2. 在Python中,函数必须先定义后使⽤。
    3 快速体验
        注意:⼀定是先定义函数,后调⽤函数。
        # 封装ATM机功能选项 -- 定义函数
        定义函数:
            def select_func():
                 print('-----请选择功能-----')
                 print('查询余额')
                 print('存款')
                 print('取款')
                 print('-----请选择功能-----')
        调⽤函数:
            select_func()
三.函数的参数作⽤
    函数的参数:函数调⽤的时候可以传⼊真实数据,增⼤函数的使⽤的灵活性
       ### 形参和实参
        形参:定义函数时,小括号中的参数,是用来接收参数用的,在函数内部作为变量使用*
        实参:调用 函数时,小括号中的参数,是用来把数据传递到 函数内部用的
    思考:完成需求如下:⼀个函数完成两个数1和2的加法运算,如何书写程序?
        # 定义函数
        def add_num1():
             result = 1 + 2
             print(result)
        # 调⽤函数
        add_num1()
    思考:上述add_num1函数只能完成数字1和2的加法运算,如果想要这个函数变得更灵活,可以计算任何⽤户指定的两个数字的和,如何书写程序?
    分析:⽤户要在调⽤函数的时候指定具体数字,那么在定义函数的时候就需要接收⽤户指定的数字。函数调⽤时候指定的数字和定义函数时候接收的数字即是函数的参数。
        # 定义函数时同时定义了接收⽤户数据的参数a和b,a和b是形参
        def add_num2(a, b):
             result = a + b
             print(result)
        # 调⽤函数时传⼊了真实的数据10 和 20,真实数据为实参
        add_num2(10, 20)
四.函数的返回值作⽤
        例如:我们去超市购物,⽐如买烟,给钱之后,是不是售货员会返回给我们烟这个商品,在函数中,如果需要返回结果给⽤户需要使⽤函数返回值。
        def buy():
            return '烟'
        # 使⽤变量保存函数返回值
        goods = buy()
        print(goods)
    1 应⽤
        需求:制作⼀个计算器,计算任意两数字之和,并保存结果
            def sum_num(a, b):
                 return a + b
            # ⽤result变量保存函数返回值
            result = sum_num(1, 2)
            print(result)
五.函数的说明⽂档
        思考:定义⼀个函数后,程序员如何书写程序能够快速提示这个函数的作⽤?
        答:注释
        思考:如果代码多,我们是不是需要在很多代码中找到这个函数定义的位置才能看到注释?如果想更⽅便的查看函数的作⽤怎么办?
        答:函数的说明⽂档
        函数的说明⽂档也叫函数的⽂档说明。

    1 语法
        定义函数的说明⽂档
            def 函数名(参数):
                 """ 说明⽂档的位置 """
                 代码
                 ......
        查看函数的说明⽂档
            help(函数名)
    2 快速体验
        def sum_num(a, b):
             """ 求和函数 """
             return a + b
        help(sum_num)
六.函数嵌套调⽤
        所谓函数嵌套调⽤指的是⼀个函数⾥⾯⼜调⽤了另外⼀个函数。
    示例:
        def testB():
             print('---- testB start----')
             print('这⾥是testB函数执⾏的代码...(省略)...')
             print('---- testB end----')
        def testA():
             print('---- testA start----')
             testB()
             print('---- testA end----')
        testA()

七. 函数应⽤
    1 打印图形
        1. 打印⼀条横线
            def print_line():
                print('-' * 20)
            print_line()                 
        2. 打印多条横线
            def print_line():
                print('-' * 20)
            def print_lines(num):
                 i = 0
                 while i < num:
                    print_line()
                    i += 1
            print_lines(5)

函数二

⼀. 变量作⽤域
    变量作⽤域指的是变量⽣效的范围,主要分为两类:局部变量和全局变量。
    局部变量
        所谓局部变量是定义在函数体内部的变量,即只在函数体内部⽣效。
            def testA():
                a = 100
            print(a)
            testA() # 100
            print(a) # 报错:name 'a' is not defined
        变量a是定义在 testA 函数内部的变量,在函数外部访问则⽴即报错。
        局部变量的作⽤:在函数体内部,临时保存数据,即当函数调⽤完成后,则销毁局部变量。

    全局变量
        所谓全局变量,指的是在函数体内、外都能⽣效的变量。
        思考:如果有⼀个数据,在函数A和函数B中都要使⽤,该怎么办?
        答:将这个数据存储在⼀个全局变量⾥⾯。
            # 定义全局变量a a = 100
            def testA():
                print(a) # 访问全局变量a,并打印变量a存储的数据
            def testB():
                 print(a) # 访问全局变量a,并打印变量a存储的数据
            testA() # 100
            testB() # 100
        思考: testB 函数需求修改变量a的值为200,如何修改程序?
            a = 100
            def testA():
                print(a)
            def testB():
                a = 200
                print(a)
            testA() # 100
            testB() # 200
            print(f'全局变量a = {a}') # 全局变量a = 100
        思考:在 testB 函数内部的 a = 200 中的变量a是在修改全局变量 a 吗?
        答:不是。观察上述代码发现,15⾏得到a的数据是100,仍然是定义全局变量a时候的值,⽽没有返回testB 函数内部的200。综上: testB 函数内部的 a = 200 是定义了⼀个局部变量。
        思考:如何在函数体内部修改全局变量?
            a = 100
            def testA():
                print(a)
            def testB():
                # global 关键字声明a是全局变量
                 global a
                 a = 200
                 print(a)
            testA() # 100
            testB() # 200
            print(f'全局变量a = {a}') # 全局变量a = 200
⼆. 多函数程序执⾏流程    
    ⼀般在实际开发过程中,⼀个程序往往由多个函数(后⾯知识中会讲解类)组成,并且多个函数共享某些数据,如下所示:
    1.共⽤全局变量
        # 1. 定义全局变量
        glo_num = 0
        def test1():
            global glo_num
             # 修改全局变量
             glo_num = 100
        def test2():
             # 调⽤test1函数中修改后的全局变量
             print(glo_num)
        # 2. 调⽤test1函数,执⾏函数内部代码:声明和修改全局变量
        test1()
        # 3. 调⽤test2函数,执⾏函数内部代码:打印
        test2() # 100
    2.返回值作为参数传递
        def test1():
            return 50
        def test2(num):
            print(num)
        # 1. 保存函数test1的返回值
        result = test1()
        # 2.将函数返回值所在变量作为参数传递到test2函数
        test2(result) # 50
三. 函数的返回值
        思考:如果⼀个函数如些两个return (如下所示),程序如何执⾏?
            def return_num():
                 return 1
                 return 2
            result = return_num()
            print(result) # 1
        答:只执⾏了第⼀个return,原因是因为return可以退出当前函数,导致return下⽅的代码不执⾏。
        思考:如果⼀个函数要有多个返回值,该如何书写代码?
            def return_num():
                return 1, 2
            result = return_num()
            print(result) # (1, 2)
        注意:
           1. return a, b 写法,返回多个数据的时候,默认是元组类型。
           2. return后⾯可以连接列表、元组或字典,以返回多个值。
四. 函数的参数
    1 位置参数
        位置参数:调⽤函数时根据函数定义的参数位置来传递参数。
            def user_info(name, age, gender):
                print(f'您的名字是{name}, 年龄是{age}, 性别是{gender}')
            user_info('TOM', 20, '男')
        注意:传递和定义参数的顺序及个数必须⼀致。
    2 关键字参数
        函数调⽤,通过“键=值”形式加以指定。可以让函数更加清晰、容易使⽤,同时也清除了参数的顺序需求。
            def user_info(name, age, gender):
                print(f'您的名字是{name}, 年龄是{age}, 性别是{gender}')
            user_info('Rose', age=20, gender='⼥')
            user_info('⼩明', gender='男', age=16)
        注意:函数调⽤时,如果有位置参数时,位置参数必须在关键字参数的前⾯,但关键字参数之间不存在先后顺序。
    3 缺省参数
        缺省参数也叫默认参数,⽤于定义函数,为参数提供默认值,调⽤函数时可不传该默认参数的值(注意:所有位置参数必须出现在默认参数前,包括函数定义和调⽤)。
            def user_info(name, age, gender='男'):
                 print(f'您的名字是{name}, 年龄是{age}, 性别是{gender}')
            user_info('TOM', 20)
            user_info('Rose', 18, '⼥')
        注意:函数调⽤时,如果为缺省参数传值则修改默认参数值;否则使⽤这个默认值。
    4 不定⻓参数
        不定⻓参数也叫可变参数。⽤于不确定调⽤的时候会传递多少个参数(不传参也可以)的场景。此时,可⽤包裹(packing)位置参数,或者包裹关键字参数,来进⾏参数传递,会显得⾮常⽅便。
        1.包裹位置传递
                def user_info(*args):
                     print(args)
                # ('TOM',)
                user_info('TOM')
                # ('TOM', 18)
                user_info('TOM', 18)
            注意:传进的所有参数都会被args变量收集,它会根据传进参数的位置合并为⼀个元组(tuple),args是元组类型,这就是包裹位置传递。
        2.包裹关键字传递
                def user_info(**kwargs):
                        print(kwargs)
                # {'name': 'TOM', 'age': 18, 'id': 110}
                user_info(name='TOM', age=18, id=110)
            综上:⽆论是包裹位置传递还是包裹关键字传递,都是⼀个组包的过程。

    五. 拆包和交换变量值
        1 拆包
            拆包:元组
                    def return_num():
                        return 100, 200
                    num1, num2 = return_num()
                    print(num1) # 100
                    print(num2) # 200
            拆包:字典
                    dict1 = {'name': 'TOM', 'age': 18} 
                    a, b = dict1
                    # 对字典进⾏拆包,取出来的是字典的key
                    print(a) # name
                    print(b) # age
                    print(dict1[a]) # TOM
                    print(dict1[b]) # 18     
        2 交换变量值    
                需求:有变量 a = 10 和 b = 20 ,交换两个变量的值。
                    a, b = 1, 2 a, b = b, a
                    print(a) # 2
                    print(b) # 1
    六. 引⽤                
        1 了解引⽤
                在python中,值是靠引⽤来传递来的。我们可以⽤id()来判断两个变量是否为同⼀个值的引⽤。 我们可以将id值理解为那块内存的地址标识。
                # 1. int类型
                a = 1 b = a
                print(b) # 1
                print(id(a)) # 140708464157520
                print(id(b)) # 140708464157520
                a = 2
                print(b) # 1,说明int类型为不可变类型
                print(id(a)) # 140708464157552,此时得到是的数据2的内存地址
                print(id(b)) # 140708464157520
                # 2. 列表
                aa = [10, 20]
                bb = aa
                print(id(aa)) # 2325297783432
                print(id(bb)) # 2325297783432
                aa.append(30)
                print(bb) # [10, 20, 30], 列表为可变类型
                print(id(aa)) # 2325297783432
                print(id(bb)) # 2325297783432
        2 引⽤当做实参
                  def test1(a):
                     print(a)
                     print(id(a))
                     a += a
                     print(a)
                     print(id(a))
                # int:计算前后id值不同
                b = 100
                test1(b)
                # 列表:计算前后id值相同
                c = [11, 22]
                test1(c)  
    七. 可变和不可变类型
        所谓可变类型与不可变类型是指:数据能够直接进⾏修改,如果能直接修改那么就是可变,否则是不可变.
        可变类型
                列表
                字典
                集合
        不可变类型
                整型
                浮点型
                字符串
                元组

01. 变量的引用

  • 变量 和 数据 都是保存在 内存 中的
  • Python函数 的 参数传递 以及 返回值 都是靠 引用 传递的

1.1 引用的概念

Python

  • 变量数据 是分开存储的
  • 数据 保存在内存中的一个位置
  • 变量 中保存着数据在内存中的地址
  • 变量记录数据的地址,就叫做 引用
  • 使用 id() 函数可以查看变量中保存数据所在的 内存地址

注意:如果变量已经被定义,当给一个变量赋值的时候,本质上是 修改了数据的引用

  • 变量 不再 对之前的数据引用
  • 变量 改为 对新赋值的数据引用

1.2 变量引用 的示例

Python 中,变量的名字类似于 便签纸 贴在 数据

1.3 函数的参数和返回值的传递

Python 中,函数的 实参/返回值 都是是靠 引用 来传递来的

def test(num):

    print("-" * 50)
    print("%d 在函数内的内存地址是 %x" % (num, id(num)))

    result = 100

    print("返回值 %d 在内存中的地址是 %x" % (result, id(result)))
    print("-" * 50)

    return  result

a = 10
print("调用函数前 内存地址是 %x" % id(a))

r = test(a)

print("调用函数后 实参内存地址是 %x" % id(a))
print("调用函数后 返回值内存地址是 %x" % id(r))

02. 可变和不可变类型

  • 不可变类型,内存中的数据不允许被修改:
    • 数字类型 int, bool, float, complex, long(2.x)
    • 字符串 str
    • 元组 tuple
  • 可变类型,内存中的数据可以被修改:
    • 列表 list
    • 字典 dict
a = 1
a = "hello"
a = [1, 2, 3]
a = [3, 2, 1]
demo_list = [1, 2, 3]

print("定义列表后的内存地址 %d" % id(demo_list))

demo_list.append(999)
demo_list.pop(0)
demo_list.remove(2)
demo_list[0] = 10

print("修改数据后的内存地址 %d" % id(demo_list))

demo_dict = {"name": "小明"}

print("定义字典后的内存地址 %d" % id(demo_dict))

demo_dict["age"] = 18
demo_dict.pop("name")
demo_dict["name"] = "老王"

print("修改数据后的内存地址 %d" % id(demo_dict))

注意:字典的 key 只能使用不可变类型的数据

注意

  1. 可变类型的数据变化,是通过 方法 来实现的
  2. 如果给一个可变类型的变量,赋值了一个新的数据,引用会修改
    • 变量 不再 对之前的数据引用
    • 变量 改为 对新赋值的数据引用

哈希 (hash)

  • Python 中内置有一个名字叫做 hash(o) 的函数
    • 接收一个 不可变类型 的数据作为 参数
    • 返回 结果是一个 整数
  • 哈希 是一种 算法,其作用就是提取数据的 特征码(指纹)
    • 相同的内容 得到 相同的结果
    • 不同的内容 得到 不同的结果
  • Python 中,设置字典的 键值对 时,会首先对 key 进行 hash 已决定如何在内存中保存字典的数据,以方便 后续 对字典的操作:增、删、改、查
    • 键值对的 key 必须是不可变类型数据
    • 键值对的 value 可以是任意类型的数据

03. 局部变量和全局变量

  • 局部变量 是在 函数内部 定义的变量,只能在函数内部使用
  • 全局变量 是在 函数外部定义 的变量(没有定义在某一个函数内),所有函数 内部 都可以使用这个变量

提示:在其他的开发语言中,大多 不推荐使用全局变量 —— 可变范围太大,导致程序不好维护!

1 局部变量

  • 局部变量 是在 函数内部 定义的变量,只能在函数内部使用
  • 函数执行结束后,函数内部的局部变量,会被系统回收
  • 不同的函数,可以定义相同的名字的局部变量,但是 彼此之间 不会产生影响

局部变量的作用

  • 在函数内部使用,临时 保存 函数内部需要使用的数据
def demo1():

    num = 10

    print(num)

    num = 20

    print("修改后 %d" % num)


def demo2():

    num = 100

    print(num)

demo1()
demo2()

print("over")

局部变量的生命周期

  • 所谓 生命周期 就是变量从 被创建被系统回收 的过程
  • 局部变量函数执行时 才会被创建
  • 函数执行结束后 局部变量 被系统回收
  • 局部变量在生命周期 内,可以用来存储 函数内部临时使用到的数据

2 全局变量

  • 全局变量 是在 函数外部定义 的变量,所有函数内部都可以使用这个变量
# 定义一个全局变量
num = 10


def demo1():

    print(num)


def demo2():

    print(num)

demo1()
demo2()

print("over")

注意:函数执行时,需要处理变量时 会:

  1. 首先 查找 函数内部 是否存在 指定名称 的局部变量如果有,直接使用
  2. 如果没有,查找 函数外部 是否存在 指定名称 的全局变量如果有,直接使用
  3. 如果还没有,程序报错!

1) 函数不能直接修改 全局变量的引用

  • 全局变量 是在 函数外部定义 的变量(没有定义在某一个函数内),所有函数 内部 都可以使用这个变量

提示:在其他的开发语言中,大多 不推荐使用全局变量 —— 可变范围太大,导致程序不好维护!

  • 在函数内部,可以 通过全局变量的引用获取对应的数据
  • 但是,不允许直接修改全局变量的引用 —— 使用赋值语句修改全局变量的值
num = 10


def demo1():

    print("demo1" + "-" * 50)

    # 只是定义了一个局部变量,不会修改到全局变量,只是变量名相同而已
    num = 100
    print(num)


def demo2():

    print("demo2" + "-" * 50)
    print(num)

demo1()
demo2()

print("over")

注意:只是在函数内部定义了一个局部变量而已,只是变量名相同 —— 在函数内部不能直接修改全局变量的值

2) 在函数内部修改全局变量的值

  • 如果在函数中需要修改全局变量,需要使用 global 进行声明
num = 10


def demo1():

    print("demo1" + "-" * 50)

    # global 关键字,告诉 Python 解释器 num 是一个全局变量
    global num
    # 只是定义了一个局部变量,不会修改到全局变量,只是变量名相同而已
    num = 100
    print(num)


def demo2():

    print("demo2" + "-" * 50)
    print(num)

demo1()
demo2()

print("over")

3) 全局变量定义的位置

  • 为了保证所有的函数都能够正确使用到全局变量,应该 将全局变量定义在其他函数的上方
a = 10


def demo():
    print("%d" % a)
    print("%d" % b)
    print("%d" % c)

b = 20
demo()
c = 30

注意

  • 由于全局变量 c,是在调用函数之后,才定义的,在执行函数时,变量还没有定义,所以程序会报错!

代码结构示意图如下

4) 全局变量命名的建议

  • 为了避免局部变量和全局变量出现混淆,在定义全局变量时,有些公司会有一些开发要求,例如:
  • 全局变量名前应该增加 g_ 或者 gl_ 的前缀

提示:具体的要求格式,各公司要求可能会有些差异

函数加强

Python 包含了以下内置函数:

函数 描述 备注
len(item) 计算容器中元素个数
del(item) 删除变量 del 有两种方式
max(item) 返回容器中元素最大值 如果是字典,只针对 key 比较
min(item) 返回容器中元素最小值 如果是字典,只针对 key 比较
cmp(item1, item2) 比较两个值,-1 小于/0 相等/1 大于 Python 3.x 取消了 cmp 函数
⼀. 应⽤:学员管理系统
# 定义功能界面函数
def info_print():
    print('请选择功能--------------')
    print('1、添加学员')
    print('2、删除学员')
    print('3、修改学员')
    print('4、查询学员')
    print('5、显示所有学员')
    print('6、退出系统')
    print('-' * 20)


# 等待存储所有学员的信息
info = []


# 添加学员信息的函数
def add_info():
    """添加学员函数"""
    # 1. 用户输入:学号、姓名、手机号
    new_id = input('请输入学号:')
    new_name = input('请输入姓名:')
    new_tel = input('请输入手机号:')

    # 2. 判断是否添加这个学员:如果学员姓名已经存在报错提示;如果姓名不存在添加数据
    global info
    # 2.1 不允许姓名重复:判断用户输入的姓名 和 列表里面字典的name对应的值 相等 提示
    for i in info:
        if new_name == i['name']:
            print('此用户已经存在')
            # return作用:退出当前函数,后面添加信息的代码不执行
            return

    # 2.2 如果输入的姓名不存在,添加数据:准备空字典,字典新增数据,列表追加字典
    info_dict = {}

    # 字典新增数据
    info_dict['id'] = new_id
    info_dict['name'] = new_name
    info_dict['tel'] = new_tel
    # print(info_dict)

    # 列表追加字典
    info.append(info_dict)
    print(info)


# 删除学员
def del_info():
    """删除学员"""
    # 1. 用户输入要删除的学员的姓名
    del_name = input('请输入要删除的学员的姓名:')

    # 2. 判断学员是否存在:存在则删除;不存在提示
    # 2.1 声明info是全局变量
    global info
    # 2.2 遍历列表
    for i in info:
        # 2.3 判断学员是否存在:存在执行删除(列表里面的字典),break:这个系统不允许重名,删除了一个后面的不需要再遍历;不存在提示
        if del_name == i['name']:
            # 列表删除数据 -- 按数据删除remove
            info.remove(i)
            break
    else:
        print('该学员不存在')

    print(info)


# 修改函数
def modify_info():
    """修改学员信息"""
    # 1. 用户输入想要修改的学员您的姓名
    modify_name = input('请输入要修改的学员的姓名:')

    # 2. 判断学员是否存在:存在修改手机号;不存在,提示
    # 2.1 声明info是全局
    global info
    # 2.2 遍历列表,判断输入的姓名==字典['name']
    for i in info:
        if modify_name == i['name']:
            # 将tel这个key修改值,并终止此循环
            i['tel'] = input('请输入新的手机号:')
            break
    else:
        # 学员不存在
        print('该学员不存在')

    # 3. 打印info
    print(info)


# 查询学员信息函数
def search_info():
    """查询学员信息"""
    # 1. 用户输入目标学员姓名
    search_name = input('请输入要查询的学员的姓名:')

    # 2. 检查学员是否存在:存在打印这个学员的信息;不存在则提示
    # 2.1 声明info为全局
    global info
    # 2.2 遍历info,判断输入的学员是否存在
    for i in info:
        if search_name == i['name']:
            # 学员存在:打印信息并终止循环
            print('查询到的学员信息如下---------------')
            print(f"学员的学号是{i['id']}, 姓名是{i['name']}, 手机号是{i['tel']}")
            break
    else:
        # 学员不存在的提示
        print('查无此人...')


# 显示所有学员信息
def print_all():
    """显示所有学员信息"""
    # 1. 打印提示字
    print('学号\t姓名\t手机号')
    # 2. 打印所有学员的数据
    for i in info:
        print(f"{i['id']}\t{i['name']}\t{i['tel']}")


# 系统功能需要循环使用,直到用户输入6,才退出系统
while True:
    # 1. 显示功能界面
    info_print()

    # 2. 用户输入功能序号
    user_num = int(input('请输入功能序号:'))

    # 3. 按照用户输入的功能序号,执行不同的功能(函数)
    # 如果用户输入1,执行添加;如果用户输入2,执行删除... -- 多重判断
    if user_num == 1:
        # print('添加')
        add_info()
    elif user_num == 2:
        # print('删除')
        del_info()
    elif user_num == 3:
        # print('修改')
        modify_info()
    elif user_num == 4:
        # print('查询')
        search_info()
    elif user_num == 5:
        # print('显示所有')
        print_all()
    elif user_num == 6:
        # print('退出系统')
        # 程序要想结束,退出终止while True -- break
        exit_flag = input('确定要退出吗?yes or no')
        if exit_flag == 'yes':
            break
    else:
        print('输入的功能序号有误')
⼆. 递归
    递归是⼀种编程思想,应⽤场景:
      1. 在我们⽇常开发中,如果要遍历⼀个⽂件夹下⾯所有的⽂件,通常会使⽤递归来实现;
      2. 很多算法都离不开递归,例如:快速排序。   
    1 递归的特点
        递归特点:函数内部自己调用自己;必须有出口
    2 应⽤:3以内数字累加和
        # 3 + 2 + 1
        def sum_numbers(num):
             # 1.如果是1,直接返回1 -- 出⼝
             if num == 1:
                 return 1
             # 2.如果不是1,重复执⾏累加:
             result = num + sum_numbers(num-1)
             # 3.返回累加结果
             return result
        sum_result = sum_numbers(3)
        # 输出结果为6
        print(sum_result)

三. lambda 表达式

    1 lambda的应⽤场景

        如果⼀个函数有⼀个返回值,并且只有⼀句代码,可以使⽤ lambda简化。

    2 lambda语法

        lambda 参数列表 : 表达式
        注意
            lambda表达式的参数可有可⽆,函数的参数在lambda表达式中完全适⽤。
            lambda函数能接收任何数量的参数但只能返回⼀个表达式的值
        # 1. 函数
        def fn1():
            return 100
        result = fn1()
        print(result)
        # 2. lambda  匿名函数
        # lambda 参数列表: 表达式
        fn2 = lambda: 100
        print(fn2)  # lambda内存地址 <function <lambda> at 0x0000023535CB3E18>

        # 100返回值 调用函数 
        print(fn2()) #100

   3 示例

        1 函数实现
            def add(a, b):
                return a + b
            result = add(1, 2)
            print(result)
        2 lambda实现
            print((lambda a, b: a + b)(1, 2))

   4 lambda的参数形式

        1.⽆参数 
            print((lambda: 100)())
        2.⼀个参数
            print((lambda a: a)('hello world'))
        3.默认参数
            print((lambda a, b, c=100: a + b + c)(10, 20))
        4.可变参数:**args
            print((lambda *args: args)(10, 20, 30))
            注意:这⾥的可变参数传⼊到lambda之后,返回值为元组。
        5.可变参数:**kwargs
            print((lambda **kwargs: kwargs)(name='python', age=20))

   5 lambda的应⽤

        1. 带判断的lambda
            print((lambda a, b: a if a > b else b)(1000, 500))
        2. 列表数据按字典key的值排序
            students = [
                 {'name': 'TOM', 'age': 20},
                 {'name': 'ROSE', 'age': 19},
                 {'name': 'Jack', 'age': 22}
            ]
            # 按name值升序排列
            students.sort(key=lambda x: x['name'])
            print(students)
            # 按name值降序排列
            students.sort(key=lambda x: x['name'], reverse=True)
            print(students)
            # 按age值升序排列
            students.sort(key=lambda x: x['age'])
            print(students)

四. ⾼阶函数
    把函数作为参数传⼊,这样的函数称为⾼阶函数,⾼阶函数是函数式编程的体现。函数式编程就是指这种⾼度抽象的编程范式。 

   1 体验⾼阶函数
        需求:⼀个函数完成计算任意两个数字的绝对值之和。
        在Python中, abs() 函数可以完成对数字求绝对值计算。
        abs(-10) # 10
        方式一、
            def add_num(a, b):
                return abs(a) + abs(b)
            result = add_num(-1, 2)
            print(result) # 3
        方式二、
            def sum_num(a, b, f):
                return f(a) + f(b)
            result = sum_num(-1, 2, abs)
            print(result) # 3
        注意:两种⽅法对⽐之后,发现,⽅法2的代码会更加简洁。
        函数式编程⼤量使⽤函数,减少了代码的重复,因此程序⽐较短,开发速度较快。
   2 内置⾼阶函数
        1 map()
            map(func, lst),将传⼊的函数变量func作⽤到lst变量的每个元素中,并将结果组成新的列表(Python2)/迭代器(Python3)返回。
            如:
                list1 = [1, 2, 3, 4, 5]
                def func(x):
                    return x ** 2
                result = map(func, list1)
                print(result) # <map object at 0x0000013769653198>
                print(list(result)) # [1, 4, 9, 16, 25]
        2 reduce()
            reduce(func(x,y),lst),其中func必须有两个参数。每次func计算的结果继续和序列的下⼀个元素做累积计算
            注意:reduce()传⼊的参数func必须接受2个参数。
            如:
                import functools
                list1 = [1, 2, 3, 4, 5]
                def func(a, b):
                    return a + b
                result = functools.reduce(func, list1)
                print(result) # 15
        3 filter()
            filter(func, lst)函数⽤于过滤序列, 过滤掉不符合条件的元素, 返回⼀个 filter 对象,如果要转换为列表,可以使⽤ list() 来转换。
            如:
                list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
                def func(x):
                    return x % 2 == 0
                result = filter(func, list1)
                print(result) # <filter object at 0x0000017AF9DC3198>
                print(list(result)) # [2, 4, 6, 8, 10]

01. 函数参数和返回值的作用

函数根据 有没有参数 以及 有没有返回值,可以 相互组合,一共有 4 种 组合形式

  1. 无参数,无返回值
  2. 无参数,有返回值
  3. 有参数,无返回值
  4. 有参数,有返回值

定义函数时,是否接收参数,或者是否返回结果,是根据 实际的功能需求 来决定的!

  1. 如果函数 内部处理的数据不确定,就可以将外界的数据以参数传递到函数内部
  2. 如果希望一个函数 执行完成后,向外界汇报执行结果,就可以增加函数的返回值

1.1 无参数,无返回值

此类函数,不接收参数,也没有返回值,应用场景如下:

  1. 只是单纯地做一件事情,例如 显示菜单
  2. 在函数内部 针对全局变量进行操作,例如:新建名片,最终结果 记录在全局变量

注意:

  • 如果全局变量的数据类型是一个 可变类型,在函数内部可以使用 方法 修改全局变量的内容 —— 变量的引用不会改变
  • 在函数内部,使用赋值语句 才会 修改变量的引用

1.2 无参数,有返回值

此类函数,不接收参数,但是有返回值,应用场景如下:

  • 采集数据,例如 温度计,返回结果就是当前的温度,而不需要传递任何的参数

1.3 有参数,无返回值

此类函数,接收参数,没有返回值,应用场景如下:

  • 函数内部的代码保持不变,针对 不同的参数 处理 不同的数据
  • 例如 名片管理系统 针对 找到的名片修改删除 操作

1.4 有参数,有返回值

此类函数,接收参数,同时有返回值,应用场景如下:

  • 函数内部的代码保持不变,针对 不同的参数 处理 不同的数据,并且 返回期望的处理结果
  • 例如 名片管理系统 使用 字典默认值提示信息 提示用户输入内容
    • 如果输入,返回输入内容
    • 如果没有输入,返回字典默认值

02. 函数的返回值 进阶

  • 在程序开发中,有时候,会希望 一个函数执行结束后,告诉调用者一个结果,以便调用者针对具体的结果做后续的处理
  • 返回值 是函数 完成工作后,最后 给调用者的 一个结果
  • 在函数中使用 return 关键字可以返回结果
  • 调用函数一方,可以 使用变量接收 函数的返回结果

问题:一个函数执行后能否返回多个结果?

示例 —— 温度和湿度测量

  • 假设要开发一个函数能够同时返回当前的温度和湿度
  • 先完成返回温度的功能如下:
def measure():
    """返回当前的温度"""

    print("开始测量...")
    temp = 39
    print("测量结束...")

    return temp

result = measure()
print(result)
  • 在利用 元组 在返回温度的同时,也能够返回 湿度
  • 改造如下:
def measure():
    """返回当前的温度"""

    print("开始测量...")
    temp = 39
    wetness = 10
    print("测量结束...")

    return (temp, wetness)

提示:如果一个函数返回的是元组,括号可以省略

技巧

  • Python 中,可以 将一个元组 使用 赋值语句 同时赋值给 多个变量
  • 注意:变量的数量需要和元组中的元素数量保持一致
result = temp, wetness = measure()

面试题 —— 交换两个数字

题目要求

  1. 有两个整数变量 a = 6, b = 100
  2. 不使用其他变量,交换两个变量的值

解法 1 —— 使用其他变量

# 解法 1 - 使用临时变量
c = b
b = a
a = c

解法 2 —— 不使用临时变量

# 解法 2 - 不使用临时变量
a = a + b
b = a - b
a = a - b

解法 3 —— Python 专有,利用元组

a, b = b, a

03. 函数的参数 进阶

3.1. 不可变和可变的参数

问题 1:在函数内部,针对参数使用 赋值语句,会不会影响调用函数时传递的 实参变量? —— 不会!

  • 无论传递的参数是 可变 还是 不可变
    • 只要 针对参数 使用 赋值语句,会在 函数内部 修改 局部变量的引用不会影响到 外部变量的引用
def demo(num, num_list):

    print("函数内部")

    # 赋值语句
    num = 200
    num_list = [1, 2, 3]

    print(num)
    print(num_list)

    print("函数代码完成")


gl_num = 99
gl_list = [4, 5, 6]
demo(gl_num, gl_list)
print(gl_num)
print(gl_list)

问题 2:如果传递的参数是 可变类型,在函数内部,使用 方法 修改了数据的内容,同样会影响到外部的数据

def mutable(num_list):

    # num_list = [1, 2, 3]
    num_list.extend([1, 2, 3])

    print(num_list)

gl_list = [6, 7, 8]
mutable(gl_list)
print(gl_list)

面试题 —— +=

  • python 中,列表变量调用 += 本质上是在执行列表变量的 extend 方法,不会修改变量的引用
def demo(num, num_list):

    print("函数内部代码")

    # num = num + num
    num += num
    # num_list.extend(num_list) 由于是调用方法,所以不会修改变量的引用
    # 函数执行结束后,外部数据同样会发生变化
    num_list += num_list

    print(num)
    print(num_list)
    print("函数代码完成")


gl_num = 9
gl_list = [1, 2, 3]
demo(gl_num, gl_list)
print(gl_num)
print(gl_list)

3.2 缺省参数

  • 定义函数时,可以给 某个参数 指定一个默认值,具有默认值的参数就叫做 缺省参数
  • 调用函数时,如果没有传入 缺省参数 的值,则在函数内部使用定义函数时指定的 参数默认值
  • 函数的缺省参数,将常见的值设置为参数的缺省值,从而 简化函数的调用
  • 例如:对列表排序的方法
gl_num_list = [6, 3, 9]

# 默认就是升序排序,因为这种应用需求更多
gl_num_list.sort()
print(gl_num_list)

# 只有当需要降序排序时,才需要传递 `reverse` 参数
gl_num_list.sort(reverse=True)
print(gl_num_list)

指定函数的缺省参数

  • 在参数后使用赋值语句,可以指定参数的缺省值
def print_info(name, gender=True):

    gender_text = "男生"
    if not gender:
        gender_text = "女生"

    print("%s 是 %s" % (name, gender_text))

提示

  1. 缺省参数,需要使用 最常见的值 作为默认值!
  2. 如果一个参数的值 不能确定,则不应该设置默认值,具体的数值在调用函数时,由外界传递!

缺省参数的注意事项

1) 缺省参数的定义位置
  • 必须保证 带有默认值的缺省参数 在参数列表末尾
  • 所以,以下定义是错误的!
def print_info(name, gender=True, title):

2) 调用带有多个缺省参数的函数
  • 调用函数时,如果有 多个缺省参数需要指定参数名,这样解释器才能够知道参数的对应关系!
def print_info(name, title="", gender=True):
    """

    :param title: 职位
    :param name: 班上同学的姓名
    :param gender: True 男生 False 女生
    """

    gender_text = "男生"

    if not gender:
        gender_text = "女生"

    print("%s%s 是 %s" % (title, name, gender_text))


# 提示:在指定缺省参数的默认值时,应该使用最常见的值作为默认值!
print_info("小明")
print_info("老王", title="班长")
print_info("小美", gender=False)

3.3 多值参数(知道)

定义支持多值参数的函数

  • 有时可能需要 一个函数 能够处理的参数 个数 是不确定的,这个时候,就可以使用 多值参数
  • python 中有 两种 多值参数:
    • 参数名前增加 一个 * 可以接收 元组
    • 参数名前增加 两个 * 可以接收 字典
  • 一般在给多值参数命名时,习惯使用以下两个名字
    • *args —— 存放 元组 参数,前面有一个 *
    • **kwargs —— 存放 字典 参数,前面有两个 *
  • argsarguments 的缩写,有变量的含义
  • kwkeyword 的缩写,kwargs 可以记忆 键值对参数
def demo(num, *args, **kwargs):

    print(num)
    print(args)
    print(kwargs)


demo(1, 2, 3, 4, 5, name="小明", age=18, gender=True)

提示:多值参数 的应用会经常出现在网络上一些大牛开发的框架中,知道多值参数,有利于我们能够读懂大牛的代码

多值参数案例 —— 计算任意多个数字的和

需求

  1. 定义一个函数 sum_numbers,可以接收的 任意多个整数
  2. 功能要求:将传递的 所有数字累加 并且返回累加结果
def sum_numbers(*args):

    num = 0
    # 遍历 args 元组顺序求和
    for n in args:
        num += n

    return num

print(sum_numbers(1, 2, 3))

元组和字典的拆包(知道)

  • 在调用带有多值参数的函数时,如果希望:
    • 将一个 元组变量,直接传递给 args
    • 将一个 字典变量,直接传递给 kwargs
  • 就可以使用 拆包,简化参数的传递,拆包 的方式是:
    • 元组变量前,增加 一个 *
    • 字典变量前,增加 两个 *
def demo(*args, **kwargs):

    print(args)
    print(kwargs)


# 需要将一个元组变量/字典变量传递给函数对应的参数
gl_nums = (1, 2, 3)
gl_xiaoming = {"name": "小明", "age": 18}

# 会把 num_tuple 和 xiaoming 作为元组传递个 args
# demo(gl_nums, gl_xiaoming)
demo(*gl_nums, **gl_xiaoming)

04. 函数的递归

函数调用自身的 编程技巧 称为递归

4.1 递归函数的特点

特点

  • 一个函数 内部 调用自己
    • 函数内部可以调用其他函数,当然在函数内部也可以调用自己

代码特点

  1. 函数内部的 代码 是相同的,只是针对 参数 不同,处理的结果不同
  2. 参数满足一个条件 时,函数不再执行
    • 这个非常重要,通常被称为递归的出口,否则 会出现死循环

示例代码

def sum_numbers(num):

    print(num)

    # 递归的出口很重要,否则会出现死循环
    if num == 1:
        return

    sum_numbers(num - 1)

sum_numbers(3)

4.2 递归案例 —— 计算数字累加

需求

  1. 定义一个函数 sum_numbers
  2. 能够接收一个 num 的整数参数
  3. 计算 1 + 2 + … num 的结果
def sum_numbers(num):

    if num == 1:
        return 1

    # 假设 sum_numbers 能够完成 num - 1 的累加
    temp = sum_numbers(num - 1)

    # 函数内部的核心算法就是 两个数字的相加
    return num + temp

print(sum_numbers(2))

提示:递归是一个 编程技巧,初次接触递归会感觉有些吃力!在处理 不确定的循环条件时,格外的有用,例如:遍历整个文件目录的结构

⼀. ⽂件操作的作⽤
    思考:什么是⽂件?
    思考:⽂件操作包含什么?
    答:打开、关闭、读、写、复制....
    思考:⽂件操作的的作⽤是什么?
    答:读取内容、写⼊内容、备份内容......
    总结:⽂件操作的作⽤就是把⼀些内容(数据)存储存放起来,可以让程序下⼀次执⾏的时候直接使⽤,⽽不必重新制作⼀份,省时省⼒。           
⼆. ⽂件的基本操作
    1 ⽂件操作步骤
        1. 打开⽂件
            在python,使⽤open函数,可以打开⼀个已经存在的⽂件,或者创建⼀个新⽂件,语法如下:
                open(name, mode)
                name:是要打开的⽬标⽂件名的字符串(可以包含⽂件所在的具体路径)。
                mode:设置打开⽂件的模式(访问模式):只读、写⼊、追加等。
            如:
                f = open('test.txt', 'w')
                注意:此时的 f 是 open 函数的⽂件对象。
        2. 读写等操作
            1 写
                ⽂件对象.write('内容')
                如:
                    # 1. 打开⽂件
                    f = open('test.txt', 'w')
                    # 2.⽂件写⼊
                    f.write('hello world')
                    # 3. 关闭⽂件
                    f.close()
                注意:
                    1. w 和 a 模式:如果⽂件不存在则创建该⽂件;如果⽂件存在, w 模式先清空再写⼊, a 模式直接末尾追加。
                    2. r 模式:如果⽂件不存在则报错。
            2 读
                ⽂件对象.read(num)
                    num表示要从⽂件中读取的数据的⻓度(单位是字节),如果没有传⼊num,那么就表示读取⽂件中所有的数据
                readlines()
                    readlines可以按照⾏的⽅式把整个⽂件中的内容进⾏⼀次性读取,并且返回的是⼀个列表,其中每⼀⾏的数据为⼀个元素。 
                    如:
                        f = open('test.txt')
                        content = f.readlines()
                        # ['hello world\n', 'abcdefg\n', 'aaa\n', 'bbb\n', 'ccc']
                        print(content)
                        # 关闭⽂件
                        f.close()
                readline()
                    ⼀次读取⼀⾏内容。    
                    如:
                        f = open('test.txt')
                        content = f.readline()
                        print(f'第⼀⾏:{content}')
                        content = f.readline()
                        print(f'第⼆⾏:{content}')
                        # 关闭⽂件
                        f.close()
            3 seek()
                ⽤来移动⽂件指针。
                ⽂件对象.seek(偏移量, 起始位置)
                起始位置:
                    0:⽂件开头
                    1:当前位置
                    2:⽂件结尾
        3. 关闭⽂件
                ⽂件对象.close()
        注意:可以只打开和关闭⽂件,不进⾏任何读写操作。
三. ⽂件备份

    如:
        old_name = input('请输⼊您要备份的⽂件名:')
        index = old_name.rfind('.') #.所处的索引
        if index > 0:
            postfix = old_name[index:] #获取.开始的后缀
        new_name = old_name[:index] + '[备份]' + postfix
        old_f = open(old_name, 'rb')
        new_f = open(new_name, 'wb')
        while True:
             con = old_f.read(1024)
             if len(con) == 0:
                break
              new_f.write(con)
        old_f.close()
        new_f.close()

四. ⽂件和⽂件夹的操作    
    在Python中⽂件和⽂件夹的操作要借助os模块⾥⾯的相关功能,具体步骤如下:
    1. 导⼊os模块 import os
    2. 使⽤ os 模块相关功能 os.函数名()             
        1 ⽂件重命名
            os.rename(⽬标⽂件名, 新⽂件名)
        2 删除⽂件
            os.remove(⽬标⽂件名)
        3 创建⽂件夹
            os.mkdir(⽂件夹名字)
        4 删除⽂件夹
            os.rmdir(⽂件夹名字)
        5 获取当前⽬录
            os.getcwd()
        6 改变默认⽬录
            os.chdir(⽬录)
        7 获取⽬录列表
            os.listdir(⽬录)
    综合使用:
        import os
        # 设置重命名标识:如果为1则添加指定字符,flag取值为2则删除指定字符
        flag = 1
        # 获取指定⽬录
        dir_name = './'
        # 获取指定⽬录的⽂件列表
        file_list = os.listdir(dir_name)
        # print(file_list)
        # 遍历⽂件列表内的⽂件
        for name in file_list:
             # 添加指定字符
             if flag == 1:
                new_name = 'Python-' + name
             # 删除指定字符
             elif flag == 2:
                 num = len('Python-')
                 new_name = name[num:]

             # 打印新⽂件名,测试程序正确性
             print(new_name)

             # 重命名
             os.rename(dir_name+name, dir_name+new_name)

⾯向对象基础

⼀. 理解⾯向对象    
        ⾯向对象是⼀种抽象化的编程思想,很多编程语⾔中都有的⼀种思想。
        例如:洗⾐服
        思考:⼏种途径可以完成洗⾐服?
        答: ⼿洗 和 机洗。
        ⼿洗:找盆 - 放⽔ - 加洗⾐粉 - 浸泡 - 搓洗 - 拧⼲⽔ - 倒⽔ - 漂洗N次 - 拧⼲ - 晾晒。
        机洗:打开洗⾐机 - 放⾐服 - 加洗⾐粉 - 按下开始按钮 - 晾晒。
        思考:对⽐两种洗⾐服途径,同学们发现了什么?
        答:机洗更简单
        思考:机洗,只需要找到⼀台洗⾐机,加⼊简单操作就可以完成洗⾐服的⼯作,⽽不需要关⼼洗⾐机内部发⽣了什么事情。
        总结:⾯向对象就是将编程当成是⼀个事物,对外界来说,事物是直接使⽤的,不⽤去管他内部的情况。⽽编程就是设置事物能够做什么事。
⼆. 类和对象
        思考:洗⾐机洗⾐服描述过程中,洗⾐机其实就是⼀个事物,即对象,洗⾐机对象哪来的呢?
        答:洗⾐机是由⼯⼚⼯⼈制作出来。
        思考:⼯⼚⼯⼈怎么制作出的洗⾐机?
        答:⼯⼈根据设计师设计的功能图纸制作洗⾐机。
        总结:图纸 → 洗⾐机 → 洗⾐服。
        在⾯向对象编程过程中,有两个重要组成部分:类 和 对象。
        类和对象的关系:⽤类去创建⼀个对象。
    1 理解类和对象
           类是对⼀系列具有相同特征和⾏为的事物的统称,是⼀个抽象的概念,不是真实存在的事物。
                特征即是属性
                ⾏为即是⽅法
                类⽐如是制造洗⾐机时要⽤到的图纸,也就是说类是⽤来创建对象。 
           对象是类创建出来的真实存在的事物,例如:洗⾐机。
                注意:开发中,先有类,再有对象。 

    2 ⾯向对象实现⽅法
           1 定义类
                Python2中类分为:经典类 和 新式类
                    语法
                        class 类名():
                             代码
                             ......
                        注意:类名要满⾜标识符命名规则,同时遵循⼤驼峰命名习惯。
                    如:
                        class Washer():
                            def wash(self):
                         print('我会洗⾐服')
           2 创建对象 
                对象⼜名实例。
                    语法:
                        对象名 = 类名()
                    如:
                        # 创建对象
                        washer = Washer()
                        # <__main__.Washer object at 0x0000018B7B224240>
                        print(washer)
                        # washer对象调⽤实例⽅法
                        washer.wash()
                    注意:创建对象的过程也叫实例化对象。
           3 self 
                self指的是调⽤该函数的对象。
                    如:
                        # 1. 定义类
                        class Washer():
                             def wash(self):
                                 print('我会洗⾐服')
                                 # <__main__.Washer object at 0x0000024BA2B34240>
                                 print(self)

                        # 2. 创建对象
                        washer = Washer()
                        # <__main__.Washer object at 0x0000018B7B224240>
                        print(washer)
                        # washer对象调⽤实例⽅法
                        washer.wash()
                    注意:打印对象和self得到的结果是⼀致的,都是当前对象的内存中存储地址。

三. 添加和获取对象属性 
        属性即是特征,⽐如:洗⾐机的宽度、⾼度、重量...
        对象属性既可以在类外⾯添加和获取,也能在类⾥⾯添加和获取。
    1 类外⾯添加对象属性
        对象名.属性名 = 值
        如:
            washer.width = 500
            washer.height = 800
    2 类外⾯获取对象属性
        对象名.属性名
    3 类⾥⾯获取对象属性
        self.属性名
        如:
            # 定义类
            class Washer():
                def print_info(self):
                     # 类⾥⾯获取实例属性
                     print(f'宽度是{self.width}')
                     print(f'⾼度是{self.height}')
    四. 魔法⽅法
        在Python中, __xx__() 的函数叫做魔法⽅法,指的是具有特殊功能的函数。
        1 __init__() __init__()    
           作⽤:初始化对象。
                如:
                    class Washer():
                         # 定义初始化功能的函数
                         def __init__(self):
                             # 添加实例属性
                             self.width = 500
                             self.height = 800
                        def print_info(self):
                            # 类⾥⾯调⽤实例属性
                            print(f'洗⾐机的宽度是{self.width}, ⾼度是{self.height}')
                    meidi = Washer()
                    meidi.print_info()
                注意:
                    __init__() ⽅法,在创建⼀个对象时默认被调⽤,不需要⼿动调⽤
                    __init__(self) 中的self参数,不需要开发者传递,python解释器会⾃动把当前的对象引⽤传递过去。

                带参数的__init__() __init__()
                    思考:⼀个类可以创建多个对象,如何对不同的对象设置不同的初始化属性呢?
                    答:传参数。
                    如:
                        class Washer():
                             def __init__(self, width, height):
                                 self.width = width
                                 self.height = height
                             def print_info(self):
                                 print(f'洗⾐机的宽度是{self.width}')
                                 print(f'洗⾐机的⾼度是{self.height}')
                            meidi = Washer(10, 20)
                            meidi.print_info()
                            meidi1 = Washer(30, 40)
                            meidi1.print_info()
        2 __str__() __str__()
            当使⽤print输出对象的时候,默认打印对象的内存地址。如果类定义了 __str__ ⽅法,那么就会打印从在这个⽅法中 return 的数据。
            如:
                class Washer():
                     def __init__(self, width, height):
                         self.width = width
                         self.height = height
                     def __str__(self):
                         return '这是美的洗⾐机的说明书'
                    meidi = Washer(10, 20)
                    # 这是海尔洗⾐机的说明书
                    print(meidi)

        3 __del__() 
            当删除对象时,python解释器也会默认调⽤ __del__() ⽅法。
            如:
                class Washer():
                     def __init__(self, width, height):
                         self.width = width
                         self.height = height
                     def __del__(self):
                         print(f'{self}对象已经被删除')
                    meidi = Washer(10, 20)
                    # <__main__.Washer object at 0x0000026118223278>对象已经被删除
                    del meidi
    五.案例:
         class Furniture():
            def __init__(self, name, area):
                self.name = name
                self.area = area
        class Home():
            def __init__(self, address, area):
                # 地理位置
                self.address = address
                # 房屋面积
                self.area = area
                # 剩余面积
                self.free_area = area
                # 家具列表
                self.furniture = []

            def __str__(self):
                return f'房子地理位置在{self.address}, 房屋面积是{self.area}, 剩余面积{self.free_area}, 家具有{self.furniture}'

            def add_furniture(self, item):
                """容纳家具"""
                # 如果 家具占地面积 <= 房子剩余面积:可以搬入(家具列表添加家具名字数据并房子剩余面积更新:
                # 房屋剩余面积 - 该家具的占地面积
                # 否则:提示用户家具太大,剩余面积不足,无法容纳
                # )
                if item.area <= self.free_area:
                    self.furniture.append(item.name)
                    self.free_area -= item.area
                else:
                    print('家具太大,剩余面积不足,无法容纳')
        # 双人床, 6
        bed = Furniture('双人床', 6)
        sofa = Furniture('沙发', 10)
        # 房子1: 北京, 1000
        jia1 = Home('北京', 1000)
        print(jia1)
        jia1.add_furniture(bed)
        print(jia1)
        ball = Furniture('篮球场', 2000)
        jia1.add_furniture(ball)
        print(jia1)

⾯向对象-继承

⼀. 继承的概念
    Python⾯向对象的继承指的是多个类之间的所属关系,即⼦类默认继承⽗类的所有属性和⽅法
    如:
        # 继承:子类默认继承父类的所有属性和方法
        # 1. 定义父类
        class A(object):
            def __init__(self):
                self.num = 1
            def info_print(self):
                print(self.num)
        # 2. 定义子类 继承父类
        class B(A):
            pass
        # 3. 创建对象,验证结论
        result = B()
        result.info_print()
    在Python中,所有类默认继承object类,object类是顶级类或基类;其他⼦类叫做派⽣类。
⼆. 单继承
    class 类名(父类类名):
        。。。。。

⼆. 多继承
    class 类名(父类类名1,父类类名2,....):
        。。。。。
    多继承意思就是⼀个类同时继承了多个⽗类。
    注意:当⼀个类有多个⽗类的时候,默认使⽤第⼀个⽗类的同名属性和⽅法。

四. ⼦类重写⽗类同名⽅法和属性
    ⼦类和⽗类具有同名属性和⽅法,默认使⽤⼦类的同名属性和⽅法。
    如:
        # 1. 师父类,属性和方法
        class Master(object):
            def __init__(self):
                self.kongfu = '[太极拳]'

            def make_cake(self):
                print(f'运用{self.kongfu}制作蛋糕')
        # 为了验证多继承,添加School父类
        class School(object):
            def __init__(self):
                self.kongfu = '[八卦掌]'
            def make_cake(self):
                print(f'运用{self.kongfu}制作蛋糕')
        # 2. 定义徒弟类,继承师父类 和 学校类, 添加和父类同名的属性和方法
        class Prentice(School, Master):
            def __init__(self):
                self.kongfu = '[降龙十八掌]'
            def make_cake(self):
                print(f'运用{self.kongfu}制作蛋糕')
        # 3. 用徒弟类创建对象,调用实例属性和方法
        prentice = Prentice()
        print(prentice.kongfu)
        daqiu.make_cake()
        # 结论:如果子类和父类拥有同名属性和方法,子类创建对象调用属性和方法的时候,调用到的是子类里面的同名属性和方法
五. ⼦类调⽤⽗类的同名⽅法和属性
    如:
        # 1. 师父类,属性和方法
        class Master(object):
            def __init__(self):
                self.kongfu = '[太极拳]'
            def make_cake(self):
                print(f'运用{self.kongfu}制作蛋糕')
        # 为了验证多继承,添加School父类
        class School(object):
            def __init__(self):
                self.kongfu = '[八卦掌]'
            def make_cake(self):
                print(f'运用{self.kongfu}制作蛋糕')
        # 2. 定义徒弟类,继承师父类 和 学校类, 添加和父类同名的属性和方法
        class Prentice(School, Master):
            def __init__(self):
                self.kongfu = '[降龙十八掌]'
            def make_cake(self):
                # 加自己的初始化的原因:如果不加这个自己的初始化,kongfu属性值是上一次调用的init内的kongfu属性值
                self.__init__()
                print(f'运用{self.kongfu}制作蛋糕')
            # 子类调用父类的同名方法和属性:把父类的同名属性和方法再次封装
            def make_master_cake(self):
                # 父类类名.函数()
                # 再次调用初始化的原因:这里想要调用父类的同名方法和属性,属性在init初始化位置,所以需要再次调用init
                Master.__init__(self)
                Master.make_cake(self)
            def make_school_cake(self):
                School.__init__(self)
                School.make_cake(self)
        # 3. 用徒弟类创建对象,调用实例属性和方法
        prentice = Prentice()
        prentice.make_cake()
        prentice.make_master_cake()
        prentice.make_school_cake()
        prentice.make_cake()
六.super()调⽤⽗类⽅法
    如:
      # ⼀次性调⽤⽗类的同名属性和⽅法
     def make_old_cake(self):
         # ⽅法⼀:代码冗余;⽗类类名如果变化,这⾥代码需要频繁修改
         # Master.__init__(self)
         # Master.make_cake(self)
         # School.__init__(self)
         # School.make_cake(self)
         # ⽅法⼆: super()
         # ⽅法2.1 super(当前类名, self).函数()
         # super(Prentice, self).__init__()
         # super(Prentice, self).make_cake()  
         # ⽅法2.2 super().函数()
         super().__init__()
         super().make_cake()  
    注意:使⽤super() 可以⾃动查找⽗类。调⽤顺序遵循 __mro__ 类属性的顺序。⽐较适合单继承使⽤。

⼋. 私有权限
    1 定义私有属性和⽅法
        在Python中,可以为实例属性和⽅法设置私有权限,即设置某个实例属性或实例⽅法不继承给⼦类。
        设置私有权限的⽅法:在属性名和⽅法名 前⾯ 加上两个下划线 __。
        class Prentice(object):
            def __init__(self):
                self.kongfu = '[技术]'
                # self.money = 2000000
                # 定义私有属性
                self.__money = 2000000
            # 定义私有方法
            def __info_print(self):
                print('这是私有方法')
            def make_cake(self):
                self.__init__()
                print(f'运用')
    2 获取和修改私有属性值
        在Python中,⼀般定义函数名 get_xx ⽤来获取私有属性,定义 set_xx ⽤来修改私有属性值。
        class Prentice(object):
            def __init__(self):
                self.kongfu = '[技术]'
                # 定义私有属性
                self.__money = 2000000
            # 定义函数:获取私有属性值 get_xx
            def get_money(self):
                return self.__money
            # 定义函数:修改私有属性值 set_xx
            def set_money(self):
                self.__money = 500
            # 定义私有方法
            def __info_print(self):
                print('这是私有方法')
            def make_cake(self):
                self.__init__()
                print(f'运用')

九.总结
    继承的特点
        ⼦类默认拥有⽗类的所有属性和⽅法
        ⼦类重写⽗类同名⽅法和属性
        ⼦类调⽤⽗类同名⽅法和属性
    super()⽅法快速调⽤⽗类⽅法
    私有权限
        不能继承给⼦类的属性和⽅法需要添加私有权限
        class 类名():
         # 私有属性
         __属性名 = 值
         # 私有⽅法
         def __函数名(self):
            代码

⾯向对象-其他

⼀. ⾯向对象三⼤特性

封装
    将属性和⽅法书写到类的⾥⾯的操作即为封装
    封装可以为属性和⽅法添加私有权限
继承
    ⼦类默认继承⽗类的所有属性和⽅法
    ⼦类可以重写⽗类属性和⽅法
多态
    传⼊不同的对象,产⽣不同的结果

⼆. 多态

1 了解多态
    多态指的是⼀类事物有多种形态,(⼀个抽象类有多个⼦类,因⽽多态的概念依赖于继承)。
    定义:多态是⼀种使⽤对象的⽅式,⼦类重写⽗类⽅法,调⽤不同⼦类对象的相同⽗类⽅法,可以产⽣不同的执⾏结果
    好处:调⽤灵活,有了多态,更容易编写出通⽤的代码,做出通⽤的编程,以适应需求的不断变化
    实现步骤:
        定义⽗类,并提供公共⽅法
        定义⼦类,并重写⽗类⽅法
        传递⼦类对象给调⽤者,可以看到不同⼦类执⾏效果不同
    如:
        # 需求:警务人员和警犬一起工作,警犬分2种:追击敌人和追查毒品,携带不同的警犬,执行不同的工作
        # 1. 定义父类,提供公共方法: 警犬 和 人
        class Dog(object):
            def work(self):
                pass
        # 2. 定义子类,子类重写父类方法:定义2个类表示不同的警犬
        class ArmyDog(Dog):
            def work(self):
                print('追击敌人...')
        class DrugDog(Dog):
            def work(self):
                print('追查毒品...')
        # 定义人类
        class Person(object):
            def work_with_dog(self, dog):
                dog.work()
        # 3. 创建对象,调用不同的功能,传入不同的对象,观察执行的结果
        ad = ArmyDog()
        dd = DrugDog()
        person = Person()
        person.work_with_dog(ad)
        person.work_with_dog(dd)

三. 类属性和实例属性

1 类属性    
    1 设置和访问类属性
        类属性就是 类对象 所拥有的属性,它被 该类的所有实例对象 所共有。
        类属性可以使⽤ 类对象 或 实例对象 访问。
        如:
            # 1. 定义类,定义类属性
            class Dog(object):
                tooth = 10
        类属性的优点:
            类的实例 记录的某项数据 始终保持⼀致时,则定义类属性。
            实例属性 要求每个对象 为其单独开辟⼀份内存空间来记录数据,⽽类属性为全类所共有,仅占⽤⼀份内存,更加节省内存空间。
    2 修改类属性    
        类属性只能通过类对象修改,不能通过实例对象修改,如果通过实例对象修改类属性,表示的是创建了⼀个实例属性。
        如:
            class Dog(object):
                tooth = 10
            wangcai = Dog()
            xiaohei = Dog()
            # 修改类属性
            Dog.tooth = 12
            print(Dog.tooth) # 12
            print(wangcai.tooth) # 12
            print(xiaohei.tooth) # 12
            # 不能通过对象修改属性,如果这样操作,实则是创建了⼀个实例属性
            wangcai.tooth = 20
            print(Dog.tooth) # 12
            print(wangcai.tooth) # 20
            print(xiaohei.tooth) # 12

2 实例属性
    class Dog(object):
        def __init__(self):
            self.age = 5
        def info_print(self):
            print(self.age)
    dogone = Dog()
    print(dogone.age) # 5
    # print(Dog.age) # 报错:实例属性不能通过类访问
    dogone.info_print() # 5

四. 类⽅法和静态⽅法

1 类⽅法
    1 类⽅法特点
        第⼀个形参是类对象的⽅法
        需要⽤装饰器 @classmethod 来标识其为类⽅法,对于类⽅法,第⼀个参数必须是类对象,⼀般以cls 作为第⼀个参数
    2 类⽅法使⽤场景
        当⽅法中 需要使⽤类对象 (如访问私有类属性等)时,定义类⽅法
        类⽅法⼀般和类属性配合使⽤
        如:
            # 1. 定义类:私有类属性,类方法获取这个私有类属性
            class Dog(object):
                __tooth = 10
                # 定义类方法
                @classmethod
                def get_tooth(cls):
                    return cls.__tooth
            # 2. 创建对象,调用类方法
            dogone = Dog()
            result = dogone.get_tooth()
            print(result)
2 静态⽅法    
    1 静态⽅法特点
        需要通过装饰器 @staticmethod 来进⾏修饰,静态⽅法既不需要传递类对象也不需要传递实例对象(形参没有self/cls)。
        静态⽅法 也能够通过 实例对象 和 类对象 去访问。

    2 静态⽅法使⽤场景
        当⽅法中 既不需要使⽤实例对象(如实例对象,实例属性),也不需要使⽤类对象 (如类属性、类⽅法、创建实例等)时,定义静态⽅法
        取消不需要的参数传递,有利于 减少不必要的内存占⽤和性能消耗
        如:
           # 1. 定义类,定义静态方法
            class Dog(object):
                @staticmethod
                def info_print():
                    print('这是一个静态方法')
            # 2. 创建对象
            dogone = Dog()
            # 3. 调用静态方法:类 和 对象
            dogone.info_print()
            Dog.info_print()

异常:

⼀. 了解异常
        当检测到⼀个错误时,解释器就⽆法继续执⾏了,反⽽出现了⼀些错误的提示,这就是所谓的"异常"。
        例如:以 r ⽅式打开⼀个不存在的⽂件。
        open('test.txt', 'r')
⼆. 异常的写法
    1 语法:
         try:
            可能发⽣错误的代码
         except:
            如果出现异常执⾏的代码   
    2 快速体验
         try:
             f = open('test.txt', 'r')
         except:
             f = open('test.txt', 'w')   

    3 捕获指定异常
         try:
            可能发⽣错误的代码
         except 异常类型:
            如果捕获到该异常类型执⾏的代码
         如:
                try:
                    # print(num)
                    print(1/0)
                except NameError:
                    print('有错误')
         注意:
              1. 如果尝试执⾏的代码的异常类型和要捕获的异常类型不⼀致,则⽆法捕获异常。
              2. ⼀般try下⽅只放⼀⾏尝试执⾏的代码。
    4 捕获多个指定异常              
         当捕获多个异常时,可以把要捕获的异常类型的名字,放到except 后,并使⽤元组的⽅式进⾏书写
            try:
                print(1/0)
            except (NameError, ZeroDivisionError):
                print('有错误')    

    5 捕获异常描述信息
            try:
                print(1/0)
            except (NameError, ZeroDivisionError) as result:
                print(result)
    6 捕获所有异常
            Exception是所有程序异常类的⽗类。
            如:
                # 尝试执行打印num,捕获Exception 打印异常描述信息
                try:
                    print(num)
                except Exception as result:
                    print(result)
    7 异常的else
            try:
                print(1)
            except Exception as result:
                print(result)
            else:
                print('我是else,当没有异常的时候执行的代码')
    8 异常的finally
            finally表示的是⽆论是否异常都要执⾏的代码,例如关闭⽂件。
            如:
                # 需求:尝试以r打开文件,如果有异常以w打开这个文件,最终关闭文件
                try:
                    f = open('test100.txt', 'r')
                except Exception as e:
                    f = open('test100.txt', 'w')
                finally:
                    f.close()
三. 异常的传递
    如:
        # 需求1: 尝试只读打开test.txt 文件存在读取内容,不存在提示用户
        # 需求2:读取内容:循环读取,当无内容的时候退出循环,如果用户意外终止,提示用户已经被意外终止
        import time
        try:
            f = open('test.txt')
            # 尝试循环读取内容
            try:
                while True:
                    con = f.readline()
                    # 如果读取完成退出循环
                    if len(con) == 0:
                        break

                    time.sleep(3)
                    print(con)
            except:
                # 在命令提示符中如果按下ctrl+C结束终止的键
                print('程序被意外终止')
        except:
            print('该文件不存在')

四. ⾃定义异常
    在Python中,抛出⾃定义异常的语法为 raise 异常类对象 。
    预发:
        # 1. ⾃定义异常类
        class 异常类类名(Exception):
            代码
            # 设置抛出异常的描述信息
             def __str__(self):
                    return ...
        # 2. 抛出异常
        raise 异常类名()
        # 捕获异常
        except Exception...
    需求:密码⻓度不⾜,则报异常(⽤户输⼊密码,如果输⼊的⻓度不⾜3位,则报错,即抛出⾃定义异常,并捕获该异常)。
    如:
       class ShortInputError(Exception):
            def __init__(self, length, min_len):
                # 用户输入的密码长度
                self.length = length
                # 系统要求的最少长度
                self.min_len = min_len
            # 设置异常描述信息
            def __str__(self):
                return f'您输入的密码长度是{self.length}, 密码不能少于{self.min_len}'
        def main():
            # 2. 抛出异常: 尝试执行:用户输入密码,如果长度小于3,抛出异常
            try:
                password = input('请输入密码:')
                if len(password) < 3:
                    # 抛出异常类创建的对象
                    raise ShortInputError(len(password), 3)
            # 3. 捕获该异常
            except Exception as result:
                print(result)
            else:
                print('没有异常,密码输入完成')
        main()

模块和包:

⼀. 模块
        Python 模块(Module),是⼀个 Python ⽂件,以 .py 结尾,包含了 Python 对象定义和Python语句。
        模块能定义函数,类和变量,模块⾥也能包含可执⾏的代码。
    1. 导⼊模块
        1 导⼊模块的⽅式
                import 模块名
                from 模块名 import 功能名
                from 模块名 import *
                import 模块名 as 别名
                from 模块名 import 功能名 as 别名
        2 导⼊⽅式详解
            1 import
                    # 1. 导⼊模块
                    import 模块名
                    import 模块名1, 模块名2...
                    # 2. 调⽤功能
                    模块名.功能名()
                如:
                    import math
                    print(math.sqrt(9)) # 3.0
            2 from..import..
                    from 模块名 import 功能1, 功能2, 功能3...
                如:
                    from math import sqrt
                    print(sqrt(9))
            3 from .. import *
                    from 模块名 import *
                如:
                    from math import *
                    print(sqrt(9))
            4 as定义别名
                    # 模块定义别名
                    import 模块名 as 别名
                    # 功能定义别名
                    from 模块名 import 功能 as 别名
                如:
                    # 模块别名
                    import time as tt
                    tt.sleep(2)
                    print('hello')
                    # 功能别名
                    from time import sleep as sl
                    sl(2)
                    print('hello')
        3. 模块定位顺序
            当导⼊⼀个模块,Python解析器对模块位置的搜索顺序是:
                1. 当前⽬录
                2. 如果不在当前⽬录,Python则搜索在shell变量PYTHONPATH下的每个⽬录。
                3. 如果都找不到,Python会察看默认路径。UNIX下,默认路径⼀般为/usr/local/lib/python/
                模块搜索路径存储在system模块的sys.path变量中。变量⾥包含当前⽬录,PYTHONPATH和由安装过程决定的默认⽬录。
                注意
                    ⾃⼰的⽂件名不要和已有模块名重复,否则导致模块功能⽆法使⽤
                    使⽤from 模块名 import 功能 的时候,如果功能名字重复,调⽤到的是最后定义或导⼊的功能。
        4. __all__ 
            如果⼀个模块⽂件中有 __all__ 变量,当使⽤ from xxx import * 导⼊时,只能导⼊这个列表中的元素。
               如:
                my_module1模块代码
                     __all__ = ['testA']
                    def testA():
                        print('testA')
                    def testB():
                        print('testB') 
                导⼊模块的⽂件代码
                    from my_module1 import *
                    testA()
                    testB() #报错 不存在该方法

⼆. 包
        包将有联系的模块组织在⼀起,即放到同⼀个⽂件夹下,并且在这个⽂件夹创建⼀个名字为 __init__.py ⽂件,那么这个⽂件夹就称之为包。
    1 制作包
            [New] — [Python Package] — 输⼊包名 — [OK] — 新建功能模块(有联系的模块)。
            注意:新建包后,包内部会⾃动创建 __init__.py ⽂件,这个⽂件控制着包的导⼊⾏为。
        1 快速体验
            1. 新建包 mypackage
            2. 新建包内模块: my_module1 和 my_module2
            3. 模块内代码如下
            # my_module1
            print(1)
            def info_print1():
                print('my_module1')
            # my_module2
            print(2)
            def info_print2():
                print('my_module2')
        2 导⼊包    
            1 ⽅法⼀   
                import 包名.模块名
                包名.模块名.⽬标
                如:
                    import my_package.my_module1
                    my_package.my_module1.info_print1()
            ⽅法⼆
                注意:必须在 __init__.py ⽂件中添加 __all__ = [] ,控制允许导⼊的模块列表。
                from 包名 import *
                模块名.⽬标
                如:
                    from my_package import *
                    my_module1.info_print1()