Python笔记

Python基础

1.字面量

代码中,被写在代码中的固定的值,称之为字面量。

常见的字面量

整数、浮点数、字符串

2.注释

Python中单行注释

#(空格)单行注释

Python中的多行注释三个引号开头三个引号结尾

“”“

多行注释

“”“

3.变量

变量的定义格式为 变量名称=变量的值

print输出语句的格式为 print(内容1,内容2…)

如果不想换行可以使用end=’ ’ , \t制表符可以使内容对齐, \n换行

例如

print("内容1", "内容2", end=' ')

变量的特征是 变量的值可以改变

4.数据类型

string 字符串类型

int 整型

float 浮点型

type(字面量或者变量) 返回这个字面量或变量的数据类型,可以用一个变量接收 如 name_type=type(name)

在Python中变量是没有类型的但是它存储的数据有类型

5.数据类型转换

强转

int(X) 将X转换为一个整数

float(x) 将X转换为一个浮点数

str(X) 将X转换为一个字符串

(1)任何类型都能转为字符串

(2)浮点数转为整数会丢失精度

6.标识符

标识符的概念

我们给变量起的名字、方法的名字、类的名字等统称为标识符

内容限定

Python中只允许出现英文、数字、下划线这四类元素,数字不能放在开头使用

大小写敏感

Python中可以区分大小不同的变量名等命名

不可使用关键字

变量命名规范

1)见名知意

2)下划线命名法

多个单词用下划线连接

3)英文字母全小写

7.运算符

算术运算符

+加

-减

*乘

/除

//取整除

%取余

**指数

赋值运算符和复合运算符

num = 2

num += 3

8.字符串的三种定义方式

单引号’ 内容 ‘

双引号“ 内容 ”

三引号“”“ 内容 ”“”

可以用’ \ '对双引号转义

字符串的拼接

可以用+对字符串进行拼接

也可以用+对变量进行拼接

如 print(“内容1”+content)

注意:Python字符串不能用+直接拼接数值类型的变量或字面量或其他类型的数据

9.字符串格式化

在Python中可以用

%s 占位并且把数据类型转为字符串类型

%d 占位并且把数据类型转为int整型

%f 占位并且把数据类型转为浮点类型默认保留6位小数

如单个占位符单个变量写法

print('姓名:%s' % name)

多个变量多个占位符写法

name = 'Tom'
age = 18
score = 98.6
print('姓名:%s  age:%d  分数:%f' % (name, age, score))

占位符2

在分号前面加一个f (format格式化的意思) 然后在需要拼接的位置添加{变量}即可 但是他并不会进行精度和类型控制

name = 'Tom'
age = 18
score = 98.6
print(f'姓名:{name}  年龄:{age}  分数{score}')

10.占位符数字精度控制

精度控制的语法是

m.n的形式控制,如%5d、%6.2f、%.2f

m和.n均可省略

%5.2f 表示限制宽度(长度)为5,小数部分保留2位数,小数会四舍五入

11.表达式和表达式的格式化

表达式:一条具有明确执行结果的代码语句

12数据输入input语句

用一个变量接收输入的结果

name = input()

示例

age = input("请输入年龄:")
print(f"你的年龄是{age}岁")

input() 不管输入的是什么数据都会当成字符串看待

13.布尔类型

布尔类型有两个值True和False 在Python中True和False首字母都是大写的

比较运算符

==, !=, >, <, >=, <=

这些运算符会返回一个布尔值True 或False

14.if语句

Python中if语句语法

if 判断条件:

​ 执行内容

elif 判断条件:

​ 执行内容

else:

​ 执行内容

age = 26
if age >= 18:
    print("已成年")
elif age >= 20:
    print("你已满20岁将会有特殊待遇")
else:
    print("未成年")
    
# 猜数字小游戏
num = random.randint(1, 9)
if int(input("请输入您猜的数字:")) == num:
    print("恭喜你猜对了!")
elif int(input("您猜错了,再猜一次:")) == num:
    print("恭喜你猜对了")
elif int(input("您猜错了,再猜一次:")) == num:
    print("恭喜你猜对了")
else:
    print("对不起,您的机会已用完,正确的数字是:%d" % num)

知识点:程序从上到下执行,可以在条件判断中直接写入input语句进行判断

15.嵌套if

示例代码

score = 90
age = 18
if score >= 60:
    if age >= 18:
        print("及格,已成年")
    else:
        print("及格,未成年")
else:
    if age >= 18:
        print("不及格,已成年")
    else:
        print("不及格,未成年")
        
# 示例2
age = 30
year = 3
if age >= 18:
    if age <= 30:
        if year >= 3:
            print("年龄和入职时间符合条件发放奖励")
        else:
            print("入职时间小于3年不符合领取条件")
    else:
        print("年龄大于30岁不符合领取条件")
else:
    print("年龄小于18岁不符合领取条件")

16.while循环

Python中while循环语法

while 条件(TrueFalse:
    执行内容
    改变条件(如果不改变将会死循环)
# 示例
i = 1
number_sum = 0
while i <= 100:
    number_sum += i
    i += 1
print(number_sum)

# 猜数字小游戏while循环版
import random
i = 0
num = random.randint(1, 100)
time = 10
while i < time:
    ran_input = int(input("请输入您猜的数字:"))
    if ran_input == num:
        print("恭喜你猜对了,游戏结束!")
        break
    else:
        if ran_input > num and i <= time-2:
            print(f"猜大了再猜一次")
            i += 1
        elif i <= time-2:
            print(f"猜小了再猜一次")
            i += 1
        else:
            print(f"您的机会已用完正确的数字是{num}, 游戏结束!")
            i += 1

17.while嵌套循环

# 嵌套循环打印出直角三角行
i = 1
my_str = "*"
while i < 2:
    j = 1
    while j <= 5:
        print(my_str)
        my_str += "*"
        j += 1
    i += 1
    
# while嵌套 九九乘法表练习
i = 1
while i <= 9:
    j = 1
    while j <= i:
        print(f"{j}*{i}={j*i}\t", end='')
        j += 1
    i += 1
    print()

18.for循环

for循环概念 :for循环是一种轮询机制,是对一批数据内容进行逐个处理

Python中for循环语法

for 临时变量 in 待处理数据集:
	执行内容(可以对数据集中的每一个数据进行处理)

示例:

# for循环示例 输入数组中的每个元素
num = [1, 2, 3, 4, 5, 6]
for n in num:
    print(n)

for 循环遍历多个列表

list1 = ['a', 'b', 'c', 'd']
list2 = [1, 2, 3]

# 使用zip()关键字 将两个列表包起来方法一个循环中
# 这样可以一次性遍历两个列表
# 也可以用两个键值接收列表对应的元素
# 如果两个列表的长度不一致,
# 那么循环次数只会根据短的列表进行
for key, value in zip(list1, list2):
    print(key, value)

Python中的for循环无法构成无限循环

19.range语句

语法1

num = range(num)  # 表示生成一个从0-num(不包含num)的一个序列(数组)

# 例如
num = range(5)  # 生成一个从0开始到4结束的一个序列(数组)

语法2

num = range(num1, num2)  # 表示生成一个从num1-num2(不包含num2)的一个序列(数组)
# range() 示例2
num = range(1, 6)  # 生成一个从1开始到6(不包含)结束的序列
for x in num:
    print(x)

语法3

num = range(num1, num2, step)  # 表示生成一个从num1-num2(不包含num2)step(步进)为2(每一个数字相隔2)的一个序列(数组)
# range() 示例3
num = range(1, 6, 2)  # 生成一个从1开始到6(不包含)结束步进为2(每一个元素加2直到6为止但不包含6)的序列
for x in num:
    print(x)

20.for中临时变量的作用域

for 和range() 练习

# range()练习 求1-100 中有多少个偶数
count = 0
for x in range(1, 101):
    if x % 2 == 0:
        count += 1
print("1-100 中一共有%d个偶数" % count)

# 取出最后一次循环的内容
i = "a"  # 不管这里的内容是什么都不会影响for循环中的i 只会把最后一次i的内容赋值给外部的i
for i in range(5):
    print(i)
    if i == 3:
        break

print(i)

21.嵌套for循环

for循环制作九九乘法表练习

# for循环制作九九乘法表练习
for i in range(1, 10):
    for j in range(1, i+1):
        print(f"{j}*{i}={j*i}\t", end=" ")
    print()

22. continue和break

continue表示中断本次循环进入下一次循环

break表示结束整个循环体

示例

# continue示例
for i in range(5):
    if i == 3:  # 当i等于3时退出本次循环进入下一个循环
        continue
    print(i)
# for循环发放工资练习
total = 10000
for i in range(1, 21):
    performance = random.randint(1, 5)

    if performance < 5:
        print(f"员工{i}, 绩效分{performance}, 低于5, 不发工资下一位")
        if i == 20:
            print("所有员工工资已按要求发放完毕!")
        continue

    if total >= 1000:
        total -= 1000
        print(f"向员工{i}发放工资1000元,账户余额{total}")
        if i == 20:
            print("所有员工工资已按要求发放完毕!")
    else:
        print("工资发完了,下个月领取吧!")
        break

23.函数

函数的概念:是组织好的,可重复使用的,用来实现特定功能的代码段

使用函数的好处

1)将功能封装在函数内,可供随时随地的重复利用

2)提高代码的复用性,减少重复代码,提高开发效率

Python中函数的定义方式

# 函数前后都要加两个空行


def 函数名(传入参数):
	函数体
	return 返回值

示例:

# 自定义长度计算函数(注意函数定义前后要添加两行空行以符合Python规范)


def my_len(iterable):
    count = 0
    for item in iterable:
        count += 1
    return count


my_str = "123456"
print(my_len(my_str))

补充:

1)函数要先定义,后调用

2)参数和返回值都可以省略

24.函数传参

语法



def fun(参数1, 参数2, 参数3...):
	函数体
	return 返回值
	
	

示例1

# 传参函数练习 两数相加函数


def add(number1, number2):  # number1和number2称为形参,表示函数将要使用两个数字参数
    return number1 + number2  # 使用传过来的参数进行计算并将结果返回


print(f"两数相加结果为:{add(int(input('请输入第一个数字:')), int(input('请输入第二个数字:')))}")  # 输入的参数称为实际参数会在函数中进行实际的计算并返回相应的值

示例2

# 函数传参练习-测量体温


def test_temper_ture(temperature):
    print("请出示健康码并测量体温")
    if temperature > 37.5:
        print(f"您的体温是{temperature}请隔离")
    else:
        print(f"您的体温是{temperature}请进")


test_temper_ture(38.3)
test_temper_ture(36.3)

补充:参数可以是0个也可以是任意个

25.函数的返回值

语法



def 函数(参数...)
	函数体
	return 返回值


变量 = 函数(参数)  # 用一个变量接收函数返回的结果

示例

# 自定义长度计算函数(注意函数定义前后要添加两行空行以符合Python规范)


def my_len(iterable):
    count = 0
    for item in iterable:
        count += 1
    return count  # 返回结果 函数结束


my_str = "123456"
print(my_len(my_str))

26.None类型

函数如果不写返回值,默认返回一个None (空值)

示例



def say_hello():
    print("Hello")


def_type = say_hello()  # 接收函数的默认返回值None
print(def_type, type(def_type))  # 打印函数返回值和返回值类型,返回值类型是NoneType

也可以主动返回一个空值

示例



def say_hello():
    print("Hello")
	return None


None类型的作用

1)用在函数无返回值上

2)用在if判断上

  • 在if判断中,None等同于False

  • 一般用于在函数中主动返回None,配合if判断做相关的处理

    # None返回值的具体应用
    
    
    def check_age(age):
        if age >= 18:
            return "success"
        else:
            return None  # 在if中等于False
    
    
    if check_age(18):  # 得到返回值None为False,其他类型为True
        print("您已成年可以进行下一步了")
    else:
        print("您是未成年请止步")
    

    补充 :数字 0 在if中也是False

3)用于声明无内容的变量上

  • 定义变量,但展示不需要变量有具体值,可以用None来代替
name = None  # None用于声明无初始内容的变量

27.函数的说明文档(多行注释)

示例



def add(number1, number2):
    """
    add函数可以给把两个参数相加并返回结果
    :param number1: 传入第一个数字参数用于计算
    :param number2: 传入第二个数字参数用于计算
    :return: 返回两个参数相加的结果
    """
    return number1 + number2

28.函数的嵌套使用

示例

# 函数的嵌套使用


def fun1():
    print("我是fun1")


def fun2():
    print("我是fun2")
    print("fun1请你出来")
    fun1()


fun2()

29.局部变量、全局变量、作用域

概念:变量的作用域值得是变量的作用范围(在哪里可用, 在哪不可用)

主要分为两类:局部变量和全局变量

局部变量:

1)定义在函数体内的变量,只在函数体内生效,函数体外不可用

2)在函数体内部, 临时保存数据, 即当函数调用完成后,则销毁局部变量

全局变量:

1)定义在函数体外的变量,整个类文件都可使用

示例

# 变量的作用域
num = None


def fun1():
    my_num = 0  # 在函数体内部, 临时保存数据,当函数调用完成后,销毁局部变量
    print(my_num)  # 局部变量函数体外不可访问
    print(num)  # 全局变量在函数体内和外部都可以访问


fun1()
print(num)

global关键字作用:用于声明全局变量使得在函数体内也可以定义全局变量

示例

# global 函数体外全局变量 声明在函数体内使用外部某个全局变量
num = None


def fun():
    global num  # 声明使用函数体外部的全局变量num
    num = 0  # 改变全局变量num的值


fun()  # 调用函数修改全局变量
print(num)  # 全局变量num的值被修改了

#=====================================================
# 错误示例:按照规范不建议直接在函数体内直接定义全局变量
def fun():
    global num  # 这里是直接定义全局变量,在Python中并不推荐这样写
    num = 0  # 改变全局变量num的值


fun()  # 调用函数修改全局变量
print(num)  # 全局变量num的值被修改了

综合练习 ATM取款机

money = 5000000
name = None


def balance_inquiry():
    """
    balance_inquiry  余额查询函数
    :return: 返回账户余额
    """
    return money


def deposit(deposit_amount):
    """
    deposit  存款函数
    :param deposit_amount: 要存入的金额
    :return: True或False 存款成功或失败
    """
    global money
    money += deposit_amount
    return True


def withdrawal(withdrawal_amount):
    """
    withdrawal  取款函数
    :param withdrawal_amount: 要取出的金额
    :return: True或False 取款成功或失败
    """
    global money
    if withdrawal_amount > money:
        return False
    else:
        money -= withdrawal_amount
        return True


def main():
    """
    main 主菜单函数
    :return: None
    """

    global name
    global money
    print("欢迎使用ATM机")
    name = input("请输入您的姓名:")
    flag = True
    while flag:
        print(f"{name}您好, 请输入要进行的操作")
        print("1 查询余额")
        print("2 存款")
        print("3 取款")
        print("4 退出系统")
        operation = int(input())
        if operation == 1:
            balance = balance_inquiry()
            print(f"您的余额为{balance}")
            continue
        elif operation == 2:
            deposit_amount = int(input("请输入您要存入的金额"))
            if not deposit(deposit_amount):
                print("存入失败")
            else:
                print(f"成功存入{deposit_amount}元, 您的余额为{money}元")
            continue
        elif operation == 3:
            withdrawal_amount = int(input("请输入您要取出的金额"))
            if not withdrawal(withdrawal_amount):
                print("取出失败,余额不足")
            else:
                print(f"成功取出{withdrawal_amount}元, 余额为{money}元")
            continue
        elif operation == 4:
            print("程序退出,欢迎下次使用")
            main()
            break
        else:
            print("您的输入有误请重新输入")
            continue


main()

# 取款机写法二
money = 5000000
name = None


def balance_inquiry():
    """
    balance_inquiry  余额查询函数
    :return: int
    """
    return money


def deposit(deposit_amount):
    """
    deposit  存款函数
    :param deposit_amount: 要存入的金额
    :return: None
    """
    global money
    money += deposit_amount
    print(f"成功存入{deposit_amount}元, 您的余额为{balance_inquiry()}元")
    print("========================================================")


def withdrawal(withdrawal_amount):
    """
    withdrawal  取款函数
    :param withdrawal_amount: 要取出的金额
    :return: None
    """
    global money
    if withdrawal_amount > money:
        print("取出失败,余额不足")
        print("========================================================")
    else:
        money -= withdrawal_amount
        print(f"成功取出{withdrawal_amount}元, 余额为{balance_inquiry()}元")
        print("========================================================")


def main():
    """
    main 主菜单函数
    :return: None
    """

    global name
    global money
    print("欢迎使用ATM机")
    name = input("请输入您的姓名:")
    flag = True
    while flag:
        print(f"{name}您好, 请输入要进行的操作")
        print("1 查询余额")
        print("2 存款")
        print("3 取款")
        print("4 退出系统")
        operation = int(input())
        if operation == 1:
            print(f"您的余额为{balance_inquiry()}")
            print("========================================================")
            continue
        elif operation == 2:
            deposit(int(input("请输入您要存入的金额")))
            continue
        elif operation == 3:
            withdrawal(int(input("请输入您要取出的金额")))
            continue
        elif operation == 4:
            print("程序退出,欢迎下次使用")
            print("========================================================")
            break
        else:
            print("您的输入有误请重新输入")
            print("========================================================")
            continue


main()

30.数据容器

概念:一种可以存储多个元素的Python数据类型

Python中各种类型的数据容器

list(列表)
tuple(元组)
str(字符串)
set(集合)
dict(字典)

31.list列表

语法

# 字面量
[元素1, 元素2, 元素3...]

# 定义list类型变量
变量名称 = [元素1, 元素2, 元素3...]

# 定义空列表
变量名称 = []
变量名称 = list()

列表中的每一个数据称之为元素

  • 以[ ] 作为标识

  • 列表内每一个元素之间用逗号隔开

示例

# list列表定义方式一
array = [True, 2, "A", 4.22, None, [1, "B"]]  # list里面可以存储各种类型的数据
for item in array:  # 遍历array中的所有数据
    print(item)


print(type(array))  # 查看数据类型 为list类型

#list列表定义方式二 定义空列表
array_none = []  # 定义了一个空的list列表
array_none = list()  # 定义了一个空的list列表

列表函数补充sort方法

语法

list.sort(key=选择排序依据的函数, reverse=True)

示例

my_list = [["a", 1], ["b", 2], ["c", 3]]
# key参数=一个函数,表示要排序的规则,并且会把my_list的每一个元素作为参数都传进去计算
# key=lambda element: element[1]   表示按照每一个元素的下标 1 的元素进行大小的排序
# 并且每一个元素都得是一样的数据,不然取不到下标报错
my_list.sort(key=lambda element: element[1], reverse=True)  # 默认是False从小到大排序,对原序列进行操作
print(my_list)

32.列表的下标索引

概念:每一个列表元素都有一个下标,下标从0开始

下标的作用: 可以根据下标取出列表中的指定元素

示例

# 列表下标的使用
# 下标按从左往右从0开始,倒序从-1开始 
array = [True, 2, "A", 4.22, None, [1, "B"]]
num = array[1]  # 将下标为1的元素(也就是列表中元素2)存入num变量中
array2 = array[-1]  # 将下标为-1的元素(也就是倒数第一个[1,"B"])存入array2变量中
str1 = array[-1][0]  # 取出嵌套数组中的数据
print(num)
print(array2)
print(str1)

注意:通过下标索引取数据时不要超出数组长度,否则会报一个数组越界

33.列表的下标的查询方法index

方法的概念

  • 在Python中,如果将函数定义为class(类)的成员,那么函数将会称为方法

列表的查询方法-语法

# list列表查询一个指定的元素的下标的方法index
list.index(元素)  # 取得这个元素在list列表中的下标

示例

# list列表的index方法取出指定元素的下标
array = ["a", 2, [1, "b"], True, None]
num = array[array.index(True)]  # 先用index方法取出True元素在array列表中的下标,然后根据下标取出这个数据
print(num)
print(f"True在array列表中的下标是{array.index(True)}")

注意:如果index查找的元素不存会出现异常

34.修改指定下标在列表中的内容

示例1

# 修改列表中指定下标对应的元素
array = ["a", 2, [1, "b"], True, None]
array[2] = "c"  # 把array列表中下标为2的元素修改为字符串"c"
array[array.index(True)] = "Q"  # 先获取array列表中的元素True的下标,然后把这个下标对应的元素修改为字符串“Q”
for item in array:  # 遍历array列表 输出a 2 c Q None
    print(item)

35.列表的插入元素的方法insert

语法

list.insert(下标, 元素)  # 在指定的下标插入指定的元素,原本在这个下标的元素和后面的元素下标都向后移动一位

示例

# list列表的insert方法,向列表中的指定下标插入一个元素
array = ["a", 2, [1, "b"], True, None]
array.insert(0, 1)  # 在下标为0的位置插入一个元素 1 原本在这个下标位置的元素"a"向后移动一位变为1 后面的元素也是一样往后移动一位
print(array)

36.向列表后面添加元素的方法append和extend

append语法

list.append(元素)  # 向列表的后面添加一个元素

示例

# list列表的append方法,向列表的末尾添加一个元素
array = ["a", 2, [1, "b"], True, None]
print(array)
array.append("TT")  # 向array列表的末尾添加一个元素"TT"
print(array)

extend语法

list.extend(其他数据容器)  # 向列表的后面添加一批元素

示例

# list列表的append方法,向列表的末尾添加一个元素
array = ["a", 2, [1, "b"], True, None]
array.extend(["w", 5, 9])  # 向array列表的末尾添加一批元素,每个元素按顺序依次添加到array列表的末尾
print(array)

37.列表删除元素del关键字

del 关键字的语法

del list[下标]  # 删除指定下标对应的元素
lst = ['apple', 'banana', 'cherry', 'date']
del lst[1]  # 删除索引为1的元素,即'banana'
print(lst)  # 输出:['apple', 'cherry', 'date']

del lst[0:2]  # 删除索引从0到2(不包括2)的元素,即['apple', 'cherry']
print(lst)  # 输出:['date']

示例

# 使用del关键字删除列表中指定下标对应的元素
array = ["a", 2, [1, "b"], True, None]
del array[3]  # 删除array列表中下标为3的元素,后面的元素下标依次减一
print(array)

38.方法pop提取列表中的元素并返回

列表的pop方法的语法

list.pop(下标)
lst = ['apple', 'banana', 'cherry', 'date']
removed_element = lst.pop(1)  # 删除索引为1的元素,即'banana'
print(lst)  # 输出:['apple', 'cherry', 'date']
print(removed_element)  # 输出:'banana'

last_element = lst.pop()  # 删除并返回最后一个元素,即'date'
print(lst)  # 输出:['apple', 'cherry']
print(last_element)  # 输出:'date'

示例

# 使用列表的pop方法移除列表中指定下标对应的元素,并将这个元素返回
array = ["a", 2, [1, "b"], True, None]
flag = array.pop(3)  # 移除(提取)array列表中下标为3的元素,并将这个元素返回,后面的元素下标依次减一
print(array)  # 查看当前的array列表,发现True已经被移除了
print(flag)  # 输入pop方法返回的元素的值

39.删除列表中指定的元素remove方法

语法

list.remove(元素)  # 删除列表中的指定元素

示例

# 使用list列表的remove方法移除指定的元素
array = ["a", "b", [1, "b"], True, "b"]
array.remove("b")  # 移除array列表中第一个元素为b的元素
print(array)

40.清空list列表的方法clear

语法

list.claer()  # 清空list中的所有元素

示例

# 使用list的clear方法清空列表中的所有元素
array = ["a", "b", [1, "b"], True, "b"]
print(array)
array.clear()  # 清空列表中所有元素,使之成为空列表
print(array)

41.统计某个元素在列表中的数量count方法

语法

list.count(X)  # 统计元素X在list列表中的个数,并返回

示例

# 使用list列表的count方法统计某个元素在列表中的个数并返回
array = ["a", "b", [1, "b"], True, "b"]
num = array.count("b")  # 统计元素b 在array中的个数并返回
print(f"元素b在array列表中的个数为{num}个")

42.使用内置函数len()统计列表中元素的个数

语法

len(list)  # 返回list列表中元素的个数

示例

# 使用len()函数统计列表的元素个数
array = ["a", "b", [1, "b"], True, "b"]
count = len(array)  # 统计array列表中的元素个数并返回
print(count)

43.列表的总结

  • 可以容纳多个元素(上限为2 ** 63-1、9223372036854775807个)
  • 可以容纳不同类型的元素
  • 数据是有序存储的(有下标序号)
  • 允许重复数据存在
  • 可以修改(增加删除修改元素等)

列表的操作练习

# list列表的操作练习
array = [21, 25, 21, 23, 22, 20]
print(array)
# 使用append向array尾部追加一个数字31
array.append(31)
# 使用extend向array尾部添加一批数据[29, 33, 30]
array.extend([29, 33, 30])
# 取出array列表中的第一个元素
print(f"array列表的第一个元素是{array[0]}")
# 取出array列表的最后的最后一个元素
print(f"array列表的最后一个元素是{array[-1]}")
# 使用index方法返回元素31在array列表中的下标位置
index_31 = array.index(31)
print("元素31在array列表中的下标位置为%d" % index_31)
print(array)

44.list列表的循环遍历

示例

# 使用while和for循环遍历list列表中的每一个元素
array = [1, "d", 3, "j", 6]

# 使用while循环遍历array列表中的每个元素
index = 0
while index < len(array):
    print(array[index])  # 循环取出array列表中的每一个元素
    index += 1

print("===========分割线===============")

# 使用for循环遍历array列表中的每个元素
for item in array:
    print(item)  # 循环取出array列表中的每一个元素

练习

# 遍历list列表练习
array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
new_array1 = list()
new_array2 = []

# 取出列表内的偶数,并存入一个新的列表对象中
index = 0
while index < len(array):
    if array[index] % 2 == 0:
        new_array1.append(array[index])
    index += 1
print(new_array1)

# for循环写法
for item in array:
    if item % 2 == 0:
        new_array2.append(item)

print(new_array2)

45.元组tuple

概念:

  • 元组tuple和列表list的相同点在于可以封装多个不同类型的元素
  • 元组tuple和列表list的不同点在于元组一旦定义完成,将不可修改,并且使用小括号定义
  • 元组里面的元素不可修改,但是可以给整个元组重新赋值

语法

# 元组的定于:定于元组使用( )小括号,多个元素使用逗号隔开,元素可以是不同的数据类型
# 定义字面量
(元素1, 元素2, 元素3...)
# 定义元组变量
变量名称 = (元素1, 元素2, 元素3...)
# 定义空元组
变量名称 = ()  # 方式一
变量名称 = tuple()  # 方式二

示例

# 元组定义单个元素要在后面加上, 否则就不是元组类型
# 不加逗号示例
t1 = (1)  # 不加逗号结果,元素的是什么类型就是什么类型 1 是int型t1的类型就是int型
print(type(t1))  # 打印结果为<class 'int'>
# 正确元组定义单个元素示例
t2 = (1, )  # 只有一个元素后面加上逗号,t2就是元组类型了
print(type(t2))  # 打印结果为<class 'tuple'>
# 元组存入字符串、整型、浮点型、None、布尔型、列表、嵌套元组
t3 = ("a", 1, 2.1, None, True, [1, "b"], ("r", ))  # 元组和列表一样都可以存入不同类型的数据

取出元组中的元素和列表一样

t3 = ("a", 1, 2.1, None, True, [1, "b"], ("r", ))
element = t3[-2][1]  # 取出元组倒数第二个元素列表的第二个元素“b”
print(element)

46.元组的操作

由于元组不可修改所以它的常用方法只有3个

  • tuple.index(元素) 查找某个元素在元组中的下标位置
  • tuple.count(元素) 统计某个元素在元组中出现的次数
  • len(元组) 统计元组中元素的个数

示例

# tuple元组的方法练习
t3 = ("a", 1, 1, 2.2, None, True, [2, "b"], ("r", ))
element = t3[-2][1]  # 取出元组倒数第二个元素列表的第二个元素“b”
print(element)

# 元组中index方法练习
index = t3.index("a")  # 查找元素a 在元组中的下标位置
print("第一个a 元素在元组中的下标位置为%d" % index)

# 元组中count方法练习
count_1 = t3.count(1)  # 统计元素1 在元组中出现的次数  注意True会被认为是数字1 False会被认为是数字0
print("元素1 在元组中出现的次数为%d" % count_1)  # True会被认为是数字1会被统计

# 使用len()得到元组的长度
t3_len = len(t3)  # 使用len方法得到t3元组的长度(元素个数)
print(t3_len)  # 输入元组长度为8

遍历元组

# 遍历元组
t3 = ("a", 1, 1, 2.2, None, True, [2, "b"], ("r", ))

# 使用while循环遍历元组
index = 0
while index < len(t3):
    item = t3[index]
    print(item)
    index += 1

print("========================================[")

# 使用for循环遍历元组
for item in t3:
    print(item)

修改元组里面的列表的元素

# 元组里面是元素不能修改,但是元组里面的列表的元素是可以修改的
t3 = ("a", 1, 1, 2.2, None, True, [2, "b"], ("r", ))
t3[-2][0] = "我原本是数字2,我被修改了"  # 修改元组里面的列表的元素-数字2
print(t3[-2])  # 查看元组里面的列表的所有元素
print(t3)  # 查看元组中的所有元素

# 元组的单个元素不能改变但是可以重新赋值
student = ('Tom', 16, ['football', 'rap'])
print(student)
student = ("jack", )
print(student)

元组练习

# tuple元组练习
# 定义一个元组, 记录学生信息(姓名、年龄、多个爱好)
student = ('Tom', 16, ['football', 'rap'])
# 查询年龄所在的下标位置
index = student.index(16)  # 得到年龄在元组中的下标位置
print(index)
# 查询学生姓名
print(student[0])  # 使用下标获得学生姓名
# 删除学生爱好football
# del student[2][0]  # 使用del关键字删除列表中的爱好football
# student[2].pop(0)  # 删除列表中下标为0的元素并返回
student[2].remove("football")  # 删除列表中第一个出现的football元素
print(student)
# 增加爱好coding到list内
student[2].insert(0, "coding")  # 使用insert在列表指定下标位置插入指定元素
# student[2].append("coding")  # 使用列表的append方法在列表末尾添加一个元素coding
print(student)

47.字符串的定义

概念:

  • 字符串也容器的一种,用于存放字符的容器
  • 字符串中的每一个字符就是一个元素
  • 空格也算是一个字符,也会占用一个元素下标
  • 字符串是也是也不可修改的容器
  • 和元组一样一旦定义不可改变字符串的元素

字符串的定义

my_str = "Test Python th"

print(len(my_str))
for item in my_str:
    print(item)  # 空格也算一个字符也会占用一个下标位置
    
# 使用index方法查找某个字符或字符串在字符串中的起始下标位置
index = my_str.index("Py")
print(index)  # P的下标为5

# 使用count方法统计某个字符或字符串在字符串中的个数
count_th = my_str.count("th")
print(count_th)  # th在字符串中出现的次数为2所有,输出2

特点

  • 只能存储字符串
  • 长度任意(取决于内存大小)
  • 支持下标索引
  • 允许重复字符串存在
  • 不可修改
  • 支持for循环

48.字符串的replace方法

语法

# 使用字符串的replace方法替换某一段字符并返回一个新的字符串
变量名 = 字符串.replace(目标字符串,要替换的字符串)

示例

# 使用字符串的replace方法替换某一段字符并返回一个新的字符串
my_str1 = "Test Python Test"
my_str2 = my_str1.replace("Test", "Java")  # 将字符串1中的所有Test 替换为Java并返回一个新的字符串,原字符串不受影响
print(my_str1)  # 原字符串不受影响,也无法改变
print(my_str2)  # 替换后的新字符串

注意: 不是修改字符串本身,而是得到一个新的字符串

49.字符串的split方法

语法

array = string.split(单个字符)  # 根据这个字符对整个字符串进行分割并保存到一个列表中

示例

# 使用字符串的split方法对字符串进行分割并保存到一个列表中
my_str1 = "Test Python Test"
array = my_str1.split(" ")  # 对字符串按照空格进行分割并将每一个分割的元素存入一个列表中
print(array)

注意: 不是分割字符串本身,而是得到一个新的列表对象

50.字符串的strip方法

语法

string2 = string1.strip()  # 取出字符串的前后空格并返回一个新的字符串

示例

# 使用字符串的strip方法去除前后空格并返回新的字符串
my_str1 = "  452 Test Python Test 256   "
my_str2 = my_str1.strip()  # 去除str1的前后空格并返回一个新的字符串,原字符串不受影响
print(my_str1)
print(my_str2)


# 使用字符串的strip方法去除前后指定出现的字符串
my_str3 = "542 Test Python Test 2565"
my_str4 = my_str3.strip("45")  # 取出前后出现的4或5两个字符串不分顺序
print(my_str4)

补充字符串的 join()方法

list_0 = ['a', 'b', 'c']
# 可以用字符串的join() 方法传入一个列表
# 将所有的元素连成一个字符串
new_str = ''.join(list_0)

51.数据容器的序列

序列的概念:

  • 序列是指内容连续、有序、可使用下标索引的一类数据容器
  • 例如:list列表、tuple元组、string字符串都属于序列

序列的切片:

  • 从一个序列中取出一个子序列,原序列是什么类型,返回的序列就是什么类型

语法

# 从指定的下标位置开始,依次取出元素,到指定的下标位置结束 返回一个新的序列
序列[start起始下标:end结束下标:step步长] # 返回一个新的序列

补充

  • 起始下标可以留空,视为从0开始
  • 结束下标也可以留空,视为取到最后一个元素
  • 取出的数据不包含结束下标的元素
  • 步长表示切取元素的间隔,步长为1可以省略,负数表示反向切取

示例

# 序列切片操作
my_str = "Testit Python_it Testit th_it"
# 对字符串进行切片获得一个新的字符串
my_str2 = my_str[my_str.index("Python"):my_str.index("_it"):1]  # 使用index方法获得起始下标和结束下标,结束下标并不会截取到
print(my_str2)

array1 = [1, 2, 3, 4, 5]
# 取出2、3、4存入一个新列表中
array2 = array1[array1.index(2):array1.index(4)+1:1]  # 使用index方法获取2的下标,由于切片不包含结束下标所以要把元素4的下标+1
print(array2)

练习

# 切片练习
my_str = "Testit nohtyP_it Testit th_it"

# 取出字符串中的Python
my_str2 = my_str[::-1]
my_str3 = my_str2[my_str2.index("Python"):my_str2.index("Python")+7]
print(my_str2)
print(my_str3)

52.set集合

概念

  • set集合一个无序的数据容器,并且元素不允许重复
  • 可以修改set集合的元素
  • 可以存入不同类型的数据
  • 支持for循环

语法

set = {元素1,元素2,元素3...}

set集合的add方法

# 语法:set.add(元素)
# 将一个指定的元素添加到集合中,如果这个元素重复了会被去重
my_set = {"a", "b", "c", "b", "c"}  # 如果set集合中有重复的元素那么他会自动去重
my_set.add("e")  # 向set集合中添加一个元素 e
print(my_set)

set集合的remove方法

# 语法:set.remove(要移除的元素)
# 将set集合中指定的元素移除
my_set = {"a", "b", "c", "b", "c"}  # 如果set集合中有重复的元素那么他会自动去重
my_set.remove("c")  # 将set集合中指定的元素移除
print(my_set)

set集合的pop方法

# 语法:element = set.pop() 
# 随机将set集合中的一个元素移除并返回
my_set = {"a", "b", "c", "b", "c"}  # 如果set集合中有重复的元素那么他会自动去重
element = my_set.pop()  # 随机将set集合中的一个元素移除并返回
print(element)
print(my_set)

set集合的clear方法

# 语法: set.clear()
my_set = {"a", "b", "c", "b", "c"}
my_set.clear()  # 清空set集合
print(my_set)

set集合的difference方法

# 语法: set3 = set1.difference(set2)
# 将set1中有而set2中没有的元素提取出来并存入一个新的集合,原集合不会改变
my_set1 = {"a", "b", "c", "d"}
my_set2 = {"c", "d", "e", "f"}
my_set3 = my_set1.difference(my_set2)  # 将set1中有而set2中没有的元素提取出来并存入一个新的集合,原集合不会改变
print(my_set3)

set集合的difference_update方法

# 语法:set1.difference_update(set2)
# 将set1和set2中都有元素的删除,集合1发生改变
my_set1 = {"a", "b", "c", "d"}
my_set2 = {"c", "d", "e", "f"}
my_set1.difference_update(my_set2)  # 将set1和set2中都有元素的删除,保留不同的元素,集合1发生改变,集合2不变
print(my_set1)

set集合的union方法

# 语法: set3 = set1.union(set2)
# 将集合1和集合2中的元素都集合到一起返回一个新的集合,set会进行去重操作
my_set1 = {"a", "b", "c", "d"}
my_set2 = {"c", "d", "e", "f"}
my_set3 = my_set1.union(my_set2)  # 将集合1和集合2中的元素都集合到一起返回一个新的集合,set会进行去重操作

练习

# 练习 将列表中的数据存入set集合中,得到一个数据不重复的集合
array = ["a", "b", "c", "d", "d", "a", "f"]
my_set = set()
for item in array:
    my_set.add(item)

print(array)  # 原本的数据
print(my_set)  # 得到一个元素不重复的集合

53.dict字典

概念:

  • 存储键值对的集合
  • key不可以是list、set、dict类型
  • value可以是任意类型
  • 可以修改
  • key不可重复

语法

# 定义dict字典类型的字面量
{key:value, key:value, key:value...}

# 定义dict字典变量
my_dict = {key:value, key:value, key:value...}

# 定义空dict类型
my_dict1 = {}
my_dict2 = dict{}

示例

# 定义一个dict类型的变量
my_dict = {"a": 1, "b": 2, "c": 3}

# 定义一个空的dict类型变量
my_dict1 = dict()

# 定义一个dict类型的变量(嵌套)
my_dict = {
    "a": {
        "英语": 67,
        "语文": 32,
        "数学": 53
    },
    "b": {
        "英语": 37,
        "语文": 35,
        "数学": 56
    },
    "c": {
        "英语": 78,
        "语文": 96,
        "数学": 83
    }
}
# 根据dict字典类型的key取出value
print(my_dict["b"])  # 取出字典中a对应的value值,如果key不存在会报错

# 使用for循环取出dict类型的key和对应的value
for name in my_dict:
    value = my_dict[name]
    print("学生姓名:%s" % name)
    for subject in value:
        score = value[subject]
        print(f"科目:{subject} 分数:{score}")

54.dict类型的操作和方法

向dict新增数据

my_dict = {"a": 1, "b": 2, "c": [3]}
my_dict["d"] = 4  # 如果key存在将会覆盖原本的value值
# 使用append方法给key更新Value
# 如果key不存在将会报错
# my_dict["c"] 返回一个列表(因为key的原Value是一个列表)然后给这个列表追加元素
my_dict["c"].append(4)  

修改dict里的数据

my_dict = {"a": 1, "b": 2, "c": 3}
my_dict["c"] = 4  # 根据key给value重新赋值,和新增数据写法一样

使用pop方法移除并返回dict里的键值对

my_dict = {"a": 1, "b": 2, "c": 3}
value = my_dict.pop("c")  # 将dict中key对应的value移除并返回
print(value)
print(my_dict)

使用clear方法清空dict

my_dict = {"a": 1, "b": 2, "c": 3}
my_dict.clear()  # 清空dict
print(my_dict) 

使用keys方法获取dict的所有key存入dict_keys类型中

my_dict = {"a": 1, "b": 2, "c": 3}
keys = my_dict.keys()  # 获取dict的所有key存入一个dict_keys类型中并返回
print(keys)
keys = list(keys)  # 将序列转为list列表类型

使用values方法获取dict的所有value存入dict_values类型中

my_dict = {"a": 1, "b": 2, "c": 3}
values = my_dict.values()  # 获取dict的所有key存入一个dict_keys类型中并返回
print(values)
values = list(values)  # 将序列转为list列表类型

字典的get方法获取字典中键对应的值

dic = {1: 'a', 2: 'c', 3: 'd'}
# 字典的get方法
# 可以传入两个参数
# 第一个意为获取字典中key对应的Value
# 第二个是,如果key不存在则返回的默认值
key = dic.get(5, '默认值')  # 字典中不存在key5 返回默认值
print(key)  # 打印输出内容为 默认值(因为key不存在)

练习

# 练习使用循环和判断修改字典里的嵌套数据
emp = {
    "Tom": {
        "部门": "科技部",
        "工资": 3000,
        "级别": 1
    },
    "Jack": {
        "部门": "市场部",
        "工资": 5000,
        "级别": 2
    },
    "lili": {
        "部门": "市场部",
        "工资": 8000,
        "级别": 3
    },
    "mary": {
        "部门": "科技部",
        "工资": 3000,
        "级别": 1
    }
}

# 需求:将级别为1的员工级别加一,并且工资加1000
# 直接判断写法
print(emp)
for name in emp:
    if emp[name]["级别"] == 1:  
        emp[name]["级别"] += 1
        emp[name]["工资"] += 1000
        
print("==========================================")
print(emp)

55.数据容器的总结对比

区别

  • 是否支持下标索引和重复元素
    • 支持: list 列表、tuple元组、string字符串,这一类统称为序列类型
    • 不支持:set集合、dict字典,这一类统称为非序列类型
  • 是否可以修改
    • 支持:list、set、dict
    • 不支持:tuple、string
list列表tuple元组string字符串set集合dict字典
元素数量支持多个支持多个支持多个支持多个支持多个
元素类型任意任意仅字符任意key:value
下标索引支持支持支持不支持不支持
重复元素支持支持支持不支持不支持
可修改性支持不支持不支持支持支持
数据是否有序
使用场景可修改,可重复的一批数据记录场景不可修改、可重复的一批数据记录场景一串字符的记录场景不可重复的数据记录场景以key检索Value的数据记录场景

56.数据容器的通用函数

len函数

num = len(数据容器)  # 统计数据容器的长度(元素个数)并返回一个int值

max函数

my_max = max(数据容器)  # 返回数据容器中最大元素(字母会转为ASCLL码再进行比较)如果数据容器是字典类型那么会返回Value值最大的key

min函数

my_min = min(数据容器)  # 返回数据容器中最小元素(字母会转为ASCLL码再进行比较)

sorted函数

# 将容器中的元素从小到大排序,并返回一个新的容器
new_list = sorted(容器)  # 按从小到大排序并返回一个列表对象,原容器不变

# 将容器中的元素从大到小排序,并返回一个新的容器
new_list = sorted(容器, reverse=True)  # 按从大到小排序并返回一个列表对象,原容器不变

数据容器之间的转换

list(容器)  # 将其他数据容器传换位list列表类型的容器,并返回一个新的容器,原容器不变
str(容器)  # 将其他数据容器传换位str字符串类型的容器,并返回一个新的容器,原容器不变
tuple(容器)  # 将其他数据容器传换位tuple元组类型的容器,并返回一个新的容器,原容器不变
set(容器)  # 将其他数据容器传换位set集合类型的容器,并返回一个新的容器,原容器不变

# 补充1:如果是dict类型转换为其他类型那么只会把key存为新的元素
# 补充1:如果是dict类型转为str字符串类型那么key和Value都会存为新元素

57.函数的多返回值

语法

def 函数名():
	return 返回值1, 返回值2  # 多个返回值用逗号隔开
变量1, 变量2 = 函数名()  # 使用多个变量接收多个返回值,变量用逗号隔开

示例

# 函数的多个返回值


def fun():
    """
    fun练习函数
    :return: 返回两个数据
    """
    return 1, 0  # 返回两个数据


bool1, bool2 = fun()  # 使用两个变量接收两个返回值
print(bool1)
print(bool2)

58.函数的多种传参

位置参数

def fun(name, age, score):
    return 0


# 位置参数
fun("Tom", 18, 89)  # 按照形参的顺序传入参数

关键字参数

def fun(name, age, score):
    return 0


# 关键字参数
fun(age=16, name="Jack", score=39)  # 使用键值对的方式传入参数,传参顺序没有要求

fun("Jack", score=39, name="jack")  # 使用位置传参和键值对传参的方式传入参数,位置参数只能写在前面

缺省参数(参数默认值)

# 缺省参数(设置参数默认值)


def fun(name, age, score=60):  # 设置参数score的默认值为60,设置默认值的参数必须放在后面
    print(name, age, score)
    return 0


fun("Tom", 17)  # 由于函数fun已经给score设置了默认值,传参的时候可以省略

不定长参数(不限制参数个数)

  • 位置传递

    语法

    def fun(*args):  # 使用不定长参数,不限制传入的参数个数
    	print(args)  # args会接收到一个元组类型
    
        
    fun("Tom", 67, 89)  # 传入三个参数
    fun("Tom")  # 传入一个参数 
    
  • 关键字传递

    语法

    def fun(**kwargs):  # 使用不定长参数,不限制传入参数的个数,并且传参的时候要使用键值对的方式传参
        print(kwargs)  # kwargs会接收到一个dict类型的数据容器
    
    
    fun(姓名="Tom", 年龄=17, 分数=98)  # 使用键值对的方式传入N个参数,key不能加分号,并且只能是字符,变量名称规则
    

59.把函数作为另一个函数的参数

示例

# 将函数作为参数传参


def fun(my_fun):  # 传入一个函数my_fun作为参数
    """
    将函数my_fun返回的值加10并返回
    :param my_fun:  传入一个my_fun函数
    :return:
    """
    # result = my_fun(1, 5, 6)  # 可以在函数内给传入的函数传参
    result = my_fun  # 也可以在外部调用fun函数的时候给my_fun传参
    return result + 10  # 返回的值加10


def my_fun(*args):
    """
    计算多个数字的和
    :param args: 传入N个数字参数
    :return: 返回传入参数的和
    """
    res = 0
    for item in args:
        res += item

    return res


print(fun(my_fun(1, 5, 5)))

60.lambda表达式(一次函数)

语法

# lambda一般作为函数的参数使用
lambda 参数1, 参数2: 执行内容  # 如果执行内容有返回值那么就会把这个值返回

示例

def fun(my_fun):  # 传入一个my_fun函数或者是匿名函数
    num = my_fun(1, 2)  # 传入参数
    print(type(num))
    print(f"{num}fun")


fun(lambda *agrs: list(agrs))  # 如果匿名函数执行内容有返回值那么就会把这个返回值返回
fun(lambda x, y: print(x + y))  # 没有执行内容没有返回值,那么就返回一个None

注意事项:

  • 匿名函数只能临时构建一个函数,使用一次
  • 函数体只能写一行代码

61.文件编码

  • 什么是编码?

    编码就是一种规则集合,记录了内容和二进制间进行相互转换的逻辑。编码有许多,我们常用的是UTF-8

  • 为什么需要使用编码

    计算机只认识0和1,所有需要将内容翻译成0和1才能保存在计算机中,同时也需要编码,将计算机保存的0和1,反向翻译成可以识别的内容

62.文件的读取操作

打开文件语法open函数

# 传入三个参数
# 第一个参数是文件的路径,如果只写文件名表示打开项目文件夹下的文件
# 第二个参数事文件的打开模式,r只读如果文件不存在会报错、w写入、a追加等, 如果文件不存在会创建一个新文件,并且只能传入一个字符
# 第三个参数选择文件的编码格式
file = open(要打开的文件路径,mode打开文件的模式,encoding文件编码格式)  # 返回一个IO操作对象(文件对象)
print(type(file))
# 使用文件对象的close方法关闭文件对象
file.close()

文件对象的read方法

# 语法
# 文件对象.read(num)  # num参数表示要从文件中读取的数据的长度(单位是字节)如果不传入,那么默认读取文件中的所有数据

示例

# 打开一个存在于计算机中的文件
file = open("C:/Users/admin/Desktop/TEST.txt", mode="r", encoding="UTF-8")  # encoding必须要写因为他的位置不在第三个参数
# read(参数)  如果传入参数就会读取固定的字符数量
str_1 = file.read(18)  # 使用文件对象的read方法取出18个字符(回车、空格也算一个字符),并返回一个字符串类型的数据
print(type(str_1))  # read方法返回的类型的是字符串类型
print(str_1)  # 18个字符回车、换行也算一个字符
# read()  不传入参数就会读文件中所有的数据
# 注意:如果调用了两次以上那么第二次读取的内容会接着上一次的内容开始读取
str_2 = file.read()  # 接着上一次读取的末尾开始读取剩下的所有内容,并返回一个字符串
print(str_2) 
# 使用文件对象的close方法关闭文件对象
file.close()

文件对象的readlines方法

# 语法 
# list = 文件对象.readlines()  # 将文件中的每一行提取为一个元素保存到一个列表中并返回这个列表集合

示例readline方法和readlines方法

# 文件的读取操作
file = open("C:/Users/admin/Desktop/TEST.txt", "r", encoding="UTF-8")  # encoding必须要写因为他的位置不在第三个参数
# readline()方法,读取文件中的一行数据
str_3 = file.readline()  # readline读取文件中的一行数据,并返回一个字符串
print(type(str_3))
print(str_3)
# readlines()依次读取文件中的一行数据,并返回一个list列表类型的数据
# 注意:如果调用了两次以上那么第二次读取的内容会接着上一次的内容开始读取
array = file.readlines()  # readlines()依次读取文件中的一行数据,并返回一个list列表类型的数据
print(type(array))
print(array)
# 使用文件对象的close方法关闭文件对象
file.close()

使用for循环读取文件对象中的每一行数据

# 使用for循环读取文件中每一行数据
file = open("C:/Users/admin/Desktop/TEST.txt", "r", encoding="UTF-8")  # encoding必须要写因为他的位置不在第三个参数
array = list()
for line in file:  # 循环读取文件对象中的每一行数据
    array.append(line)  # 将每一行添加到list列表中,和文件对象的readlines方法功能一样

print(array)
# 使用文件对象的close方法关闭文件对象
file.close()

文件对象的关键方法close()

# 每个打开的文件对象都要关闭,不然会占用资源造成系统卡慢
file = open("C:/Users/admin/Desktop/TEST.txt", "r", encoding="UTF-8")  # encoding必须要写因为他的位置不在第三个参数
# 使用文件对象的close方法关闭文件对象
file.close()

使用with open打开文件

# 使用with open打开文件会自动关闭文件,建议使用这种方式打开文件
with open("C:/Users/admin/Desktop/TEST.txt", "r", encoding="UTF-8") as file:  # 打开一个文件存入一个文件对象中
    array = list()
    for line in file:  # 循环读取文件对象中的每一行数据
        array.append(line)  # 将每一行添加到list列表中,和文件对象的readlines方法功能一样

63.文件的写入操作

语法

# 打开一个文件存入一个文件对象
with open("要读的文件地址", "w", encoding="UTF-8") as file:
    file.write("hello world")  # 使用文件对象的write方法写入数据
    file.flush()  # 把文件对象的内容刷新到硬盘中

向文件中写入覆盖数据

# 打开一个文件存入一个文件对象,结尾自动关闭文件
with open("C:/Users/admin/Desktop/TEST1.txt", "w", encoding="UTF-8") as file:
    # 使用文件对象的write方法写入传入的参数覆盖原本文件中存在的内容(mode="w")
    # 如果文件不存就会新建一个文件到这个路径下
    file.write("hello world5")  # 将数据写入内存中
    # 把文件对象的内容刷新到硬盘中,或者当文件对象调用close关闭的时候会把数据写入硬盘
    file.flush()  # 把文件对象的内容刷新到硬盘中

向文件中追加写入数据

with open("C:/Users/admin/Desktop/TEST1.txt", "a", encoding="UTF-8") as file:
    # 使用文件对象的write方法写入传入的参数追加到原文件数据的末尾(mode="a")
    # 如果文件不存就会新建一个文件到这个路径下
    file.write("hello world6")  # 将数据写入内存中
    # 把文件对象的内容刷新到硬盘中,或者当文件对象调用close关闭的时候会把数据写入硬盘
    for item in range(9):
        file.write(f"\nhello world{item}")  # 将数据循环追加写入文件对象中,使用\n换行写入
    file.flush()  # 把文件对象的内容刷新到硬盘中

文件的读写练习

# 文件读取练习
# 读取一个文件中的内容
with open("C:/Users/admin/Desktop/bill.txt", "r", encoding="UTF-8") as file:
    for line in file:
        if line.count("测试") == 0:
            # 打开或创建一个文件,将上一个读取的内容,按要求追加写入
            with open("C:/Users/admin/Desktop/bill.txt.bak", "a", encoding="UTF-8") as file_bak:
                file_bak.write(f"{line}\n")  # mode为a,追加写入每一行数据
                # file_bak.flush()

64.异常

概念:bug表示程序出现错误无法运行

65.捕获异常

语法:

try:
	可能出现异常的代码
except:
	当代码出现异常时执行的代码

捕获一种类型异常示例

# 捕获一种类型异常示例
try:
    # 读取一个不存在的文件时会出现异常
    with open("D:/dea.txt", "r", encoding="UTF-8") as file:
        print(file.read())
except IOError as e:  # 仅捕获文件IO操作异常,并将这个异常存入一个异常对象中
    print("读取文件出现异常了")
    print(e)  # 输出异常信息
    error = str(e)  # 将异常类型转换为字符串
    error_type = str(type(e))  # 将异常类型保存到一个字符串中
    print(error_type)  # 打印异常类型

捕获多中类型的异常示例(except传入多种异常类型)

# 捕获多中类型的异常示例
try:
    1 / 0
    print(name)
# 捕获多种类型的异常
except (NameError, ZeroDivisionError) as e:  # e只能接收到第一次捕获的异常
    print(e)  # 输出异常信息
    error = str(e)  # 将异常类型转换为字符串
    error_type = str(type(e))  # 将异常类型保存到一个字符串中
    print(error_type)  # 打印异常类型

捕获所有类型的异常异常示例(Exception)

# 捕获所有类型的异常异常示例
try:
    1 / 0
    print(name)
# 捕获所有类型的异常
except Exception as e:  # e只能接收到第一次捕获的异常
    print(e)  # 输出异常信息
    error = str(e)  # 将异常类型转换为字符串
    error_type = str(type(e))  # 将异常类型保存到一个字符串中
    print(error_type)  # 打印异常类型

捕获异常的else模块(可选)

# 捕获所有类型的异常异常示例
try:
    array = [1, 2, 3]
    print(array[2])
# 捕获所有类型的异常
except Exception as e:  # e只能接收到第一次捕获的异常
    print(e)  # 输出异常信息
    error = str(e)  # 将异常类型转换为字符串
    error_type = str(type(e))  # 将异常类型保存到一个字符串中
    print(error_type)  # 打印异常类型
else:
    # 当程序没有出现异常时执行的代码(else可写可不写)
    print("程序正常执行了")

捕获异常的finally模块(可选)

# 捕获所有类型的异常异常示例
try:
    file = open("C:/Users/admin/Desktop/TEST.txt", "r", encoding="UTF-8")  # encoding必须要写因为他的位置不在第三个参数

# 捕获所有类型的异常
except Exception as e:  # e只能接收到第一次捕获的异常
    print(e)  # 输出异常信息
    error = str(e)  # 将异常类型转换为字符串
    error_type = str(type(e))  # 将异常类型保存到一个字符串中
    print(error_type)  # 打印异常类型
else:
    # 当程序没有出现异常时执行的代码(else可写可不写)
    print("程序正常执行了")
finally:
    # 无论程序是否出现异常都要执行的代码(finally可写可不写)
    print("不管程序是否有异常我都要执行,将打开的文件关闭")
    file.close()  # 关闭文件对象

嵌套异常示例

# 捕获所有类型的异常异常示例
try:
    file = open("C:/Users/admin/Desktop/TEST.txt", "r", encoding="UTF-8")  # encoding必须要写因为他的位置不在第三个参数
# 捕获所有类型的异常
except Exception as e:  # e只能接收到第一次捕获的异常
    print(e)  # 输出异常信息
    error = str(e)  # 将异常类型转换为字符串
    error_type = str(type(e))  # 将异常类型保存到一个字符串中
    print(error_type)  # 打印异常类型
else:
    # 当程序没有出现异常时执行的代码(else可写可不写)
    print("程序正常执行了")
finally:
    # 无论程序是否出现异常都要执行的代码
    print("不管程序是否有异常我都要执行,将打开的文件关闭")
    try:
        file.close()  # 关闭文件对象,如果文件不存在则会抛出一个变量未定义的异常,因为上面的file并没有创建成功
    except NameError as e2:
        print("当文件未创建成功,file.close()会抛出一个变量未定义异常")
        print(e2)  # 输出异常信息
        error = str(e2)  # 将异常类型转换为字符串
        error_type = str(type(e2))  # 将异常类型保存到一个字符串中
        print(error_type)  # 打印异常类型

66.异常的传递

示例

# 异常的传递


def fun1():
    array = [1, 2, 3]
    print(array[3])


def fun2():
    print("fun2")
    fun1()  # 调用fun1函数,fun1函数的异常会传递给当前函数


def main():
    try:
        fun2()  # 调用fun2函数,fun2的异常会传递给当前函数
    except IndexError as e:
        print(e)
        print(type(e))


main()  # 调用主函数

67.Python模块

概念:

  • Python模块(Module),是一个Python文件,以.py结尾
  • 模块能定义函数、类、和变量
  • 模块里也可能包含可执行的代码
  • 我们可以认为一个模块就是一个工具包
  • 导入各种工具包,实现各种功能

模块的作用

  • 每一个模块都可以实现一个功能
  • 比如random模块,可以生成随机数

模块的导入语法

import 模块名
import 模块名 as 别名
from 模块名 import *  # 导入模块中的所有类、变量、函数等
from 模块名 import 类 as 别名  # 可以不写别名,要使用的时候直接用类名就可以了,不用模块名.类名()的方式了
from 模块名 import 变量 as 别名
from 模块名 import 函数 as 别名
from 模块名 import 功能名 as 别名

# 通过模块名.函数名() 可以调用这个模块的指定函数进行使用
模块名.函数名()
变量 = 模块名.变量名()  # 用变量接收模块名里面的变量
模块名.类名()  # 调用模块中的类

# 补充:按住Ctrl点击模块名,可以进入到这个模块查看它拥有的函数、变量等

自定义模块的使用

# 在外部新建一个Python文件并且写入add函数
# 导入自定义模块的add函数
from add_module import add
print(add(1, 3, 4, 6))

注意事项

  • 如果调用了两个模块,里面有两个名字一样的函数,那么后面导入的模块的函数会生效,前面的函数无效

模块内代码的执行

# 当前文件作为一个模块
# 判断如果是在模块内运行,则执行以下内容
# 如果是在外部调用时则不会,执行以下内容
if __name__ == '__main__':  # 判断是否为主函数
    print(add(1, 3))

模块中的__all__变量

# 当别的文件使用from add_module import * 导入所有时
# 只能只使用到__all__中包含的函数、变量等,而其他函数、变量等不可用
__all__ = ['num', 'add']


def add(*args):
    """
    add函数实现多个数字相加返回结果
    :param args: 任意个数字参数
    :return: 返回它们的和
    """
    sum_add = 0
    for item in args:
        try:
            sum_add += item
        except Exception as e:
            print(e)
            print(type(e))
    return sum_add


# 判断如果是在模块内运行,则执行以下内容
# 如果是在外部调用时则不会,执行以下内容
if __name__ == '__main__':
    print(add(1, 3))


num = 5
array = [1, 2]

68.自定义Python包

操作步骤

  • 右键项目文件夹新建一个package
  • 每一个包都会有一个__init__.py文件
  • 如果没有这个文件,那它就是一个普通的文件夹
  • 每一个包中都可以包含多个模块文件

导入包中的模块的语法

import 包名.模块名  # 表示导入包中的模块中的所有东西
from 包名 import 模块名  # 表示导入包中的模块中的所有东西
from 包名 import 模块名1, 模块名2  # 表示导入包中的模块1和模块2中的所有东西
from 包名.模块名 import 函数名  # 表示导入包中的模块中的某个函数

包的__all__变量

# 作用:当使用from 包名 import * 导入模块时只会导入变量中包含的模块
# 这个变量要写在__init__.py中
__all__ = ['模块名1', '模块名2', '模块名3']

69.安装第三方包

语法

# 在cmd中运行
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple 包名称

示例

# 安装 科学计算numpy包
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple numpy

# 安装 数据分析pandas包
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pandas

# 安装 请求包request
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple requests

# 安装 数据可视化模块pyecharts
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyecharts
# 安装 web服务器通信模块
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple urllib3
# 安装 pymysql用于对数据进行操作
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pymysql
# 安装 pyspark框架
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyspark
# PyArrow 是一个强大的库,可以提供高性能的数据处理和交换功能,pandas3.0依赖库
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyarrow

查看已安装的所有第三方包

pip list

更新模块

pip install --upgrade 模块名

查看某个模块是否已安装

pip show 模块名

补充:设置pip默认的安装源

  • 第一步在文件路径栏输入 %appdata% 回车

  • 在这个目录下新键一个名为 pip 的文件夹

  • 在这个文件夹中新建一个文件 命名为 pip.ini

  • 在这个文件中写入以下代码

  • [global]
    timeout = 6000
    index-url = https://pypi.tuna.tsinghua.edu.cn/simple
    trusted-host = pypi.tuna.tsinghua.edu.cn
    

    index-url 表示下载源的路径

    trusted-host 表示下载源的域名

70.异常-模块-包综合练习

模块文件str_util.py

def str_reverse(string):
    """
    字符串反转函数
    :param string: 传入一个字符串
    :return: 返回反转后的字符串
    """
    return string[::-1]  # 反转字符串,将新的字符串返回


def substr(string, x, y):
    """
    返回一个区间的元素
    :param string: 传入一个字符串
    :param x: 起始下标
    :param y: 结束下标(不包含)
    :return: 返回截取后的字符串
    """
    new_str = string[x: y: 1]
    return new_str


if __name__ == '__main__':  # 在外部导入的时候不会被执行
    print(substr("Tom", 1, 2))

模块文件file_util.py



def get_file_info(file_name):
    """
    读取文件中的所有内容并打印
    :param file_name: 文件的路径
    :return: None
    """
    file = None  # None 默认的布尔值为False
    try:
        file = open(file_name, 'r', encoding='UTF-8')
        print(file.read())
    except Exception as e:
        print(f"文件不存在,file变量创建失败")
        print(e)
        print(type(e))
    finally:
        if file:  # 如果file变量不为None则执行文件的关闭操作
            file.close()


def append_to_file(file_name, data):
    """
    将数据追加写入指定的文件中
    :param file_name: 文件的路径
    :param data: 数据
    :return: None
    """
    with open(file_name, 'a', encoding='UTF-8') as file:
        file.write(str(data))

主程序代码

# 字符串操作、函数、文件操作、包和模块、异常处理练习
from my_utils import file_util, str_util

my_str = "Tom good morning!"
array = [1, 2, 3, 4, 5]
reverse_str = str_util.str_reverse(my_str)
print(f"字符串反转后的结果为:{reverse_str}")

substring = str_util.substr(my_str, my_str.index("good"), my_str.index("good")+5)
print(f"截取的子字符串为:{substring}")

array2 = str_util.substr(array, 0, 2)  # 返回一个区间的元素并存到列表中,因为array参数是列表类型
print(f"截取的子字符串为:{array2}")

# 使用自定义模块里的get_file_info方法读取指定路径下的所有文件内容并打印出来
file_util.get_file_info("C:/Users/admin/Desktop/bil.txt")

# 使用自定义模块里的append_to_file方法向指定的文件追加写入指定的数据
file_util.append_to_file("C:/Users/admin/Desktop/TEST.txt", array)

71.JSON数据格式

概念:

  • json是一种轻量级的数据交互格式
  • json是一种可以在各种编程语言中流通的数据格式
  • 负责将不同编程语言中的数据传递和交互
  • 可以按照json指定的格式取组织和封装数据
  • json本质上是一个带有特定格式的字符串
  • json数据和dict字典类型和list列表类型写法是一样的
  • 也可以是列表类型嵌套多个字典数据的写法,但列表中只能嵌套字典和列表类型

语法

# json写法1
{"name":"admin", "age":18}

# json数据写法2
[{"name":"admin", "age":18},{"name":"admin", "age":18}]

Python数据和Json数据的相互转换

# 导入json模块
import json

# 准备符合Json格式的Python数据
data = [{"名字": "admin", "age": 18}, {"name": "admin", "age": 18}, ["Tom", 5]]

# 通过json模块的dumps方法将Python数据转换为Json数据
# 传入两个参数
# 第一个是要转为Json的python数据
# 第二个是选择是否保留其中的非ASCII字符,False为保留中文不被转码
my_json = json.dumps(data, ensure_ascii=False)  # 把Python数据作为参数传入,返回一个Json数据
print(my_json)
print(type(my_json))  # str类型,json本质就是字符串

# 通过json模块的loads方法把Json数据转换为了python数据
data2 = json.loads(my_json)  # 把Json数据作为参数传入,最外层是什么类型就返回什么类型
print(data2)
print(type(data2))

72.综合练习使用Json数据生成折线图

# 练习 将Json数据生成一个折线图
from pyecharts.charts import Line
import json
import time
from pyecharts.options import TitleOpts, LabelOpts
array = ['美国', '日本', '印度']  # 文件的名称列表
line = Line()  # 获得折线图对象
# 循环读取每个文件的数据并将他们的数据作为x和y添加到折线图对象中
for country in array:
    # 获得每个文件的地址
    file_addr = "D:/App Data/PythonProject/Python-learn/折线图数据/%s.txt" % country
    # 打开每个文件
    file = open(file_addr, "r", encoding="UTF-8")
    data = file.read()  # 读取每个文件中的所有数据存入字符串中
    first_str = data.split('(')[0]+'('  # 把文件的前面的部分提取出来
    data = data.replace(first_str, "")  # 将指定字符串替换为空
    data = data[:-2]  # 取出末尾的两个字符并返回新的字符串
    data = json.loads(data)  # 将Json数据转换为python类型的dict类型或list类型
    # 获取美国疫情数据的x和y数据
    us_data_x = data["data"][0]["trend"]["updateDate"][:314]  # 获取日期list列表
    us_data_y = data["data"][0]["trend"]["list"][0]["data"][:314]  # 获取日期对应的数据list

    line.add_xaxis(us_data_x)  # 添加x轴数据(共用)
    line.add_yaxis(f"{country}的疫情数据", us_data_y, label_opts=LabelOpts(is_show=False))  # 添加美国的y轴数据,并设置系列的属性,不在折线上显示数字
    # line.add_yaxis("日本的疫情数据", jp_data_y, label_opts=LabelOpts(is_show=False))  # 添加日本的y轴数据
    # line.add_yaxis("印度的疫情数据", in_data_y, label_opts=LabelOpts(is_show=False))  # 添加印度的y轴数据
    # 数据图表模块的全局配置
    line.set_global_opts(
        title_opts=TitleOpts(title="2020年美日印三国确诊人数对比折线图", pos_left="center", pos_bottom="1%")
    )

line.render()  # 构成折线图文件rander.html
file.close()  # 关闭文件对象

73.综合练习使用Json数据和pyecharts模块生成热点地图

构建全国热点图

# 全国疫情热点图的构建
# 创建一个可视化的地图文件
# 导入可视化图表模块的Map功能
from pyecharts.charts import Map
from pyecharts.options import VisualMapOpts, TitleOpts
import json
import time

my_map = Map()  # 构建地图对象
file = open("D:/App Data/PythonProject/Python-learn/地图数据/疫情.txt", "r", encoding="UTF-8")
data = file.read()
file = close()
data = json.loads(data)  # 将Json数据转换为python数据 重要
provinces = data["areaTree"][0]["children"]  # 获得所有省份的数据
data = list()
for province in provinces:  # 循环获得每个省份的数据
    pro_name = None  # aa
    if province["name"] == "广西":  # 通过判断修改数据
        pro_name = "广西壮族自治区"
    elif province["name"] == "香港" or province["name"] == "澳门":
        pro_name = province["name"] + "特别行政区"
    elif province["name"] == "新疆":
        pro_name = "新疆维吾尔自治区"
    elif province["name"] == "西藏":
        pro_name = "西藏自治区"
    elif province["name"] == "宁夏":
        pro_name = "宁夏回族自治区"
    elif province["name"] == "内蒙古":
        pro_name = "内蒙古自治区"
    else:
        pro_name = province["name"] + "省"

    my_tuple = (pro_name, province["children"][0]["total"]["confirm"])
    data.append(my_tuple)  # 将元组数据传入data列表中

# print(data)
# time.sleep(60)

# 添加数据
my_map.add("各省份确诊人数", data, "china")
# 设置全局配置
my_map.set_global_opts(
    title_opts=TitleOpts(title="全国疫情热点图"),
    visualmap_opts=VisualMapOpts(
        is_show=True,
        is_piecewise=True,
        pieces=[
            {"min": 1, "max": 9, "label": "1-9人", "color": "#ccffff"},
            {"min": 10, "max": 99, "label": "10-99人", "color": "#ffff99"},
            {"min": 100, "max": 499, "label": "100-499人", "color": "#ff9966"},
            {"min": 500, "max": 999, "label": "500-999人", "color": "#ff6666"},
            {"min": 1000, "max": 9999, "label": "1000-9999人", "color": "#cc3333"},
            {"min": 10000, "label": "1000人以上", "color": "#990033"}
        ]
    )
)
# 绘图
my_map.render("map_china.html")

各省疫情热点图的构建

# 各省疫情热点图的构建
# 创建一个可视化的地图文件
# 导入可视化图表模块的Map功能
from pyecharts.charts import Map
from pyecharts.options import VisualMapOpts, TitleOpts
import json
import time

my_map = Map()  # 构建地图对象
file = open("D:/App Data/PythonProject/Python-learn/地图数据/疫情.txt", "r", encoding="UTF-8")
data = file.read()
file.colse()
data = json.loads(data)  # 将Json数据转换为python数据 重要
provinces = data["areaTree"][0]["children"]  # 获得所有省份的数据
data = list()
city_s = None
c_name = "四川"  # 修改这个字符串变量可以查看不同省份的疫情数据
for province in provinces:  # 循环获得每个省份的数据
    if province["name"] == c_name:  # 只保存固定省份的数据
        city_s = province["children"]
        for city in city_s:
            city_name = None
            if city["name"] == "境外输入":
                continue
            elif province["name"] == "北京":
                city_name = city["name"] + "区"  # 对市名称进行拼接
            else:
                city_name = city["name"] + "市"  # 对市名称进行拼接

            my_tuple = (city_name, city["total"]["confirm"])  # 创建元组
            data.append(my_tuple)  # 将元组数据传入data列表中

# print(data)
# time.sleep(60)

# 添加数据
my_map.add(f"{c_name}省确诊人数", data, c_name)
# 设置全局配置
my_map.set_global_opts(
    title_opts=TitleOpts(title=f"{c_name}疫情热点图"),
    visualmap_opts=VisualMapOpts(
        is_show=True,
        is_piecewise=True,
        pieces=[
            {"min": 1, "max": 9, "label": "1-9人", "color": "#ccffff"},
            {"min": 10, "max": 99, "label": "10-99人", "color": "#ffff99"},
            {"min": 100, "max": 499, "label": "100-499人", "color": "#ff9966"},
            {"min": 500, "max": 999, "label": "500-999人", "color": "#ff6666"},
            {"min": 1000, "max": 9999, "label": "1000-9999人", "color": "#cc3333"},
            {"min": 10000, "label": "1000人以上", "color": "#990033"}
        ]
    )
)
# 绘图
my_map.render(f"{c_name}.html")

74.综合练习动态GDP图表的构建

from pyecharts.charts import Bar, Timeline
from pyecharts.options import LabelOpts
# 打开文件读取数据
data = None
with open("D:/App Data/PythonProject/Python-learn/GDPFile/1960-2019全球GDP数据.csv", "r", encoding="GB2312") as file:
    data = file.readlines()
    file.close()

del data[0]  # 删除第一条无效数据



data_dict = dict()
for line in data:
    line = line.strip("\n")
    year = int(line.split(',')[0])  # 取到年份
    country = line.split(',')[1]  # 取到国家
    gdp = float(line.split(',')[2])  # 取到gdp数据并把它转为浮点型
    try:
        data_dict[year].append([country, gdp])  # 向指定的key追加指定的值,如果key不存在会报错
    except Exception as e:
        data_dict[year] = []  # 向dict中添加元素key为year,值为空列表
        data_dict[year].append([country, gdp])  # 向指定的key追加指定的值
        # print(e)
        # print(type(e))

# print(data_dict)


# 排序年份
sorted_year_list = sorted(data_dict.keys())
# print(sorted_year_list)
time_line = Timeline()
for year in sorted_year_list:
    data_dict[year].sort(key=lambda element: element[1], reverse=True)  # 根据每一个列表元素里的第二个元素从大到小排序
    # 取出本年份前8的国家
    year_date = data_dict[year][0:8]
    x_data = []
    y_data = []
    for item in year_date:
        x_data.append(item[0])
        y_data.append(item[1] / 100000000)
    # 构建柱状图
    bar = Bar()
    x_data = x_data[::-1]
    y_data = y_data[::-1]
    bar.add_xaxis(x_data)
    bar.add_yaxis("GDP(亿)", y_data, label_opts=LabelOpts(position="right"))
    # 反转x轴和y轴
    bar.reversal_axis()
    time_line.add(bar, year)

# 自动播放设置
time_line.add_schema(
    play_interval=1000,  # 自动播放的间隔时间,单位毫秒
    is_timeline_show=True,  # 设置是否显示时间线
    is_auto_play=True,  # 设置是否自动播放
    is_loop_play=True  # 设置是否循环播放
)
# 主题设置

# 绘图
time_line.render("GDP时间线动态柱状图.html")

75.对象

概念

  • 设计类 (在生活中称为设计表格)
  • 创建对象(在生活中称为打印表格)
  • 对象属性赋值(在生活中称为填写表格)
  • 让对象执行具体的操作(称为面向对象)
  • 类(Class):类是对象的蓝图或模板。它定义了一个对象所具有的属性和方法。类是创建对象的基础
  • 对象(Object):对象是类的实例化具有类定义的属性和方法每个对象都是独立的,可以具有不同的状态和行为。
  • 属性(Attribute):属性是对象的数据,表示对象的状态。它们可以是变量或数据结构,用于存储对象的特定信息。
  • 方法(Method):方法是对象的行为,用于执行特定的操作。它们是与对象相关联的函数,可以访问和操作对象的属性来完成任务。
  • 封装(Encapsulation):封装是将相关数据和方法组合在一个单独的对象中的概念。通过封装,对象的内部细节对外部是隐藏的,只有特定的接口可以与对象进行交互。
  • 继承(Inheritance):继承是一种机制,允许创建一个新的类,从已存在的类中继承属性和方法。继承提供了代码重用层次结构的概念
  • 多态(Polymorphism):多态是指同一个方法可以根据对象的不同类型而表现出不同的行为。它允许使用统一的接口来处理不同类型的对象

语法

# 定义类的方法
class 类名称:
	类的属性 (成员变量)
	类的方法(成员方法) # 类中定义的函数称为方法
    
# 创建类对象的语法
对象 = 类名称()

示例

# 设计一个学生信息类
class Student:  # 类首字母通常大写,学生信息类
    name = None  # 记录学生姓名
    gender = None  # 记录学生性别
    nationality = None  # 记录学生国籍
    native_place = None  # 记录学生的籍贯
    age = None  # 记录学生年龄

    def fun(self):  # 类的成员方法 必须传入一个self的参数表示对象自身
        print(f"{self.name}")  # 输出对象的name变量,使用成员变量的时候必须使用self.变量名

    def fun2(self, *args):  # 类的成员方法 必须传入一个self的参数表示对象自身
        print(f"{self.name}{args[0]}")  # 输出对象的name变量,使用成员变量的时候必须使用self.变量名


stu = Student()  # 获得一个学生类的对象
stu.name = "Tom"  # 给学生类对象的name赋值
print(stu.name)  # 调用学生类对象的属性

print(stu)
print(type(stu))  # 打印stu的类型为主函数下面的Student类

stu.fun()  # 调用对象的成员方法fun()
stu.fun2(5)  # 调用对象得到成员方法fun2()

76.面向对象的概念

现实实物的组成是什么?

  • 属性
  • 行为
  • 类用于描述现实世界中的事物

类和对象的关系

  • 类是程序中的“设计图纸”
  • 对象是基于图纸生产的具体实体

什么是面向对象编程?

  • 面向对象编程就是使用对象进行编程
  • 设计一个类,基于类生成一个对象,并使用对象来完成具体的工作

77.构造方法

语法

# 在类中定义一个方法
def __init__(self, name, age, score):  # 构造方法,会自动把创建对象时传入的参数传到这里
	

示例

# 构造函数演示


class Student:  # 类首字母通常大写,学生信息类
    name = None  # 记录学生姓名
    # gender = None  # 记录学生性别
    nationality = None  # 记录学生国籍
    native_place = None  # 记录学生的籍贯
    age = None  # 记录学生年龄

    # 构造函数
    def __init__(self, name, gender, *args):  # 当对象被创建的时候会自动执行该方法
        print(f"姓名:{name}")
        print(f"性别:{gender}")
        # 如果上面没有定义这个成员变量,表示在这里创建一个变量并给他赋值
        self.name = name  # 把创建对象时传入的参数赋值给成员变量
        self.gender = gender  # 把创建对象时传入的参数并创建一个成员变量
        for item in args:
            print(item)


stu = Student("Tom", "男", "美国", "18")  # 创建对象会调用类的构造方法

print(stu.name)  # 因为在构造函数中已经把传入的参数赋值给成员变量name了,所有这里可以拿到传入的数据

练习

# 类、对象和构造函数的练习
# 输入学生信息并保存所有学生信息


class Student:

    def __init__(self, in_name, in_age, in_address):
        self.name = in_name
        self.age = in_age
        self.address = in_address


stu_list = list()
num = 10  # range的长度
for item in range(num):
    print(f"当前录入第{item+1}位学生信息, 总共需要录入{len(range(num))}位学生信息")
    name = input("请输入学生姓名:")
    age = input("请输入学生年龄:")
    address = input("请输入学生地址:")
    stu = Student(name, age, address)
    print(f"第{item+1}位学生信息录入完成,信息为:【学生姓名:{stu.name},年龄:{stu.age},地址:{stu.address}】")
    stu_list.append(stu)  # 将每个学生的数据对象存入list列表中

print(stu_list[0].name)  # 获得列表的第一个元素对象的name属性

补充:在类中调用自身的方法的语法

class Read:
    def read_line(self, name):
        print(name)
	
    # 在类中调用自身的方法需要传一个self参数="自身的类名",后面的参数需要使用关键字参数传参
    read_line(self="Read", name="Tom")

78.魔术(内置)方法

概念:类的内置方法也称为魔术方法

类的内置方法__str__()

class Student:

    def __init__(self, in_name, in_age, in_address):
        self.name = in_name
        self.age = in_age
        self.address = in_address
    # 当打印对象的时候会返回一个地址值
    # 这个函数可以改变对象打印时输出的值

    def __str__(self):
        # 把所有的成员变量放进列表中然后转成字符串
        print_str = str([self.name, self.age, self.address])
        # 返回值是一个字符串
        return print_str  # 打印输出时显示的字符串


# 创建Student类的实例
stu = Student("Tom", 18, "北京")
print(stu)
print(type(stu))  # 未转为字符串前的类型为<class '__main__.Student'>
stu = str(stu)  # 把对象转成字符串,以便对数据进行操作
print(type(stu))  # 转为字符串后的数据类型为<class 'str'>

类的内置方法__lt__()

class Student:

    def __init__(self, in_name, in_age, in_address):
        self.name = in_name
        self.age = in_age
        self.address = in_address

    # 当打印对象的时候会返回一个地址值
    # 这个函数可以改变对象打印时输出的值

    def __lt__(self, other):  # 在类中写了这个方法,就可以让对象进行比较了
        # lt小于 所意在这里比较的时候要有 < 符号
        # 自定义比较的规则
        return self.age < other.age  # 返回一个布尔类型,两个对象都要有age成员变量,并且本类创建的对象必须在前面并且使用 <


# 创建Student类的实例
stu1 = Student("Tom", 18, "北京")
stu2 = Student("Jack", 20, "上海")

print(stu1 < stu2)  # 当使用比较符号的时候stu2会被传入stu1对象的__lt__函数的other参数中

类的内置方法__le__()

class Student:

    def __init__(self, in_name, in_age, in_address):
        self.name = in_name
        self.age = in_age
        self.address = in_address

    # 当打印对象的时候会返回一个地址值
    # 这个函数可以改变对象打印时输出的值

    def __le__(self, other):  # 在类中写了这个方法,就可以让对象进行比较了
        # le小于等于 所以在这里比较的时候要有 <= 符号
        # 自定义比较的规则
        return self.age <= other.age  # 返回一个布尔类型,两个对象都要有age成员变量,并且本类创建的对象必须在前面并且使用 <=

# 创建实例
stu1 = Student("Tom", 20, "北京")
stu2 = Student("Jack", 20, "上海")

print(stu1 <= stu2)  # 当使用比较符号的时候stu2会被传入stu1对象的__le__函数的other参数中

类的内置方法__eq__()

class Student:

    def __init__(self, in_name, in_age, in_address):
        self.name = in_name
        self.age = in_age
        self.address = in_address

    # 当打印对象的时候会返回一个地址值
    # 这个函数可以改变对象打印时输出的值

    def __eq__(self, other):  # 在类中写了这个方法,就可以让对象进行比较了
        # eq等于 所以在这里比较的时候要有 == 符号
        # 自定义比较的规则
        return self.age == other.age  # 返回一个布尔类型,两个对象都要有age成员变量,并且本类创建的对象必须在前面并且使用 ==

# 创建实例
stu1 = Student("Tom", 20, "北京")
stu2 = Student("Jack", 20, "上海")

print(stu1 == stu2)  # 当使用比较符号的时候stu2会被传入stu1对象的__eq__函数的other参数中

79.封装

概念

  • 封装的概念
    • 就是把类中成员变量和成员方法定义为私有的,外部不能访问的
  • 什么事私有成员?为什么需要私有成员?
    • 现实实物的部分属性和行为是不对外公开对使用者开放的
    • 在类中的属性和方法也要达成这个要求,所有就有了私有变量
  • 如何定义私有变量
    • 成员变量和成员方法的命名以__两个下划线作为开头即可
  • 私有成员的访问限制
    • 类对象无法访问私有成员
    • 类中的其它成员可以访问私有成员

语法

class Student:
	__私有变量名 = None  # 定义一个私有变量
    
    def __私有方法名():  # 定义一个私有方法
        pass
    

示例

class Student:

    def __init__(self, in_name, in_age, in_address):
        self.name = in_name
        self.age = in_age
        self.address = in_address

    # 定义一个私有变量
    __private_variate = None

    # 定义一个私有方法
    def __private_fun(self):
        pass


# 创建实例
stu = Student("Tom", 18, "beijing")

print(stu.__private_variate)  # 无法调用类对象的私有成员变量,只能在类中使用,否则直接报错
stu.__private_fun()  # 无法调用类对象的私有方法,只能在类中使用,否则直接报错

封装练习

# 设计一个手机类
class Phone:
    __is_5g_enable = False  # 私有成员变量,表示是否开启5g,默认为False不开启

    def __check_5g(self):  # 私有成员方法,控制5g的开启和关闭
        if self.__is_5g_enable:
            print("5g开启")
        else:
            print("5g关闭,使用4g网络")


    def call_by_5g(self, electric_quantity):  # 传入一个参数电池电量
        if electric_quantity >= 30:  # 根据电量决定是否开启5g
            self.__is_5g_enable = True  # 开启5g
        else:
            self.__is_5g_enable = False  # 关闭5g
        self.__check_5g()  # 调用私有方法修改,具体实现网络的改变
        print("正在通话中")

# 创建实例
phone = Phone()
phone.call_by_5g(100)  # 调用对象的方法,传入电池电量可以根据电量控制5g的开和关

80.继承

单继承语法

# 把要继承的类作为参数传入新类即可得到父类中的所有变量和方法
class 类名(父类名称):  
	pass

示例

class Phone:
    __is_5g_enable = False  # 私有成员变量,表示是否开启5g,默认为False不开启

    def __check_5g(self):  # 私有成员方法,控制5g的开启和关闭
        if self.__is_5g_enable:
            print("5g开启")
        else:
            print("5g关闭,使用4g网络")

    def call_by_5g(self, electric_quantity):  # 传入一个参数电池电量
        if electric_quantity >= 30:  # 根据电量决定是否开启5g
            self.__is_5g_enable = True  # 开启5g
        else:
            self.__is_5g_enable = False  # 关闭5g
        self.__check_5g()  # 调用私有方法修改,具体实现网络的改变
        print("正在通话中")


# 定义一个新类的继承旧类Phone的所有变量和方法
class Phone2024(Phone):  # 将旧类Phone作为参数传入,表示新类继承旧类的所有东西
    phone_name = "phone2024"  # 定义新类的新增成员变量

    def fun(self):  # 定义新类的新增成员方法
        print("继承自Phone类")
    # pass关键字表示占位,暂时不写任何东西
    pass


# 创建一个新类的实例
phone_2024 = Phone2024()
phone_2024.call_by_5g(50)  # 调用继承自Phone类的方法
print(phone_2024.phone_name)  # 调用新类的新增的成员变量
phone_2024.fun()  # 调用新类新增的成员方法

多继承语法

# 把要继承的类作为参数传入新类即可得到父类中的所有变量和方法
# 子类调用两个父类都拥有的成员变量,会使用第一个父类参数的成员变量,同理方法也是一样
class 类名(父类名称1, 父类名称2, 父类名称3):  
	pass

示例

class Phone:  # 手机类
    phone_name = None
    common = "phone"

    def call_by_5g(self):  # 旧版本手机的通话方法
        print(f"{self.phone_name}5g通话中")


class Flashlight:  # 手电筒类
    brand = None
    common = "flashlight"

    def illumination(self):  # 手电筒的照明方法
        print(f"{self.brand}照明中")


class NewPhone(Phone, Flashlight):  # 继承多个父类,用于所有父类的变量和方法
    pass


# 创建新手机的实例
new_phone = NewPhone()
new_phone.call_by_5g()  # 调用新手机继承自旧版本手机的打电话方法
new_phone.illumination()  # 调用新手机继承自手电筒的照明方法
print(new_phone.common)  # 调用两个父类都拥有的成员变量,会使用第一个父类参数的成员变量,同理方法也是一样

81.复写父类成员方法和调用父类的方法和属性

概念

  • 复写
    • 对父类的成员属性或成员方法进行重新定义
  • 复写的语法
    • 在子类中重新实现同名成员方法或成员属性即可
  • 在子类中,如何调用父类成员
    • 方式一:父类名.成员方法(self)
    • 方式二:super().成员方法()
    • 成员属性也是一样的写法
  • 注意:只能在子类内部调用父类的同名成员,子类的实体类对象调用默认是调用子类复写的

示例

class Phone:  # 手机类
    phone_name = None
    common = "phone"

    def __init__(self, p_name):
        self.phone_name = p_name

    def call_by_5g(self):  # 旧版本手机的通话方法
        print(f"{self.phone_name}5g通话中")
        print("phone")


class Flashlight:  # 手电筒类
    f_brand = None
    common = "flashlight"

    def __init__(self, f_brand):
        self.f_brand = f_brand

    def illumination(self):  # 手电筒的照明方法
        print(f"{self.f_brand}照明中")


class NewPhone(Phone, Flashlight):  # 继承多个父类,用于所有父类的变量和方法
    # 重新给父类中拥有的成员变量赋值表示复写成员变量
    common = "NewPhone"  # 复写父类的成员变量(属性)

    # 重新定义一个函数,函数名用父类中一样的某个方法名表示重写这个方法
    def call_by_5g(self):  # 重写父类的成员方法
        print(f"{self.phone_name}5g通话中")
        # super()调用父类中的变量,顺序是继承的父类参数从左往右依次查找这个变量
        print(f"父类Phone中的common变量的值为:{super().common}")
        print(f"子类NewPhone中的common变量的值为:{self.common}")
        
        # 方式一使用super()调用父类的照明方法,顺序是继承的父类参数从左往右依次查找这个方法
        # super().illumination()
        
        # 方式二,制定调用某个父类的方法
        Flashlight.illumination(self)


new_phone = NewPhone(p_name="小米")  # 构造方法也是只继承第一个父类参数的构造方法
new_phone.call_by_5g()  # 调用重写后的成员方法
new_phone.illumination()  # 调用父类手电筒类的照明方法
print(new_phone.common)  # 调用重写后的成员变量

82.多态

概念

  • 多态,指的是多种状态,即完成某个行为时,使用不同的对象会得到不同的状态
  • 同样的行为、传入不同的对象、得到不同的状态
  • 如通过类型注解声明需要父类对象,实际传入子类对象进行工作,从而得到不同的工作状态
  • 抽象类:包含抽象方法的类称为抽象类
  • 抽象方法:没有具体实现的方法称之为抽象方法
  • 抽象类的作用
    • 多用于顶层的设计(设计标准)以便于具体实现
    • 也是对子类的一种软性约束,要求子类必须重写(实现父类的抽象方法)

示例1

class Animal:  # 定义一个动物类
    name: str = None

    # 父类确定有哪些属性和方法
    # 具体的实现由子类自行决定
    # 这个类就称之为抽象类
    # 方法体是空实现pass 我们称之为抽象方法
    # 含有抽象方法的类就是抽象类
    def speak(self):  # 定义一个成员方法
        pass


class Cat(Animal):  # 继承Animal类,子类的实例也可以称为Animal对象
    name = "猫"  # 重新父类的属性

    def speak(self):  # 重新父类的方法
        print(f"{self.name}类喵喵叫")


class Dog(Animal):  # 继承Animal类,子类的实例也可以称为Animal对象
    name = "狗"  # 重新父类的属性

    def speak(self):  # 重新父类的方法
        print(f"{self.name}类汪汪叫")


# 创建一个函数,传入一个Animal类对象参数,继承这个类的子类也可以称为Animal对象
def make_noise(animal: Animal):
    animal.speak()  # 不同的Animal对象调用相同的方法,执行的内容也不一样


# 创建实例
cat = Cat()
dog = Dog()

# 调用制造声音函数传入不同的动物类对象
make_noise(cat)
make_noise(dog)

示例2

# 定义一个顶层的抽象类,确定空调含有的方法,但不做具体实现
class AirConditioner:
    # 制冷方法
    def make_coll(self):
        pass

    # 制热方法
    def make_hot(self):
        pass

    # 左右摆风方法
    def swing(self):
        pass


class GreeAirConditioner(AirConditioner):  # 继承父类空调类
    # 重写制冷方法
    def make_coll(self):
        print("格力空调强力制冷")

    # 重写制热方法
    def make_hot(self):
        print("格力空调温和制热")

    # 重写左右摆风方法
    def swing(self):
        print("格力空调左右摆风")


class MideaAirConditioner(AirConditioner):  # 继承父类空调类
    # 重写制冷方法
    def make_coll(self):
        print("美的空调均衡制冷")

    # 重写制热方法
    def make_hot(self):
        print("美的空调快速制热")

    # 重写左右摆风方法
    def swing(self):
        print("美的空调左右摆风")


# 定义一个使用空调对象的函数
def use_air_conditioner(air_conditioner: AirConditioner):
    air_conditioner.make_coll()
    air_conditioner.make_hot()
    air_conditioner.swing()


# 创建子类实例,因为子类才有具体实现
gree_air: GreeAirConditioner = GreeAirConditioner()
midea_air: MideaAirConditioner = MideaAirConditioner()

print("=====格力空调的运行====")
use_air_conditioner(gree_air)
print("=====美的空调的运行====")
use_air_conditioner(midea_air)

83.变量的类型注解

概念

  • 给变量标注应该赋予的数据的类型
  • 不是强制性的,是解释性的

语法

变量名: 类型名称 =# 解释变量的值应该为指定的类型
变量名: list[int] = [int, int]  # 解释列表中元素的值值应该为指定的类型
# 解释列表中的元素应该为指定的类型并且元素类型顺序不能乱
变量名: tuple[str, int, bool] = (str, int, bool)  
变量名: set[list] = {[], [], []}  # 解释集合中元素的类型应该为列表类型
# 解释字典中key和Value的数据类型应该为指定的类型
变量名:dict[str, list] = {str: list, str: list}  

示例

# 方式一
# 基础数据类型注解
int_var: int = 2
str_var: str = '52'
bool_vat: bool = True

# 类对象类型注解


class Student:
    pass


stu: Student = Student()  # 给变量stu做注解,表示这个变量应该为一个Student对象类型的数据

# 基础容器类型的注解,给变量添加注释,让人清楚应该填入什么样的数据
list_var: list[int] = [2, 5, 6]
tuple_var: tuple[str, int, bool] = ("A", 23, True)
set_var: set[str] = {"sd", "Q", "N"}
dict_var: dict[str: list[str, int]] = {"a": ["B", 12]}

print(dict_var)


# 方式二 直接在变量的后面添加一个注释的方法
# 基础数据类型注解
int_var1 = 2         # type: int
str_var1 = '52'      # type: str
bool_vat1 = True     # type: bool

# 类对象类型注解


class Student:
    pass


# 给变量stu做注解,表示这个变量应该为一个Student对象类型的数据
stu1 = Student()  # type: Student

# 基础容器类型的注解,给变量添加注释,让人清楚应该填入什么样的数据
list_var1 = [2, 5, 6]                 # type: list[int]
tuple_var1 = ("A", 23, True)          # type: tuple[str, int, bool]
set_var1 = {"sd", "Q", "N"}           # type: set[str]
dict_var1 = {"a": ["B", 13]}          # type: dict[str: list[str, int]]

print(dict_var1)

84.函数的参数类型注解和返回值类型注解

语法

def 函数名(参数名1: 类型名, 参数名2: 类型名) -> 返回值类型:
	pass

85.Union联合类型注解

语法

变量名: list[Union[str, int]] # 表示应该传入这两个类型的其中一个作为元素

示例

from typing import Union  # 联合类型注解需要导包
my_list: list[Union[str, int]]  # 表示应该传入这两个类型的其中一个作为元素

86.面向对象思想创建图表综合练习

# 数据类,用于完成数据的封装


class Record:
    def __init__(self, date: str, order_id: str, money: float, province: str):
        self.date = date            # 订单日期
        self.order_id = order_id    # 订单ID
        self.money = money          # 订单金额
        self.province = province    # 销售省份

    def __str__(self):
        return f"{self.date}, {self.order_id}, {self.money}, {self.province}"


# 抽象类,定义文件读取的相关功能
class ReadFile:
    def reade_file(self) -> list[Record]:
        # 读取文件的数据返回,将每一行的数据封装到对象中,将所有对象封装到list列表中返回
        pass


# 子类一用于读取文件数据类型一
class TextFileRead(ReadFile):
    # 使用构造函数,让类对象被定义的时候传入一个路径参数
    def __init__(self, path):
        self.path = path  # 定义成员变量

    def reade_file(self) -> list:
        with open(self.path, "r", encoding="UTF-8") as file:
            data_list: list = file.readlines()

        # 定义一个list变量用于存储每一行的数据对象
        obj_list: list = list()
        for line in data_list:
            line = line.strip("\n")  # 去除字符串前后的子字符
            line_list = line.split(",")  # 将字符串通过逗号分割为列表
            record = Record(line_list[0], line_list[1], float(line_list[2]), line_list[3])
            obj_list.append(record)

        return obj_list  # 返回每一行的对象的列表

    # 测试函数的功能是否正常
    # reade_file(self="ReadFileSon1", file_name="D:/App Data/PythonProject/Python-learn/销售数据/2011年1月销售数据.txt")


# 用于测试的代码
# if __name__ == '__main__':
#     read_file = TextFileRead("D:/App Data/PythonProject/Python-learn/销售数据/2011年1月销售数据.txt")
#     list_obj = read_file.reade_file()
#     print(list_obj[0])


# 子类二用于读取文件数据类型二
class JsonFileRead(ReadFile):

    # 使用构造函数,让类对象被定义的时候传入一个路径参数
    def __init__(self, path):
        self.path = path

    def reade_file(self) -> list:
        with open(self.path, "r", encoding="UTF-8") as file:
            lines = file.readlines()  # 将每一行的数据存于列表中

        # 定义一个列表里面存储每一行的数据对象
        obj_list: list[Record] = []
        for line in lines:
            dict_line = json.loads(line)  # 将每一行的数据转为dict字典类型
            list_line = []  # 将每一行的数据转为list类型
            for item in dict_line:
                list_line.append(dict_line[item])
            record = Record(list_line[0], list_line[1], float(list_line[2]), list_line[3])
            obj_list.append(record)

        return obj_list  # 返回每一行的对象的列表

    # reade_file(self="ReadFileSon1", file_name="D:/App Data/PythonProject/Python-learn/销售数据/2011年2月销售数据JSON.txt")


# 用于测试的代码
# if __name__ == '__main__':
#     read_file = JsonFileRead("D:/App Data/PythonProject/Python-learn/销售数据/2011年2月销售数据JSON.txt")
#     list_obj = read_file.reade_file()
#     print(list_obj[0])
# 主函数文件
# 创建一月份的实例
text_file_read = TextFileRead("D:/App Data/PythonProject/Python-learn/销售数据/2011年1月销售数据.txt")
# 创建二月份的实例
json_file_read = JsonFileRead("D:/App Data/PythonProject/Python-learn/销售数据/2011年2月销售数据JSON.txt")

# 获得一月份的数据
jan_data: list[Record] = text_file_read.reade_file()
# 获得二月份的数据
feb_data: list[Record] = json_file_read.reade_file()
# 合并两个月的数据
all_data: list[Record] = jan_data + feb_data

# 定义一个字典变量
dict_data = dict()

for obj in all_data:
    # 如果字典的key中包含obj.date 当前(循环的)日期,就让它的值+=当前订单的money
    if obj.date in dict_data.keys():
        # 当前订单的money累加给今天的日期(key)的值
        dict_data[obj.date] += obj.money
    else:
        # 如果没有这个日期表示是今天的第一个订单直接把该订单的money给key赋值就行了
        dict_data[obj.date] = obj.money


from pyecharts.charts import Bar
from pyecharts.options import *
from pyecharts.globals import ThemeType


# 创建柱状图实例
# 创建实例的时候可以传入参数确定柱状图的主题
bar = Bar(init_opts=InitOpts(theme=ThemeType.LIGHT))
# 传入一个列表参数表示x轴的数据
bar.add_xaxis(list(dict_data.keys()))  # 添加x轴的数据
# 第一个位置参数表示Y轴的标题
# 第二个位置参数表示y轴的数据
# 第三个关键字参数label_opts 一个标签对象,这个标签对象可以传入一个布尔值表示是否显是柱状如里的数据
bar.add_yaxis("销售额", list(dict_data.values()), label_opts=LabelOpts(False))
# 设置全局配置
bar.set_global_opts(
    title_opts=TitleOpts(title="每日销售额")  # 实例化一个title对象
)

bar.render("每日销售额柱状图.html")

为什么要把代码分为多个文件

  • 可读性差:当代码量增加时,单个文件会变得非常庞大和复杂,难以阅读和理解。这会增加代码维护的难度,并且使其他开发人员很难参与到项目中。
  • 可维护性低:当需要修改或调试特定功能时,必须在整个文件中搜索并定位相关代码。这样的过程既耗时又容易出错。同时,由于缺乏模块化,对代码进行重用也变得困难。
  • 命名冲突:如果所有代码都在同一个作用域中,可能会导致变量、函数和类等命名冲突的问题。这会导致错误的行为和不可预测的结果。
  • 难于并行开发:在团队协作中,如果所有人都修改同一个文件,可能会导致冲突和代码覆盖的问题。而合并多个人的更改可能会非常困难。
  • 缺乏模块化:将代码分成逻辑相关的模块可以提高代码的可组合性和可重用性。如果所有代码都写在一个文件中,就无法充分利用模块化的好处,而且很难将代码库拆分成独立的组件。

87.在python中使用Mysql

步骤

  • 安装第三方库pymysql
  1. 导入连接数据库类:from pymysql import Connection

  2. 创建连接对象

  3. 传入数据库参数

  4. 通过数据库对象调用cursor()方法返回一个游标对象

  5. 选择通过数据库对象的select_db(‘数据库名’)方法选择要操作的数据库

  6. 使用游标对象的execute(‘SQL语句’)方法执行sql语句,获取或者修改数据库数据

  7. 使用游标对象的fetchall()和fetchone()方法获得查询返回的结果

    或者使用数据库对象的commit()方法把插入或修改的数据提交到数据库中

  8. 关闭数据库连接

示例

from pymysql import Connection  # 1.导入数据库连接类
conn = Connection(  # 2.获得数据库对象
    host='localhost',  # 3.主机名或IP地址
    port=3306,  # 端口号
    user='root',  # 数据库用户名
    password='root'  # 数据库密码
    # ,database='test_py'  # 通过传参的方式选择要操作的数据库 
)
# 打印Mysql数据库软件信息(版本号)
# print(conn.get_server_info())

# 4.获得游标对象
cursor = conn.cursor()

# 5.通过数据库对象的select_db('数据库名')方法选择要操作的数据库
conn.select_db('test_py')

# 6.使用游标对象,执行SQL语句操作数据库
# 游标对象的execute("要执行的sql语句")方法,使用这个方法可以执行SQL语句
# 当使用execute方法插入和更新数据库数据是需要调用commit()确认插入或确认修改
cursor.execute("create table user(id int, name varchar(50), gender char(1), age int);")

# 7.使用游标对象的fetchall()和fetchone()方法返回SQL语句的执行结果
# fetchall()表示返回所欲数据,通常用于查询语句,返回一个元组,元组里面有多个元组元素每一个元组就是一行数据,每一个元素就是一个列的值
# fetchone()表示返回一条数据,通常用于查询一条数据,返回一个元组,每个元素就是一个列的值
result = cursor.fetchone()
print(result)

# 8.关闭游标和数据库连接
cursor.close()
conn.close()

python对数据进行插入和更新

from pymysql import Connection  # 1.导入数据库连接类
conn = Connection(  # 2.获得数据库对象
    host='localhost',  # 3.主机名或IP地址
    port=3306,  # 端口号
    user='root',  # 数据库用户名
    password='root'  # 数据库密码
    # ,database='test_py'  # 通过传参的方式选择要操作的数据库 
)

# 4.获得游标对象
cursor = conn.cursor()

# 5.通过数据库对象的select_db('数据库名')方法选择要操作的数据库
conn.select_db('test_py')

# 6.使用游标对象,执行SQL语句操作数据库
# 当使用execute方法插入和更新数据库数据是需要调用commit()确认插入或确认修改
cursor.execute(
    "INSERT INTO user(id, name, gender, age)"
    "VALUES"
    "(3, 'mary', 0, 19),"
    "(4, 'John', 1, 22);"
)

# 7.使用游标对象的fetchall()和fetchone()方法返回SQL语句的执行结果
# 使用数据库对象的commit()方法对插入或更新进行提交
# 也可以在创建数据库对象的时候参数一个autocommit=True的参数表示自动提交
conn.commit()

# 8.关闭游标和数据库连接
cursor.close()
conn.close()

88.判断表是否存在,不存在则创建

知识点information_schema.tables是Mysql系统库中的一张表

它存储了当前数据库中所有表的信息

table_name表示information_schema.tables的表名列

重要:SQL语句中的CHARACTER SET=utf8mb4 表示将表的编码指定为UTF-8

如果数据类型对不上会报错,切记

# 2获得游标对象
cursor = conn.cursor()
# 判断orders表是否存在
table_name = 'orders'
# 知识点information_schema.tables是Mysql系统库中的一张表,它存储了当前数据库中所有表的信息
# table_name表示information_schema.tables的表名列
cursor.execute(f'''
    SELECT table_name 
    FROM information_schema.tables 
    WHERE table_type='BASE TABLE' AND table_name="{table_name}";
''')
existing_table = cursor.fetchone()  # 把SQL语句执行的结果返回

# 如果表不存在,选择要操作的数据库, 然后创建订单表
if existing_table is None:
    cursor.execute(f'''
        CREATE TABLE {table_name}(
           order_date DATE,
           order_id VARCHAR(255),
           money INT,
           province VARCHAR(10)
        ) CHARACTER SET=utf8mb4;
    ''')
    print("表创建成功")

89.防止SQL注入

使用传参的方式执行SQL语句可以防止SQL注入

sql = "INSERT INTO orders(order_date, order_id, money, province) VALUES (%s, %s, %s, %s)"
values = (obj.date, obj.order_id, obj.money, obj.province)
# 循环将每一条数据添加到数据库中
# 调用游标对象的execute()方法插入数据
# 第一个参数是静态的SQL语句,里面包含了占位符,这样做是为了避免SQL注入
# 第二个是具体的元组数据参数
cursor.execute(sql, values)

90.练习将文件存入数据库中

# 将数据存入数据库中
from pymysql import Connection  # 导入数据库连接类
# 数据类实体类,用于完成数据的封装


class Record:
    def __init__(self, date: str, order_id: str, money: float, province: str):
        self.date = date            # 订单日期
        self.order_id = order_id    # 订单ID
        self.money = money          # 订单金额
        self.province = province    # 销售省份

    def __str__(self):
        return f"{self.date}, {self.order_id}, {self.money}, {self.province}"



# 抽象类,定义文件读取的相关功能
class ReadFile:
    def reade_file(self) -> list[Record]:
        # 读取文件的数据返回,将每一行的数据封装到对象中,将所有对象封装到list列表中返回
        pass


# 子类一用于读取文件数据类型一
class TextFileRead(ReadFile):
    # 使用构造函数,让类对象被定义的时候传入一个路径参数
    def __init__(self, path):
        self.path = path  # 定义成员变量

    def reade_file(self) -> list:
        with open(self.path, "r", encoding="UTF-8") as file:
            data_list: list = file.readlines()

        # 定义一个list变量用于存储每一行的数据对象
        obj_list: list = list()
        for line in data_list:
            line = line.strip("\n")  # 去除字符串前后的子字符
            line_list = line.split(",")  # 将字符串通过逗号分割为列表
            record = Record(line_list[0], line_list[1], float(line_list[2]), line_list[3])
            obj_list.append(record)

        return obj_list  # 返回每一行的对象的列表

    # 测试函数的功能是否正常
    # reade_file(self="ReadFileSon1", file_name="D:/App Data/PythonProject/Python-learn/销售数据/2011年1月销售数据.txt")


# 用于测试的代码
# if __name__ == '__main__':
#     read_file = TextFileRead("D:/App Data/PythonProject/Python-learn/销售数据/2011年1月销售数据.txt")
#     list_obj = read_file.reade_file()
#     print(list_obj[0])


# 子类二用于读取文件数据类型二
class JsonFileRead(ReadFile):

    # 使用构造函数,让类对象被定义的时候传入一个路径参数
    def __init__(self, path):
        self.path = path

    def reade_file(self) -> list[Record]:
        with open(self.path, "r", encoding="UTF-8") as file:
            lines = file.readlines()  # 将每一行的数据存于列表中

        # 定义一个列表里面存储每一行的数据对象
        obj_list: list[Record] = []
        for line in lines:
            dict_line = json.loads(line)  # 将每一行的数据转为dict字典类型
            list_line = []  # 将每一行的数据转为list类型
            for item in dict_line:
                list_line.append(dict_line[item])
            record = Record(list_line[0], list_line[1], float(list_line[2]), list_line[3])
            obj_list.append(record)

        return obj_list  # 返回每一行的对象的列表

    # reade_file(self="ReadFileSon1", file_name="D:/App Data/PythonProject/Python-learn/销售数据/2011年2月销售数据JSON.txt")


# 获得文本读取对象
test_file_read = TextFileRead("D:/App Data/PythonProject/Python-learn/销售数据/2011年1月销售数据.txt")
# 获得Json读取对象
json_file_read = JsonFileRead("D:/App Data/PythonProject/Python-learn/销售数据/2011年2月销售数据JSON.txt")
# 获得一月数据
text_data_list: list[Record] = test_file_read.reade_file()
# 获得二月数据
json_data_list: list[Record] = json_file_read.reade_file()
# 获得所有的数据
all_data_list: list[Record] = text_data_list + json_data_list

# 1获得数据库连接对象
conn = Connection(
    host='localhost',
    port=3306,
    user='root',
    password='root',
    database='test_py'  # 选择要操作的数据库
    # ,autocommit=True
)

# 2获得游标对象
cursor = conn.cursor()
# 判断orders表是否存在
table_name = 'orders'
# 知识点information_schema.tables是Mysql系统库中的一张表,它存储了当前数据库中所有表的信息
# table_name表示information_schema.tables的表名列
cursor.execute(f'''
    SELECT table_name 
    FROM information_schema.tables 
    WHERE table_type='BASE TABLE' AND table_name="{table_name}";
''')
existing_table = cursor.fetchone()  # 把SQL语句执行的结果返回

# 如果表不存在,选择要操作的数据库, 然后创建订单表
if existing_table is None:
    cursor.execute(f'''
        CREATE TABLE {table_name}(
           order_date DATE,
           order_id VARCHAR(255),
           money INT,
           province VARCHAR(10)
        ) CHARACTER SET=utf8mb4;
    ''')
    print("表创建成功")


try:
    for obj in all_data_list:  # 获得实体类对象

        sql = "INSERT INTO orders(order_date, order_id, money, province) VALUES (%s, %s, %s, %s)"
        values = (obj.date, obj.order_id, obj.money, obj.province)
        # 循环将每一条数据添加到数据库中
        # 调用游标对象的execute()方法插入数据
        # 第一个参数是静态的SQL语句,里面包含了占位符,这样做是为了避免SQL注入
        # 第二个是具体的元组数据参数
        cursor.execute(sql, values)
        # print(f"{obj.date}, {obj.order_id}, {float(obj.money)}, {obj.province}")

        # 正常时提交事务
        conn.commit()
except Exception as e:
    # 出现错误时回滚事务
    conn.rollback()
    print(str(e))
finally:
    # 5关闭游标对象和数据库对象
    cursor.close()
    conn.close()

91.Mysql数据存为Json文件

# 连接数据库
# 导包
from pymysql import Connection

# 创建实例得到数据库链接对象
conn = Connection(
    host='localhost',
    port=3306,
    user='root',
    password='root',
    database='test_py'
)

# 获取游标对象
cursor = conn.cursor()

# 执行SQL语句获取列表中的所有数据
sql = "select * from orders"  # 定义SQL变量,避免SQL注入
cursor.execute(sql)  # 执行SQL语句获取列表中的所有数据
data: tuple[tuple] = cursor.fetchall()  # 返回一个嵌套元组

# 创建一个文件用于存方法数据
path = "C:/Users/admin/Desktop/orders.txt"
with open(path, 'a', encoding='UTF-8') as file:
    # 将每一个元素转为Json格式
    for line in data:
        index = 0
        line_list: list = list(line)  # 将元组转为列表
        line_list[0] = str(line_list[0]).strip('datetime.date()')  # 将日期格式转为str类型,然后取出前后的指定字符
        order_date = line_list[0].replace(',', '-')  # 将字符串中的逗号替换为横杠,存入变量中
        order_id = line_list[1]  # 得到订单id
        order_money = line_list[2]  # 得到订单金额
        order_province = line_list[3]  # 得到订单地址

        # 将每一条数据组合成Json格式
        line_json = '{"data": "%s", "order_id": "%s", "money": %s, "province": "%s"}\n' \
                    % (order_date, order_id, order_money, order_province)

        file.write(line_json)  # 将每一天数据追加写入文件中

# 关闭游标对象和数据库连接对象
cursor.close()
conn.close()

92.PySpark

什么是Spark? 什么是PySpark?

  • Spark是Apache基金会旗下的顶级开源项目,用于对海量数据进行大规模分布式计算
  • PySpark是Spark的Python实现,是Spark为Python开发者提供的编程入口,用于以Python代码完成Spark任务的开发
  • PySpark不仅可以作为Python第三方库使用,也可以将程序提交的Spark集群环境中,调度大规模集群进行执行

为什么要学习PySpark?

  • 大数据开发是Python众多就业方向的明星赛道,是大数据开发中的核心技术

93.PySpark的使用

使用步骤

  • 导入PySpark包
  • 创建SparkConf类对象
  • 基于SparkConf类对象创建SparkContext类对象
  • 停止SparkContext对象的运行
  • PySpark需要和python的版本一定要兼容
  • 示例使用的是Python3.10PySpark3.5

示例

# 导包
from pyspark import SparkConf, SparkContext
# 使用SparkConf类创建一个配置配对象
# 链式调用实例化的时候可以通过类名.方法名连续调用多个方法
# 它们的返回值都是conf对象,才能实现链式调用
conf = SparkConf()
# setMaster() 是 SparkConf 类的一个方法,用于设置主节点的地址或模式
conf = conf.setMaster("local[*]")
# setAppName()方法用于设置 Spark 应用程序的名称, 它在 Spark Web UI 和日志中显示,并且对于调试和监控应用程序非常有用。
conf = conf.setAppName("test_spark_app")

# 实例化一个上下文对象(可以当成一种工具)让我们实现各种操作
# 通过 SparkContext 对象 sc,我们可以执行各种操作,例如读取和处理数据、应用转换和动作操作、调度任务等。
sc = SparkContext(conf=conf)  # 实例化的时候需要传入一个配置对象

# 打印PySpark版本
print(sc.version)


# 停止上下文对象的运行
sc.stop()

PySpark的编程模型

  1. 数据输入
    • 通过SparkContext类对象的成员方法完成数据的读取操作
    • 读取后得到RDD类对象
  2. 数据处理计算
    • 通过RDD类对象的成员方法完成各种数据计算的需求
  3. 数据输出
    • 将处理完成后的RDD对象调用各种成员方法完成写出文件、转换为list等操作

94.数据输入

概念

  • 当我们完成数据的输入只会返回一个RDD对象

  • RDD全称: 弹性分布式数据集(Resilient Distributed Datasets)

  • PySpark针对数据的处理,都是以RDD对象作为载体即:

    • 数据存储在RDD内

    • 各类数据的计算方法,也都是RDD的成员方法

    • RDD的数据计算方法,返回值依旧是RDD对象(可以使用链式调用)

PySpark数据输入的方式

  1. 通过sc对象的parallelize(数据容器) 方法,将Python容器转为RDD对象
from pyspark import SparkConf, SparkContext

# 实例化配置类
conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
# 实例化主入口,传入配置参数
sc = SparkContext(conf=conf)

# 使用sc对象的parallelize(数据容器) 方法传入数据容器,返回一个RDD对象
rdd1 = sc.parallelize([1, 2, 3, 4])
rdd2 = sc.parallelize([1, 2, 3, 4])
rdd3 = sc.parallelize("Tom")
rdd4 = sc.parallelize({"d": 1, "e": 5})
rdd5 = sc.parallelize({"d", "e"})
# 通过RDD对象的collect()方法获得里面的数据
# 不管是什么类型的数据都会返回一个列表,里面装着原容器里的值
# 如果是字典类型只能获得它的所有key并封装到一个列表里,然后返回
print(rdd1.collect())
print(rdd2.collect())
print(rdd3.collect())
print(rdd4.collect())
print(rdd5.collect())

sc.stop()
  1. 通过 sc对象的textFile(文件路径) 方法,将文件转为RDD对象
from pyspark import SparkConf, SparkContext

# 实例化配置类
conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
# 实例化主入口,传入配置参数
sc = SparkContext(conf=conf)

# 使用sc对象的textFile(文本文件路径) 方法传入文件路径,返回一个RDD对象
# RDD 是不可变的(immutable),任何操作都会生成一个新的 RDD
rdd = sc.textFile("C:/Users/admin/Desktop/TEST.txt")

# 使用RDD对象的collect()方法,可以返回一个列表
# 列表中的一个元素就是文件中的一行数据
print(rdd.collect())

sc.stop()

95.RDD对象的map()方法(算子)

使用RDD对象的map()方法对每个元素进行计算,并返回一个新的RDD对象

from pyspark import SparkConf, SparkContext
import os
# import sys
# print(sys.executable)  输出当前环境

# 需要设置PYSPARK_PYTHON的环境变量
# 将Python的解释器的路径赋值给 environ
os.environ['PYSPARK_PYTHON'] = 'D:/App/Python/Python310/python.exe'

# 实例化配置类
conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
# 实例化主入口,传入配置参数
sc = SparkContext(conf=conf)

# 获得一个RDD对象
rdd = sc.parallelize([1, 2, 3, 4, 5])
# RDD对象的map方法接收一个参数
# 这个参数是一个函数
# 这个函数需要传入一个参数,这个参数就是RDD数据的每一个元素
# 通过函数体对每一个元素进行相同的计算然后返回一个新的RDD对象
# 这个新的RDD对象里面含有经过计算的数据
# RDD 是不可变的(immutable),所以任何操作都会生成一个新的 RDD
new_rdd = rdd.map(lambda x: x * 2)

print(new_rdd.collect())
sc.stop()

96.RDD对象的flatMap()方法

RDD对象的flatMap()方法对每个元素进行处理,并解除列表嵌套

from pyspark import SparkConf, SparkContext
import os
# 指定Python解释器的位置
os.environ['PYSPARK_PYTHON'] = 'D:/App/Python/Python310/python.exe'

# 创建一个配置对象
conf = SparkConf().setMaster("local[*]").setAppName("test_spark_app")

# 创建一个sc对象
sc = SparkContext(conf=conf)

# 获得一个RDD对象
rdd = sc.parallelize(['name, age, gender', 'Tom, Jack, Mary'])

# 使用rdd的flatMap方法对每个元素进行处理并解除嵌套,返回一新的rdd对象
new_rdd = rdd.flatMap(lambda x: x.split(','))

# 查看new_rdd 对象的数据,所有元素都存在于同一个列表里
print(new_rdd.collect())

# 关闭sc
sc.stop()

97.RDD对象的reduceByKey方法

知识点

  • 二元元组:一个元组里只有两个元素称为二元元组

使用步骤和详解

  • 前面输入的数据必须是一个列表 里面嵌套一个元组 元组里面有两个元素
  • 该方法接收一个函数,这个函数需要传入两个参数
  • 并且这两个参数必须是同一种数据类型
  • 然后返回计算的结果存入一个新的元组的第二个元素中
  • 把列表中元组的第一个元素去重并放入一个新的元组的第一个元素中
  • 然后把它们的第二个元素进行计算并放入一个新的元组的第二个元素中
  • 最后把这些元组放入一个列表中

示例

from pyspark import SparkConf, SparkContext
import os
# 指定Python解释器的位置
os.environ['PYSPARK_PYTHON'] = 'D:/App/Python/Python310/python.exe'

# 创建一个配置对象
conf = SparkConf().setMaster("local[*]").setAppName("test_spark_app")

# 创建一个sc对象
sc = SparkContext(conf=conf)

# 获得一个RDD对象
rdd = sc.parallelize([('a', 1), ('b', 2), ('a', 4), ('b', 5), ('a', 6), ('b', 8), ('c', 9)])
# RDD的reduceByKey() 方法
# 前面输入的数据必须是一个列表 里面嵌套一个元组 元组里面有两个元素
# 该方法接收一个函数,这个函数需要传入两个参数
# 并且这两个参数必须是同一种数据类型
# 然后返回计算的结果存入一个新的元组的第二个元素中
# 把列表中元组的第一个元素去重并放入一个新的元组的第一个元素中
# 然后把它们的第二个元素进行计算并放入一个新的元组的第二个元素中
# 最后把这些元组放入一个列表中
new_rdd = rdd.reduceByKey(lambda a, b: a + b)


print(new_rdd.collect())

# 关闭sc
sc.stop()

98.练习统计整个文件中指定的字符串出现的次数

from pyspark import SparkConf, SparkContext
import os
# 指定Python解释器的位置
os.environ['PYSPARK_PYTHON'] = 'D:/App/Python/Python310/python.exe'

# 创建一个配置对象
conf = SparkConf().setMaster("local[*]").setAppName("test_spark_app")

# 创建一个sc对象
sc = SparkContext(conf=conf)

# 获得一个RDD对象
rdd = sc.textFile("C:/Users/admin/Desktop/orders.txt")

# 统计文件中指定字符串出现的次数
# 先统计每一行的指定字符串出现的次数 然后放入列表中
# 再将RDD对象转为list类型用于循环统计整个文件的指定字符出现的次数
rdd2 = rdd.map(lambda x: x.count('广东省'))
count_list = list(rdd2.collect())
sum_GX = 0
for item in count_list:
    sum_GX += item
print(sum_GX)

# 关闭sc
sc.stop()

99.统计文件中所有相同的字符串出现的次数

from pyspark import SparkConf, SparkContext
import os
# 指定Python解释器的位置
os.environ['PYSPARK_PYTHON'] = 'D:/App/Python/Python310/python.exe'

# 创建一个配置对象
conf = SparkConf().setMaster("local[*]").setAppName("test_spark_app")

# 创建一个sc对象
sc = SparkContext(conf=conf)

# 获得一个RDD对象
rdd = sc.textFile("C:/Users/admin/Desktop/count.txt")

# 读取整个文件对每行数据进行分割成列表 并解除嵌套
# 使用map方法将每个元素转为一个二元元组
# 使用reduceByKey方法将所有元素按key进行计算,返回一个列表嵌套元组的格式
new_rdd = rdd\
    .flatMap(lambda x: x.split(' '))\
    .map(lambda x: (x, 1))\
    .reduceByKey(lambda x, y: x + y)

print(new_rdd.collect())

# 关闭sc
sc.stop()

100.RDD对象的filter() 方法

使用步骤

  • 知识点 lambda中写判断的方法
  • 语法: 当if为真时返回值 if 判断条件 else 当if为假时返回的值
  • filter()方法需要传入一个函数
  • 这个函数需要传入一个参数
  • 这个参数就是列表的每一个元素
  • 返回值必须是一个布尔类型
  • 函数体对数据进行判断是否需要保存 True为保存 False为舍弃
  • 然后将处理完的数据存入列表中并返回一个新的RDD对象

示例

from pyspark import SparkConf, SparkContext
import os
# 指定Python解释器的位置
os.environ['PYSPARK_PYTHON'] = 'D:/App/Python/Python310/python.exe'

# 创建一个配置对象
conf = SparkConf().setMaster("local[*]").setAppName("test_spark_app")

# 创建一个sc对象
sc = SparkContext(conf=conf)

# 获得一个RDD对象
rdd = sc.parallelize({1, 2, 5, 60, 80, 20})

# 使用RDD对象的filter()方法对数据进行过滤
# 知识点 lambda中写判断的方法
# 语法: 当if为真时返回值 if 判断条件 else 当if为假时返回的值
# filter()方法需要传入一个函数
# 这个函数需要传入一个参数
# 这个参数就是列表的每一个元素
# 返回值必须是一个布尔类型
# 函数体对数据进行判断是否需要保存 True为保存 False为舍弃
# 然后将处理完的数据存入列表中并返回一个新的RDD对象
new_rdd = rdd.filter(lambda x: True if x >= 10 else False)

print(new_rdd.collect())
# 关闭sc
sc.stop()

101.RDD对象的distinct()方法

示例

from pyspark import SparkConf, SparkContext
import os
# 指定Python解释器的位置
os.environ['PYSPARK_PYTHON'] = 'D:/App/Python/Python310/python.exe'

# 创建一个配置对象
conf = SparkConf().setMaster("local[*]").setAppName("test_spark_app")

# 创建一个sc对象
sc = SparkContext(conf=conf)

# 获得一个RDD对象
rdd = sc.parallelize({1, 2, 2, 60, 80, 20})

# 使用RDD对象的distinct()方法对元素进行去重,并返回一个新的RDD对象
new_rdd = rdd.distinct()

print(new_rdd.collect())
# 关闭sc
sc.stop()

101.RDD对象的sortBy方法

使用介绍

  • 使用sortBy方法对数据进行排序
  • 需要传入一个函数,这个函数接收一个参数
  • 这个参数就是每一个元素,返回值的排序的依据
  • 第二个参数ascending是决定降序还是升序,不写这个参数默认为升序排序
  • 降序从大到小(False)排序还是升序从小到大(True)排序
  • 第三个参数numPartitions指定RDD的分区数量

示例

from pyspark import SparkConf, SparkContext
import os
# 指定Python解释器的位置
os.environ['PYSPARK_PYTHON'] = 'D:/App/Python/Python310/python.exe'

# 创建一个配置对象
conf = SparkConf().setMaster("local[*]").setAppName("test_spark_app")

# 创建一个sc对象
sc = SparkContext(conf=conf)

# 获得一个RDD对象
rdd = sc.textFile("C:/Users/admin/Desktop/count.txt")

# 读取整个文件对每行数据进行分割成列表 并解除嵌套
# 使用map方法将每个元素转为一个二元元组
# 使用reduceByKey方法将所有元素按key进行计算,返回一个列表嵌套元组的格式
# 使用sortBy方法对数据进行排序
# 需要传入一个函数,这个函数接收一个参数,这个参数就是每一个元素,返回值的排序的依据
# 第二个参数ascending是决定降序还是升序,不写这个参数默认为升序排序
# 降序从大到小(False)排序还是升序从小到大(True)排序
# 第三个参数numPartitions指定RDD的分区数量
new_rdd = rdd\
    .flatMap(lambda x: x.split(' '))\
    .map(lambda x: (x, 1))\
    .reduceByKey(lambda x, y: x + y)\
    .sortBy(lambda x: x[1], ascending=False, numPartitions=1)

# 获得一个RDD对象
my_rdd = sc.parallelize([5, 6, 2, 8, 9])

# 对每个元素进行降序排序
my_new_rdd = my_rdd.sortBy(lambda x: x, ascending=False, numPartitions=1)
print(new_rdd.collect())
print(my_new_rdd.collect())

# 关闭sc
sc.stop()

102.综合练习,对文本文件数据进行分析

from pyspark import SparkConf, SparkContext
import os
import json
# 指定Python解释器的位置
os.environ['PYSPARK_PYTHON'] = 'D:/App/Python/Python310/python.exe'

# 创建一个配置对象
conf = SparkConf().setMaster("local[*]").setAppName("test_spark_app")

# 创建一个sc对象
sc = SparkContext(conf=conf)

# 获得一个RDD对象
rdd = sc.textFile("D:/App Data/PythonProject/Python-learn/订单数据/orders.txt")

# 需求一 计算各个城市销售额排名
# 使用RDD对象的flatMap方法将每一行的数据进行分割构成列表,并解除嵌套
json_rdd = rdd.flatMap(lambda x: x.split("|"))
# 使用RDD对象的map方法将每个字符串元素(json)转为字典类型
dict_str_rdd = json_rdd.map(lambda x: json.loads(x))
# 使用RDD的map方法将每个字典元素取出需要的数据(城市名称, 销售额)重新组合成元组存入列表中
list_tuple_rdd = dict_str_rdd.map(lambda x: (x['areaName'], float(x['money'])))
# 使用RDD的reduceByKey方法将数据按key进行计算
# 按城市分组销售额聚合
count_list_tuple_rdd = list_tuple_rdd.reduceByKey(lambda x, y: x + y)
# 使用RDD对象的sortBy方法对数据进行排序
result_rdd = count_list_tuple_rdd.sortBy(lambda x: x[1], ascending=False, numPartitions=1)

# 需求二 全部城市有哪些商品类别在售卖
# 取出全部的商品类别,并去重,返回一个新的RDD对象
category_rdd = dict_str_rdd.map(lambda x: x['category']).distinct()

# 需求三 取出北京有哪些商品类别在售卖
# 使用filter方法对数据进行过滤,值保留北京的数据
# 使用map方法提取出商品类别
# 使用distinct方法去重
bj_category_rdd = dict_str_rdd\
    .filter(lambda x: x['areaName'] == '北京')\
    .map(lambda x: x['category'])\
    .distinct()

print(bj_category_rdd.collect())
# 关闭sc
sc.stop()

103.将RDD输出为Python数据

collect()

  • 将rdd对象转为列表类型

  • my_list = rdd.collect()

reduce()

  • 使用RDD对象的reduce() 方法累加列表中每一个元素的值,返回一个number(int)

  • 该方法需要传入一个函数

  • 这个两个参数的类型必须一致

  • 然后计算并返回这个类型的数据

  • num = rdd.reduce(lambda a, b: a + b)

take()

  • 使用RDD对象的take() 方法 返回指定元素个数(按顺序取出)的list列表
  • 该方法需要传入一个数字参数,表示取出的元素个数
  • list_2_ele = rdd.take(2)

count()

  • 使用RDD对象的count()方法 返回RDD对象中元素个数
  • count = rdd.count()

示例

from pyspark import SparkConf, SparkContext
import os
import json
# 指定Python解释器的位置
os.environ['PYSPARK_PYTHON'] = 'D:/App/Python/Python310/python.exe'

# 创建一个配置对象
conf = SparkConf().setMaster("local[*]").setAppName("test_spark_app")

# 创建一个sc对象
sc = SparkContext(conf=conf)

# 获得一个RDD对象
rdd = sc.parallelize([5, 4, 4, 7, 1])

# 将rdd对象转为列表类型
my_list = rdd.collect()

# 使用RDD对象的reduce() 方法累加列表中每一个元素的值,返回一个number(int)
# 该方法需要传入一个函数
# 这个函数需要传入两个参数(每个元素两两传入)
# 这个两个参数的类型必须一致
# 然后计算并返回这个类型的数据
num = rdd.reduce(lambda a, b: a + b)

# 使用RDD对象的take() 方法 返回指定元素个数(按顺序取出)的list列表
# 该方法需要传入一个数字参数,表示取出的元素个数
list_2_ele = rdd.take(2)

# 使用RDD对象的count()方法 返回RDD对象中元素个数
count = rdd.count()

print(count)

# 关闭sc
sc.stop()

104.将RDD的内容输出到文件中

注意事项

  • 调用保存文件的算子saveAsTextFile()方法 需要配置Hadoop依赖

  • 下载Hadoop安装包解压到D盘的指定路径中

  • Python代码中使用os模块配置Hadoop安装路径

  • os.environ[‘HADOOP_HOME’] = ‘hadoop安装路径’

  • 下载winutils.exe 并放入Hadoop安装路径的bin目录下

  • 下载hadoop.dll, 并放入C:/Windows/System32 文件夹内

  • Hadoop版本、Python版本和PySpark版本要兼容

  • 这里用的是hadoop3.0.0 、 Python3.10 、 PySpark3.5

  • 设置并行度为1,让工作全由一个线程去完成

from pyspark import SparkConf, SparkContext
import os
import json
# 指定Python解释器的位置
os.environ['PYSPARK_PYTHON'] = 'D:/App/Python/Python310/python.exe'
# 指定Hadoop的安装路径
os.environ['HADOOP_HOME'] = 'D:\App\Hadoop\hadoop-3.0.0'

# 创建一个配置对象
conf = SparkConf().setMaster("local[*]").setAppName("test_spark_app")
# 设置全局并行度为1,让工作全由一个线程去完成
# 作用是让RDD对象执行saveAsTextFile()方法时将所有数据存入一个文件中
# conf.set('spark.default.parallelism', '1')

# 创建一个sc对象
sc = SparkContext(conf=conf)

# 获得一个RDD对象
# 将并行度设置为1,让工作全由一个线程去完成
# 让RDD对象执行saveAsTextFile()方法时将所有数据存入一个文件中
rdd = sc.parallelize([5, 4, 4, 7, 1], numSlices=1)

# 使用RDD对象的saveAsTextFile()方法将RDD的数据存入指定文件夹中
# 该方法会将所有数据分到12个线程(cpu硬件决定)中,将数据随机分散的方法12个文件中
# 如果文件夹已存在会报错,提升文件夹已存在
rdd.saveAsTextFile('./订单数据/RDD数据')

# 关闭sc
sc.stop()

105.PySpark综合练习

from pyspark import SparkConf, SparkContext
import os
import json
# 指定Python解释器的位置
os.environ['PYSPARK_PYTHON'] = 'D:/App/Python/Python310/python.exe'
# 指定Hadoop的安装路径
os.environ['HADOOP_HOME'] = 'D:\App\Hadoop\hadoop-3.0.0'

# 创建一个配置对象
conf = SparkConf().setMaster("local[*]").setAppName("test_spark_app")

# 设置全局并行度为1,让工作全由一个线程去完成
# 作用是让RDD对象执行saveAsTextFile()方法时将所有数据存入一个文件中
conf.set('spark.default.parallelism', '1')

# 创建一个sc对象
sc = SparkContext(conf=conf)

# 获得一个RDD对象
rdd = sc.textFile("D:/App Data/PythonProject/Python-learn/搜索数据/search_log.txt")

# 需求一 获得每小时访问次数最多的时间段和其访问的次数取前三的数据
# 通过链式调用的方法完成
# 将每一行按制表符分割返回一个列表,然后存入列表中
# 将每条数据的小时精度存入元组的下标0中,将该条数据设为1存入元组的下标1中,将每条数据存入列表中,返回RDD对象
# 按key将所有数据统计,将key放入元组的下标0中,将每个小时的总访问次数存入元组的下标1中,将每个小时对应的元组存入列表中
# 对数据按元素元组的下标1(访问次数)降序排序
# 将前三的数据取出并转为Python数据
top3_list = rdd.\
    map(lambda x: (x.split('\t')[0][:2], 1)).\
    reduceByKey(lambda a, b: a + b).\
    sortBy(lambda x: x[1], ascending=False, numPartitions=1).\
    take(3)

# 需求二 获得热门搜索词前三的内容
# 将每条数据按照\t分割成列表,并将该列表中的搜索词放入元组中,元组下标1位置放入一个数字1表示一条数据
# 将RDD对象数据按key进行计算,算出每个搜索词的总搜索次数
# 对数据按元组的下标1降序排序
# 将前三的数据转为Python数据
top3_search = rdd.\
    map(lambda x: (x.split('\t')[2], 1)).\
    reduceByKey(lambda a, b: a + b).\
    sortBy(lambda x: x[1], ascending=False, numPartitions=1).\
    take(3)

# 需求三 统计黑马程序员关键字在什么时段被搜索的最多
# 将每条数据按照\t分割成列表,并取出小时存入元组中,再将搜索词等于指定字符的结果True1,False0存入元组
# 将RDD对象数据按key进行计算,算出每个小时搜索指定字符的次数
# 对RDD数据按搜所次数进行降序排序
# 取出第一条数据占位Python数据
heima_time_max = rdd.\
    map(lambda x: (x.split('\t')[0][:2], x.split('\t')[2] == '黑马程序员')).\
    reduceByKey(lambda a, b: a + b).\
    sortBy(lambda x: x[1], ascending=False, numPartitions=1).\
    take(1)


# print(heima_time_max)

# 需求四 将数据转为JSON格式,写出到文件中
rdd\
    .map(lambda x: x.split('\t')).\
    map(lambda x: {"time": x[0], "user_id": x[1], "key_word": x[2], "rank1": x[3], "rank2": x[4], "url": x[5]})\
    .saveAsTextFile("./搜索数据/search")
sc.stop()

106.闭包

概念:

  • 本质就是一个嵌套函数
  • 通过给外部函数传入参数
  • 内部函数获得这个参数,将这个参数当成变量使用
  • 外部函数再返回这个闭包函数
  • 使用外部函数传入参数,返回一个闭包函数
  • 这个闭包函数里面可以使用nonlocal关键字定义外部的参数
  • 然后对它进行修改,它的存在对于闭包函数来说是持续性的
  • 多次调用闭包函数可以不断的对象里面的外部参数进行修改
  • 也只能通过这个闭包函数进行修改

示例

# 定义一个外层函数
def outer(logo):  # 可以把这个外层参数当成一个变量,用于存储数据,避免被随意修改

    # 定义一个内层函数
    # 内层函数可以获得外层函数的参数
    def inner(msg):
        # 使用nonlocal关键字定义外部函数的参数
        # 使得可以对外部函数的参数进行修改
        nonlocal logo
        # 在内部函数修改外部函数传入的参数
        logo = 50
        print(f"{logo} + {msg}")
        return "完成"

    # 通过调用外层函数返回这个函数的内层函数,这个函数也叫闭包函数
    return inner


# 因为返回值是一个函数所有,可以使用连续传参
# result = outer('logo')(' world')
# 将一个参数传给外部函数,
# 内部函数获得这个参数,
# 并且这个参数是不可变的,
# 需要通过修改外部函数的参数值来修改这个获得的变量
# 或者通过在内部函数中修改这个外部参数,使用nonlocal关键字定义
fun_inner = outer('logo')  # 获得一个内部的函数,这个函数也叫闭包函数
result = fun_inner('内容')  # 给内层函数传参实现具体操作得到返回值

print(result)

闭包ATM机练习

# 使用闭包函数实现atm机存取款功能
def outer(account_amount=0):  # 外部函数传入一个参数决定原本账户的余额是多少,默认为0

    # 内部闭包函数传入两个参数第一个为存入或取出的金额,
    # 第二个参数决定是存款(True)还是取款(False),默认为False
    def atm(num, deposit=False):
        # 使用nonlocal关键字定义外部参数,
        # 使得可以在内部闭包函数中对它进行修改
        nonlocal account_amount

        # 判断传入的参数deposit为True,将钱款存入余额中
        # 判断传入的参数deposit为False,将余额中的钱进行相应减少
        if deposit:
            account_amount += num
            print(f'存入:{num}元,账户余额:{account_amount}')
        else:
            account_amount -= num
            print(f'取出:{num}元,账户余额:{account_amount}')

    return atm


# account_amount对于返回的闭包函数来说是独有的
# 相当于闭包函数外部的一个全局变量,但它不会被轻易修改
# 只能通过闭包函数的代码体,通过nonlocal关键字对这个account_amount进行修改
fun_atn = outer(2930)
fun_atn(430, False)
# 多次调用这个闭包函数可以持续对里面的account_amount 进行修改
fun_atn(200, True)

总结

什么是闭包

  • 定义双层嵌套函数,内部函数可以访问外层函数的变量
  • 将内存函数作为外层函数的返回,此内层函数就是闭包函数

闭包的好处和缺点

  • 优点:不定义全局变量,也可以让函数持续访问和修改一个外部变量
  • 优点:闭包函数引用的外部变量,是外层函数的内部变量,作用域封闭难以被误操作修改
  • 缺点:额外占用内存

nonlocal关键字的作用

  • 在闭包函数(内部函数)中想要修改外部函数的变量值
  • 需要使用nonlocal关键字声明这个外部变量外部函数传入的参数

107.装饰器

概念

  • 什么是装饰器
    • 装饰器就是创建一个闭包函数,在外层传入一函数作为参数
    • 在闭包函数调用这个参数函数(目标函数)
    • 可以达到不改动目标函数的同时,增加额外的功能

示例

# 定义一个装饰器(也闭包函数的一种)
# 它的外部函数需要传入一个,函数作为参数
# 然后通过内部函数对这个参数函数的前后进行代码的添加
# 或者对参数函数的返回的结果进行处理

def outer(func):

    def my_inner():
        # 在前参数函数前添加代码
        print("我要睡觉了")
        # 在中间执行参数函数
        result = func()
        # 在前参数函数后添加代码
        print(f"我起床了{result}")

    # 返回闭包函数
    return my_inner


# 定义一个睡眠函数
# @outer使用注释的方式(语法糖)
# 把sleep函数作为参数传入outer函数中
# 相当于:sleep = outer(sleep)
@outer
def sleep():
    import random
    import time
    print('睡眠中。。。')
    time.sleep(random.randint(1, 10))
    return "success"


# 调用装饰过后的sleep函数
# 就通过闭包的方式在前后添加新的代码,生成一个新的函数
sleep()

# # 底层原理
# # 通过调用 outer 并传入函数 sleep,生成了一个新的函数 inner(闭包函数)
# # 这个函数对sleep函数,的前后进行了代码的增加
# # 这就称为装饰器(装饰函数的前后)
# inner = outer(sleep)
# inner()

108.设计模式之单例模式

概念

什么是设计模式

  • 设计模式是一种编程套路,使用特定的套路得到特定的结果

什么是单例模式

  • 获取唯一的一个类实例对象,可以把这个对象赋值给多个变量
  • 每一个变量的内存地址都是相同的
  • 改变其中一个对象的属性,其他变量的属性也会改变
  • 他的优点就是节省存,持续反复使用一个对象

示例

# 文件一中的代码
class StrTool:
    num = 1
    name = 'Tom'

    def __str__(self):
        return f"{self.num},{self.name}"


str_tool = StrTool()  # 创建一个实例化对象

# 文件二中的代码
# 在别的文件中使用需要导入模块
# from test import str_tool

# 使用同一个对象赋值给两个变量,让着两个对象变量使用同一个内存地址
str_obj1 = str_tool
str_obj2 = str_tool

str_obj1.num = 5
str_obj1.name = 'Jack'

# obj1中的属性被改变了,obj2中的属性也会跟着改变,因为它们用的是同一个内存地址
print(str_obj1)
print(str_obj2)

109.设计模式之工厂模式

概念

  • 通过工厂函数去决定要创建的是什么对象
  • 这个工厂函数需要含有一个方法
  • 这个方法需要传入一个参数
  • 通过这个参数决定返回的是什么类
  • 然后通过这个类创建实例

使用工厂类的好处

  • 大批量创建对象的时候有统一的入口,易于代码维护
  • 当发生修改,仅修改工厂类的创建方法即可
  • 符合现实世界的模式,即有工厂制作产品(对象)
  • 如果要升级产品(对象),只需要对象工厂类的方法进行修改就行了

示例

# 顶级的抽像类
class Person:
    pass


# 子类继承父类,并实现抽象方法
class Student(Person):
    pass


# 子类继承父类,并实现抽象方法
class Teacher(Person):
    pass


# 子类继承父类,并实现抽象方法
class Worker(Person):
    pass


# 创建一个工厂类
class PersonFactory:

    # 工厂类的方法决定要生成的类别
    # 静态方法装饰器 (声明这是一个静态方法)
    # 它们主要用于执行与类本身相关的操作
    # 或者在不需要访问实例变量的情况下执行某些功能
    # 静态方法不能访问或修改实例的状态
    # 可以使用 类名.属性名 或 类名.方法名() 的方式使用自身的属性和方法
    # 静态方法也不需要传入self参数
    @staticmethod
    def get_person(p_type):
        if p_type == 's':
            return Student()
        elif p_type == 't':
            return Teacher()
        else:
            return Worker()
        
# 给工厂函数的get_person方法传入一个参数决定要创建的实例对象
# 虽然我们可以通过实例对象来调用静态方法
# 但这样做是不推荐的,因为静态方法是与类相关联的,而不是与实例相关联的。最好的做法是使用类名来调用静态方法
person = PersonFactory.get_person('s')
print(person)

110.静态方法

概念

  • 静态方法装饰器 @staticmethod (声明这是一个静态方法)
  • 它们主要用于执行与类本身相关的操作
  • 或者在不需要访问实例变量的情况下执行某些功能
  • 静态方法不能访问或修改实例的状态
  • 可以使用 类名.属性名 或 类名.方法名() 的方式使用自身的属性和方法
  • 静态方法也不需要传入self参数

示例

# 创建一个工厂类
class Factory:

    # 工厂类的方法决定要生成的类别
    # 静态方法装饰器 (声明这是一个静态方法)
    # 它们主要用于执行与类本身相关的操作
    # 或者在不需要访问实例变量的情况下执行某些功能
    # 静态方法不能访问或修改实例的状态
    # 可以使用 类名.属性名 或 类名.方法名() 的方式使用自身的属性和方法
    # 静态方法也不需要传入self参数
    @staticmethod
    def get_person(p_type):
        if p_type == 's':
            return Student()
        elif p_type == 't':
            return Teacher()
        else:
            return Worker()

111.多线程并行执行

概念

  • 进程:就是一个程序,运行在系统上,我们称之为运行进程,并分配进程ID
  • 线程:线程是归属于进程的,一个进程可开启多个线程,是进程的实际工作单位
  • 操作系统中可以运行多个进程,即多任务运行
  • 一个进程内可以运行多个线程,即多线程运行
  • 进程之间是内存隔离的,即不同的进程拥有各自的内存空间
  • 并行执行的意思就是同一时间做不同的工作
  • 进程之间就是并行执行
  • 另外线程也是可以并行执行

线程池的使用

import time
# 导入线程池类
from multiprocessing.dummy import Pool

# 记录开始时间戳 (自1970年1月1日以来的秒数)
start_time = time.time()


# 定义一个函数用于对列表中的元素进行操作
def get_ele(ele):
    print(f"正在请求{ele}的数据")
    time.sleep(2)  # 睡眠两妙
    print(f"{ele}请求成功")


# 所有需要请求数据的url
url_list = [
    'http://dfsfewg1.com',
    'http://dfsfewg2.com',
    'http://dfsfewg3.com',
    'http://dfsfewg4.com',
    'http://dfsfewg5.com'
]

# 实例化一个线程池
# 传入一个数字参数
# 表示线程池里面线程的个数 (不要设置太多也不要设置太少)
# 如果只传入一个数字1 ,那么和同步(按顺序阻塞)执行没区别
pool = Pool(6)
# 将列表中每个元素作为get_ele的参数,
# 再把这个方法传递给线程池里的空闲线程,
# 让他它们异步执行get_ele方法
# 线程池对象的map方法
# 参数一 传入线程要执行的函数
# 参数二 传入一个列表,列表中的元素类型要和传入的函数的参数类型一致
pool.map(get_ele, url_list)

# 传入lambda表示作为参数示例
# list_url = pool.map(lambda x: x.replace('http', 'https'), url_list)

# 执行完毕后关闭线程池
pool.close()
# 等待线程池中的所有任务执行完毕后,再执行主线程
# 注意:使用pool.join() 之前必须使用pool.close()或pool.shutdown()
# 否则可能会导致程序一直阻塞无法继续执行
pool.join()

# 获得结束时间戳
end_time = time.time()

# 查看程序运行耗费的时间
print(f"列表中的所有url发送请求一共用了{end_time-start_time}秒")

示例二

import requests
import re
import ffmpeg  # 合并音频和视频的模块
import subprocess  # 进程模块,用于执行cmd命令
import json
from pprint import pprint  # 格式化输入数据的模块
import os
import concurrent.futures
# 导入线程池类
from multiprocessing.dummy import Pool


def bilibili_video(args):
    # 获得线程池传过来的参数,请求头必须放在后面
    bvid, v_title, header = args
    # 定义要删除的特殊字符的正则表达式模式
    pattern = r'[^\w\s]'

    # 使用正则表达式将特殊字符替换为空
    v_title = re.sub(pattern, '', v_title)
    print(v_title)
    # 请求地址
    url = f'https://www.bilibili.com/video/{bvid}/?spm_id_from=333.1073.channel.secondary_floor_video.click&vd_source=c3d1a960498e216c84464e70f74626d6'

    # 发送请求
    response = requests.get(url=url, headers=header)

    # 获取服务器返回的响应文本数据
    html = response.text

    # 获得视频标题, 并去除空格
    title = re.findall(r'property="og:title" content="(.*?)_哔哩哔哩_bilibili', html)[0].replace(' ', '')

    # 提示视频信息
    video_info = re.findall('<script>window.__playinfo__=(.*?)</script>', html)[0]

    # json字符串转为json字典数据
    json_dict = json.loads(video_info)

    # 格式化输出 json数据
    # pprint(json_dict)

    # 提取音频连接
    audio_url = json_dict['data']['dash']['audio'][0]['baseUrl']

    # 提取视频链接
    video_url = json_dict['data']['dash']['video'][0]['baseUrl']

    print(audio_url)
    print(video_url)

    # 获取视频和音频的二进制数据
    audio_content = requests.get(url=audio_url, headers=headers).content
    video_content = requests.get(url=video_url, headers=headers).content

    # 保存音频保存到本地MP3文件中
    with open('video/' + v_title + '.mp3', mode='wb') as audio:
        audio.write(audio_content)

    # 保存视频频保存到本地MP4文件中
    with open('video/' + v_title + '.mp4', mode='wb') as video:
        video.write(video_content)

    cmd = f'ffmpeg -i video/{v_title}.mp4 -i video/{v_title}.mp3 -c:v copy -c:a aac -strict experimental video/full_{v_title}.mp4'

    # 运行cmd命令,合并视频和音频
    subprocess.run(cmd)

    # 删除无用的音频和视频
    os.remove('video/' + v_title + '.mp3')
    os.remove('video/' + v_title + '.mp4')


# 爬取多个视频
# 请求的URL,从博主视频列表f12 点击 XHR 异步请求数据,点击search 的请求,从标头获取前半部分请求地址,从payload获得参数
# link = 'https://api.bilibili.com/x/space/wbi/arc/search'
link = 'https://api.bilibili.com/x/space/wbi/arc/search?mid=30502823&ps=30XXX'

headers = {
    # 防盗链,合并视频会用到,否则获取不到数据
    'Referer': 'https://space.bilibili.com/30502823/video',
    # 伪装浏览器发送请求
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
    # 用户登录信息,获取高清视频
    "Cookie": "XXX",
}


link_data = requests.get(url=link, headers=headers).json()
# print(link_data)

params_list = []
for item in link_data['data']['list']['vlist']:
    # 将所有的参数封装为一个元组并存入参数列表中
    params_list.append((item['bvid'], item['bvid'], headers))

# 开启十个线程
pool = Pool(10)

# 要并行执行的函数和函数所需的参数,一个列表每个元素是一次方法的参数
pool.map(bilibili_video, params_list)

# 关闭线程池
pool.close()

# 链接其他代码
pool.join()

112.多线程编程

多线程编程步骤

  • 想要实现多线程编程需要导入threading模块 import threading

  • 通过threading的Thread方法创建实例对象,构建一个线程

  • Thread方法可以传入的参数

  • target=sing 执行的目标函数名 (必须传入)

  • args=(目标函数参数值1, 目标函数参数值2) 以元组的方式给执行任务传参

  • kwargs={‘目标函数参数名1’: 值, ‘目标函数参数名2’: 值} 以字典的方式给执行任务传参

  • name=‘线程名’ 一般不用设置

  • 可以使用这个方法构建多个线程

  • 通常实例对象的start()方法让线程开始工作

示例

import time
import threading


def sing(msg):
    while True:
        print(msg)
        time.sleep(1)


def dance(msg):
    while True:
        print(msg)
        time.sleep(1)


# 通过threading类获得一个线程实例对象
# 目标函数sing有多少个参数就传入多少个元素,它们会一一对应
threading_obj = threading.Thread(target=sing, args=('唱歌中', ))
# 通过实例对象的start()方法执行线程任务
threading_obj.start()


# 通过threading类获得一个线程实例对象
# kwargs参数传入一个字典类型的数据,key表示dance的参数名,Value表示对应的值
# 目标函数有多少个参数就传入多少键值对
threading_obj1 = threading.Thread(target=dance, kwargs={'msg': '跳舞中'})
# 通过实例对象的start()方法执行线程任务
threading_obj1.start()

113.Socket服务端开发

Socket概念

  • Socket简称套接字,是进程之间通信的一个工具
  • 进程之间想要进行网络通信需要使用是Socket
  • Socket负责进程之间的网络数据传输,就好比数据的搬运工

Socket服务端(相当于客服)

  • 等待其它进程的连接,可以接收消息、可以回复消息
  • 就像客服,等待客户的提问,然后回复解决方案

Socket客户端(相当于客户)

  • 主动连接服务端、可以发送消息、可以接收回复消息
  • 就像客户主动去问客服各种问题,然后接收客服的回复消息

服务端创建步骤

  • 1.导入socket包 import socket
  • 2.通过scoket模块的socket()方法 创建Socket对象
  • socket_server = socket.socket()
  • 3.通过socket对象的bind()方法绑定IP地址和端口
  • 需要传入一个元组,第一个元素为IP地址,第二个元素为端口号
  • socket_server.bind(('ip地址', 端口号))
  • 4.监听连接的数量
  • socket_server.listen(1)
  • 5.使用socket对象的accept方法 等待连接
  • conn, address = socket_server.accept()
  • 6.接收信息并解码
  • data: str = conn.recv(1024).decode('UTF-8')
  • 7.发送消息
  • conn.send(msg.encode("UTF-8"))
  • 8.关闭本次连接和Socket连接 conn.close()``````socket_server.close()

示例

# Socket服务端开发
import socket
import threading

socket_server = socket.socket()
socket_server.bind(('localhost', 8888))
# 监听连接的数量
socket_server.listen(1)
# 使用socket对象的accept方法 等待连接
# 该方法返回值是一个二元元组,可以使用直接使用两个变量进行接收
# 第一个元素是服务端和客户端的本次连接对象,第二个元素是客户端的信息
# 该方法是一个阻塞方法,如果没有客户端连接,会一直在这里等待,不会向下执行
conn, address = socket_server.accept()

print(f"\n客户端信息是:{address}")

# 循环接收和回复客户端的消息
while True:
    # 使用本次连接对象conn的recv方法接收客户端信息
    # recv方法需要接收一个int参数,表示缓冲区大小,一般给1024
    # recv也是一个阻塞方法
    # 它的返回值是一个bytes对象,可以使用decode()方法对它进行解码
    # 然后使用decode()方法对数据进行解码,转为字符串对象
    data: str = conn.recv(1024).decode('UTF-8')

    print(f"\n客户端发来的消息是:{data}")

    # 发送回复消息
    # 接收键盘输入的消息,使用encode("UTF-8")方法将字符串编码成bytes字节对象
    msg = input('\n请输入你的和客户端回复的消息:')
    if msg == 'exit':
        break
    # 通过本次连接对象conn的send方法发送回复的消息
    conn.send(msg.encode("UTF-8"))

# 关闭本次连接
conn.close()

# 关闭socket连接
socket_server.close()

114.socket客户端开发

客户端开发

  • 导入socket包 import socket
  • 通过scoket模块的socket()方法 创建Socket对象
  • socket_client = socket.socket()
  • 通过socket对象的connect()方法链接到服务器IP地址和端口
  • 需要传入一个元组,第一个元素为IP地址,第二个元素为端口号
  • socket_client.connect(('ip地址', 端口号))
  • 先发送消息 socket_client.send(send_msg.encode("UTF-8"))
  • 后回复消息 recv_data = socket_client.recv(1024)
  • 关闭Socket连接 socket_client.close()

示例

# Socket服务端开发
import socket
import threading
# Socket客户端开发
# import socket
# 创建Socket对象
socket_client = socket.socket()

# 连接到服务端
socket_client.connect(('localhost', 8888))

# 循环接收键盘输入的消息,并循环发送
while True:
    send_msg = input("\n请输入要发送的消息:")
    if send_msg == 'exit':
        break

    # 将消息进行编码并发送
    socket_client.send(send_msg.encode("UTF-8"))

    # 回复服务端发来的消息
    recv_data = socket_client.recv(1024)
    print(f"服务端回复消息为:{recv_data.decode('UTF-8')}")

# 关闭socket连接
socket_client.close()

115.多线程和Socket综合案例

# Socket服务端开发
import socket
import threading


# 服务端配置和开发
def server():
    socket_server = socket.socket()
    socket_server.bind(('localhost', 8888))
    # 监听连接的数量
    socket_server.listen(1)
    # 使用socket对象的accept方法 等待连接
    # 该方法返回值是一个二元元组,可以使用直接使用两个变量进行接收
    # 第一个元素是服务端和客户端的本次连接对象,第二个元素是客户端的信息
    # 该方法是一个阻塞方法,如果没有客户端连接,会一直在这里等待,不会向下执行
    conn, address = socket_server.accept()

    print(f"\n客户端信息是:{address}")

    # 循环接收和回复客户端的消息
    while True:
        # 使用本次连接对象conn的recv方法接收客户端信息
        # recv方法需要接收一个int参数,表示缓冲区大小,一般给1024
        # recv也是一个阻塞方法
        # 它的返回值是一个bytes对象,可以使用decode()方法对它进行解码
        # 然后使用decode()方法对数据进行解码,转为字符串对象
        data: str = conn.recv(1024).decode('UTF-8')

        print(f"\n客户端发来的消息是:{data}")

        # 发送回复消息
        # 接收键盘输入的消息,使用encode("UTF-8")方法将字符串编码成bytes字节对象
        msg = input('\n请输入你的和客户端回复的消息:')
        if msg == 'exit':
            break
        # 通过本次连接对象conn的send方法发送回复的消息
        conn.send(msg.encode("UTF-8"))

    # 关闭本次连接
    conn.close()

    # 关闭socket连接
    socket_server.close()


# 客户端配置和开发
def client():
    # Socket客户端开发
    # import socket
    # 创建Socket对象
    socket_client = socket.socket()

    # 连接到服务端
    socket_client.connect(('localhost', 8888))

    # 循环接收键盘输入的消息,并循环发送
    while True:
        send_msg = input("\n请输入要发送的消息:")
        if send_msg == 'exit':
            break

        # 将消息进行编码并发送
        socket_client.send(send_msg.encode("UTF-8"))

        # 回复服务端发来的消息
        recv_data = socket_client.recv(1024)
        print(f"服务端回复消息为:{recv_data.decode('UTF-8')}")

    # 关闭socket连接
    socket_client.close()


# 使用多线程,在一台电脑上模拟服务端和客户端的连接
threading1 = threading.Thread(target=server)
threading2 = threading.Thread(target=client)

# 分别运行
threading1.start()
threading2.start()

116.正则表达式

Python的正则表达式

  • 在Python中使用正则的步骤
  • 导入正则表达式的模块 import re

re模块的match方法

import re   # 导入正则表达式模块

# 使用re模块的match()方法对字符串进行匹配

my_str = 'ab_student_tu'

# 传入两个参数参数
# 第一个是匹配的规则
# 第二个是需要验证的字符串
# 返回值是一个Match对象
# 从头开始匹配,如果不匹配直接返回None
result = re.match('ab', my_str)

# 通过Match对象span()方法取得匹配的开头和结尾的下标+1,并存入元组中
print(result.span())
# 通过Match对象的group方法获得匹配的字符串
print(result.group())

正则表达式 search方法

import re   # 导入正则表达式模块

# 使用re模块的match()方法对字符串进行匹配

my_str = 'ab_student_tu'

# 传入两个参数参数
# 第一个是匹配的规则
# 第二个是需要验证的字符串
# 返回值是一个Match对象
# 从头开始匹配,直到找到第一个匹配的字符串为止
result = re.search('tu', my_str)

# 通过Match对象span()方法取得匹配的开头和结尾的下标+1,并存入元组中
print(result.span())
# 通过Match对象的group方法获得匹配的字符串
print(result.group())

正则表达式 findall方法

import re   # 导入正则表达式模块

# 使用re模块的match()方法对字符串进行匹配

my_str = 'ab_student_tu'

# 传入两个参数参数
# 第一个是匹配的规则
# 第二个是需要验证的字符串
# 返回值是一个列表,列表里面是所有符合规则的字符串
# 将所有符合规则的字符串存入列表中并返回
result = re.findall('tu', my_str)

print(result)

117.正则表达式匹配规则

单字符匹配功能
.匹配任意1个字符(除了\n), \. 匹配点本身
[ a-zA-Z ]匹配[ ] 中列举的字符,匹配一个字符
\d一个杠加小写d表示匹配0 - 9 的数字
\D一个杠加大写D表示匹配非数字的字符
\s匹配空白,即空格、tab键
\S匹配非空白
\w匹配单词字符,即 a - Z 、0 - 9 、_
\W匹配非单词字符
次数匹配功能
*匹配前一个规则的字符出现0次或无数次
+匹配前一个规则的字符出现1次或无数次
匹配前一个规则的字符出现0次或1次
{m}匹配前一个规则的字符出现m次
{m, }匹配前一个规则的字符出现最少m次
{m, n}匹配前一个规则的字符出现m到n次
边界匹配功能
^匹配字符串开头,如果是在[ ] 里面表示取反
$匹配字符串结尾
\b匹配一个单词的边界
\B匹配非单词边界
分组匹配功能
|匹配左右任意一个表达式
( )将括号中字符作为一个分组

邮箱的正则

import re   # 导入正则表达式模块

# 使用re模块的match()方法对字符串进行匹配

my_str = 'dfegdsgsd@qq.com'

# 传入两个参数参数
# 第一个是匹配的规则
# 第二个是需要验证的字符串
# 返回值是一个列表,列表里面是所有符合规则的字符串
# 将所有符合规则的字符串存入列表中并返回
result = re.match(r'(^[\w-]{5,}@[a-zA-Z]{2,5}\.([a-zA-Z]{2,5})$)', my_str)  # 字符串前面写一个r 表示转义字符 \ 无效

print(result)

118. os模块的方法

要使用os模块需要先导入 import os

os模块的 listdir() 方法,将文件夹中的所有文件和文件夹存入列表中返回

import os
# listdir方法,会将文件中所有文件名和文件夹名都存入一个列表中并返回这个列表
list_dir: list = os.listdir(r'C:\Users\admin\Desktop\test')
print(list_dir)

os模块的实例变量path的 isdir() 方法

import os
# 判断该路径是文件还是文件夹,如果是文件夹返回True
print(os.path.isdir(r'C:\Users\admin\Desktop\test'))

os模块的实例变量path的 exists() 方法

import os
# 判断传入的路径是否存在
print(os.path.exists(r'C:\Users\admin\Desktop\test\r'))

119.递归

概念

  • 什么是递归?

    • 递归在编程中是一种非常重要的算法
    • 即方法(函数)自己调用自己的一种特殊编程写法,称之为递归调用
    • 在满足条件的情况下,函数自己调用自己的一种特殊编程技巧
    • 在函数中调用函数必须要一个结束的条件判断
    • 在某一个节点下不再调用自身,然后依次返回给上一个函数
  • 典型的递归应用场景找出一个文件中的全部的文件

    • 创建一个函数判断一个文件中的内容有哪些
    • 如果是文件,就把文件收集起来
    • 如果是文件夹,就在这个函数内调用自身并把这个文件夹的路径传进去
    • 如此往复,直到没有文件夹为止,然后把每个文件夹的文件返回给上一层
    • 最终得到文件夹中所有的文件
  • 示例,将一个文件夹下的文件都存入列表中

    • 导入os模块 import os 非必须,示例需要
    • 写一个函数
    • 在这个函数中判断某个条件下调用自身,某个条件不再调用
import os

def get_filer_recursion_from_dir(path):
    # 判断传入的路径是否存在
    if os.path.exists(path):
        # 列出当前路径下的所有文件夹和文件,并存入列表中
        list_file = os.listdir(path)
        # 定义一个空列表,用于存储所有的文件
        result_list = list()
        # 循环判断列表中的名称是否为文件夹
        for item in list_file:
            # 通过原路径拼接当前名称得到该文件或文件夹的路径
            if os.path.isdir(path + '/' + item):
                # 在函数中调用函数必须要一个结束的条件判断,比如这个,如果当前路径不是文件夹的时候则不会,继续调用自身,而是执行else的内容

                # 如果是文件夹则调用自身,返回一个列表类型
                # 该列表里存储的都是文件,可以直接批量添加到结果列表中
                inner_list = get_filer_recursion_from_dir(path + '/' + item)
                # 最后一个文件夹中都是文件所以直接拼接到结果集中
                result_list.extend(inner_list)
            else:
                # 将所有不为文件夹的文件名称添加到结果集中
                result_list.append(item)
        # 返回文件夹中所有的文件
        return result_list
    else:
        # 如果传入的路径不存在侧返回一个空列表
        print(f"指定的目录{path},不存在")
        return []


result = get_filer_recursion_from_dir(r'C:\Users\admin\Desktop\test')
print(result)

示例二

def my_recursion(num = 1):
    new_num = 0
    if num < 7:
        p_num = num + 2
        # 最后一次调用返回 0 + 7
        # 倒数第二次调用返回 5 + 7 = 12
        # 当前调用返回 3 + 12 = 15
        # 最终为 0 + 15
        new_num = my_recursion(p_num)
        new_num += p_num
        
    # 返回最终结果15
    return new_num

print(my_recursion())

ket客户端开发
# import socket
# 创建Socket对象
socket_client = socket.socket()

# 连接到服务端
socket_client.connect(('localhost', 8888))

# 循环接收键盘输入的消息,并循环发送
while True:
    send_msg = input("\n请输入要发送的消息:")
    if send_msg == 'exit':
        break

    # 将消息进行编码并发送
    socket_client.send(send_msg.encode("UTF-8"))

    # 回复服务端发来的消息
    recv_data = socket_client.recv(1024)
    print(f"服务端回复消息为:{recv_data.decode('UTF-8')}")

# 关闭socket连接
socket_client.close()

使用多线程,在一台电脑上模拟服务端和客户端的连接

threading1 = threading.Thread(target=server)
threading2 = threading.Thread(target=client)

分别运行

threading1.start()
threading2.start()


## 116.正则表达式

Python的正则表达式

- 在Python中使用正则的步骤
- 导入正则表达式的模块  ```import re```

re模块的match方法

```Python
import re   # 导入正则表达式模块

# 使用re模块的match()方法对字符串进行匹配

my_str = 'ab_student_tu'

# 传入两个参数参数
# 第一个是匹配的规则
# 第二个是需要验证的字符串
# 返回值是一个Match对象
# 从头开始匹配,如果不匹配直接返回None
result = re.match('ab', my_str)

# 通过Match对象span()方法取得匹配的开头和结尾的下标+1,并存入元组中
print(result.span())
# 通过Match对象的group方法获得匹配的字符串
print(result.group())

正则表达式 search方法

import re   # 导入正则表达式模块

# 使用re模块的match()方法对字符串进行匹配

my_str = 'ab_student_tu'

# 传入两个参数参数
# 第一个是匹配的规则
# 第二个是需要验证的字符串
# 返回值是一个Match对象
# 从头开始匹配,直到找到第一个匹配的字符串为止
result = re.search('tu', my_str)

# 通过Match对象span()方法取得匹配的开头和结尾的下标+1,并存入元组中
print(result.span())
# 通过Match对象的group方法获得匹配的字符串
print(result.group())

正则表达式 findall方法

import re   # 导入正则表达式模块

# 使用re模块的match()方法对字符串进行匹配

my_str = 'ab_student_tu'

# 传入两个参数参数
# 第一个是匹配的规则
# 第二个是需要验证的字符串
# 返回值是一个列表,列表里面是所有符合规则的字符串
# 将所有符合规则的字符串存入列表中并返回
result = re.findall('tu', my_str)

print(result)

117.正则表达式匹配规则

单字符匹配功能
.匹配任意1个字符(除了\n), \. 匹配点本身
[ a-zA-Z ]匹配[ ] 中列举的字符,匹配一个字符
\d一个杠加小写d表示匹配0 - 9 的数字
\D一个杠加大写D表示匹配非数字的字符
\s匹配空白,即空格、tab键
\S匹配非空白
\w匹配单词字符,即 a - Z 、0 - 9 、_
\W匹配非单词字符
次数匹配功能
*匹配前一个规则的字符出现0次或无数次
+匹配前一个规则的字符出现1次或无数次
匹配前一个规则的字符出现0次或1次
{m}匹配前一个规则的字符出现m次
{m, }匹配前一个规则的字符出现最少m次
{m, n}匹配前一个规则的字符出现m到n次
边界匹配功能
^匹配字符串开头,如果是在[ ] 里面表示取反
$匹配字符串结尾
\b匹配一个单词的边界
\B匹配非单词边界
分组匹配功能
|匹配左右任意一个表达式
( )将括号中字符作为一个分组

邮箱的正则

import re   # 导入正则表达式模块

# 使用re模块的match()方法对字符串进行匹配

my_str = 'dfegdsgsd@qq.com'

# 传入两个参数参数
# 第一个是匹配的规则
# 第二个是需要验证的字符串
# 返回值是一个列表,列表里面是所有符合规则的字符串
# 将所有符合规则的字符串存入列表中并返回
result = re.match(r'(^[\w-]{5,}@[a-zA-Z]{2,5}\.([a-zA-Z]{2,5})$)', my_str)  # 字符串前面写一个r 表示转义字符 \ 无效

print(result)

118. os模块的方法

要使用os模块需要先导入 import os

os模块的 listdir() 方法,将文件夹中的所有文件和文件夹存入列表中返回

import os
# listdir方法,会将文件中所有文件名和文件夹名都存入一个列表中并返回这个列表
list_dir: list = os.listdir(r'C:\Users\admin\Desktop\test')
print(list_dir)

os模块的实例变量path的 isdir() 方法

import os
# 判断该路径是文件还是文件夹,如果是文件夹返回True
print(os.path.isdir(r'C:\Users\admin\Desktop\test'))

os模块的实例变量path的 exists() 方法

import os
# 判断传入的路径是否存在
print(os.path.exists(r'C:\Users\admin\Desktop\test\r'))

119.递归

概念

  • 什么是递归?

    • 递归在编程中是一种非常重要的算法
    • 即方法(函数)自己调用自己的一种特殊编程写法,称之为递归调用
    • 在满足条件的情况下,函数自己调用自己的一种特殊编程技巧
    • 在函数中调用函数必须要一个结束的条件判断
    • 在某一个节点下不再调用自身,然后依次返回给上一个函数
  • 典型的递归应用场景找出一个文件中的全部的文件

    • 创建一个函数判断一个文件中的内容有哪些
    • 如果是文件,就把文件收集起来
    • 如果是文件夹,就在这个函数内调用自身并把这个文件夹的路径传进去
    • 如此往复,直到没有文件夹为止,然后把每个文件夹的文件返回给上一层
    • 最终得到文件夹中所有的文件
  • 示例,将一个文件夹下的文件都存入列表中

    • 导入os模块 import os 非必须,示例需要
    • 写一个函数
    • 在这个函数中判断某个条件下调用自身,某个条件不再调用
import os

def get_filer_recursion_from_dir(path):
    # 判断传入的路径是否存在
    if os.path.exists(path):
        # 列出当前路径下的所有文件夹和文件,并存入列表中
        list_file = os.listdir(path)
        # 定义一个空列表,用于存储所有的文件
        result_list = list()
        # 循环判断列表中的名称是否为文件夹
        for item in list_file:
            # 通过原路径拼接当前名称得到该文件或文件夹的路径
            if os.path.isdir(path + '/' + item):
                # 在函数中调用函数必须要一个结束的条件判断,比如这个,如果当前路径不是文件夹的时候则不会,继续调用自身,而是执行else的内容

                # 如果是文件夹则调用自身,返回一个列表类型
                # 该列表里存储的都是文件,可以直接批量添加到结果列表中
                inner_list = get_filer_recursion_from_dir(path + '/' + item)
                # 最后一个文件夹中都是文件所以直接拼接到结果集中
                result_list.extend(inner_list)
            else:
                # 将所有不为文件夹的文件名称添加到结果集中
                result_list.append(item)
        # 返回文件夹中所有的文件
        return result_list
    else:
        # 如果传入的路径不存在侧返回一个空列表
        print(f"指定的目录{path},不存在")
        return []


result = get_filer_recursion_from_dir(r'C:\Users\admin\Desktop\test')
print(result)

示例二

def my_recursion(num = 1):
    new_num = 0
    if num < 7:
        p_num = num + 2
        # 最后一次调用返回 0 + 7
        # 倒数第二次调用返回 5 + 7 = 12
        # 当前调用返回 3 + 12 = 15
        # 最终为 0 + 15
        new_num = my_recursion(p_num)
        new_num += p_num
        
    # 返回最终结果15
    return new_num

print(my_recursion())
  • 15
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Vermouth-1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值