Python

1.基础知识

目录:

快捷键:

1.变量

2.标识符

3.常量

4.注释

5.编码

6.数据类型

7.字符串的4种定义方式

8.字符串格式化输出

9.转义字符

10.数据类型转换

11.数学运算符

12.赋值运算符

13.比较运算符

14.布尔类型

15.逻辑运算符

16.进制

17.输入 input()

python 复制代码
# --------------------------------------
# 目录:
# 快捷键:
# 1.变量
# 2.标识符
# 3.常量
# 4.注释
# 5.编码
# 6.数据类型
# 7.字符串的4种定义方式
# 8.字符串格式化输出
# 9.转义字符
# 10.数据类型转换
# 11.数学运算符
# 12.赋值运算符
# 13.比较运算符
# 14.布尔类型
# 15.逻辑运算符
# 16.进制
# 17.输入 input()


# 快捷键:
#  1 Ctrl + S 全部保存
#  2 Ctrl + F 在本页面查询输入内容  ESC关闭
#  3 Ctrl + R 在本页面查询并替换内容 ESC关闭
#  4 Ctrl + G 跳转到指定行
#  5 Ctrl + / 注释本行,再次按,取消注释
#  6 Ctrl + y  删除本行
#  7 Alt + Ctrl + Enter 光标指向当前行的上一行(生成新的空行)
#  8 Shift + Enter  光标指向当前行的下一行(生成新的空行)
#  9 Ctrl + Alt + ↓  快速复制当前行到下一行中
#  10 shift + Esc 快速关闭控制台 (光标要在控制台或输入区域中)
#  11 Alt + 1 收起或展开项目栏
#  12 Shift + F10 运行控制台中上一次的运行
#  13 Ctrl + Shift + F10 运行当前页面中的代码
#  13.1 Shift + F9 以DeBug模式运行当前页面中的代码
#  14 按住Ctrl + 函数名 快速跳转到函数体上
#  15 Alt + F1 + 1 (项目视图)  打开/定位到 当前文件-关联到的项目视图
#  15.1 Alt + F1 + 3 (文件结构)  打开/定位到 当前文件-关联到的文件结构
#  15.2 Alt + F1 + 8 (在资源管理器中显示)  打开/定位到 当前文件-关联到的文件所在磁盘的目录中


# 插件
# 1. Key Promoter X
# 2. CodeGlance
# 3. Rainbow Brackets


#coding=utf-8
#上面为文档编注释,Python3中可以不写。默认是utf-8

"""文档字符串 eq: 在该文件中,主要学习Python的基础语法"""
import sys

'张三'
18
print(25)
print('张三的体重是66kg')
print('张三的体重是',66)

# 1.变量
name = '张三'
age = 18
weight = 66
print(name,age,weight)
weight = 67
print(name,'的体重是',weight,'kg')
print('------------------1')

# 2.标识符
# 第一个字符必须以字母(a-z, A-Z)或下划线 _ 。
# 标识符的其他的部分由字母、数字和下划线组成。
# 标识符对大小写敏感,count 和 Count 是不同的标识符。
# 标识符对长度无硬性限制,但建议保持简洁(一般不超过 20 个字符)。
# 禁止使用保留关键字,如 if、for、class 等不能作为标识符。
name = '熊大'
Name  = '熊二'
age_ = 18
_age_ =19
# 错 if = 10  标识符不要与内置函数同名
# ['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif',
#  'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or',
#  'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']
# 不报错,但不对 print = '熊三'

userName  = '张三'  # 小驼峰写法
UserName = '张三'   # 大驼峰写法
user_name = '张三'  # 蛇形写法 - Python推荐写法
print('------------------2')

# 3.常量 - 一旦被赋值,不希望被修改(区别于变量)
# Python一般约定使用全大写来表示常量,涉及多个单词时,用下划线做分割
# Python中没有强制的常量机制,Python的常量,本质还是变量,只不过我们约定好不去修改
AGE = 18
ADULT_AGE = 19
MAX_USERS = 1000
PASSING_SCORE = 60
MAX_USERS = 1100 # 常量可以被强制修改(不建议)
print(AGE, ADULT_AGE, MAX_USERS, PASSING_SCORE)
print('------------------3')

# 4.注释
# 单行注释:Python中单行注释以 # 开头
# 多行注释: 可以用多个 # 号,还有 ''' 和 """:
# 文件编码注释: 写在Python文件的开头,用于指定当前文件的字符编码。 #coding=utf-8

# 第一个单行注释
# 第二个注释

'''
第三多行注释
第四多行注释
'''

"""
第五多行注释
第六多行注释
"""

# 5.编码
# 默认情况下,Python3 源码文件以 UTF-8 编码,所有字符串都是 unicode 字符串。

# 6.数据类型
# 可以使用 type() 返回数据类型
# String(字符串) str String
# Number(数字)  int、float、bool、complex(复数)
# bool(布尔类型) True 或 False。
# List(列表)
# Tuple(元组)
# Set(集合)
# Dictionary(字典)

result = type('张三')
print(result) # <class 'str'>
print(type('18')) # <class 'str'>
print(type(18)) # <class 'int'>
print(type(18.2))  # <class 'float'>
print('------------------456')

# 整数(大整数分组)
# 整数,没有小数点的数字,可以是正数,负数,也可以是 0
age2  = 18
temp = -10
score = 0
# 当数很大时,我们可以使用下划线将数字进行分组,每3位一组,来让数字更易读
salary = 300_000
house_price = 3_200_000
graduates = 12_000_000
print(salary,house_price,graduates) # 300000 3200000 12000000
# Python中整数的上限值,取决于执行代码的计算机的内存和处理能力
a1 = 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999
print(a1)
a2 = 3 ** 2 # 3的2次方
print(a2) # 9
a3 = 9 ** 9999
# print(a3)
"""
Traceback (most recent call last):
  File "D:\python\ws\file1\p1.py", line 103, in <module>
    print(a3)
ValueError: Exceeds the limit (4300 digits) for integer string conversion; use sys.set_int_max_str_digits() to increase the limit
"""
# b2 = a3 + 1
# print(b2)
sys.set_int_max_str_digits(4300)

print('------------------61')
# 浮点型: 带有小数点的数字
w = 65.2
bal = 1426.58
out_temp = -12.7
price = 120.0
print(w,bal,out_temp,price)
print(type(w),type(bal),type(out_temp),type(price))
print('------------------62')
# 浮点型的科学计数法表示
speed_of_sound  = 3.4e+2 # 3.4乘以10的2次方
print(speed_of_sound,type(speed_of_sound)) # 340.0 <class 'float'>
world_population = 7.8e9 # 7.8乘以10的9次方
print(world_population,type(world_population)) # 7800000000.0 <class 'float'>
distance_sun_earth = 1.496E8  # 1.496乘以10的8次方
print(distance_sun_earth,type(distance_sun_earth)) # 149600000.0 <class 'float'>
speed_of_light = 2.998E+8 # 2.998乘以10的8次方
print(speed_of_light,type(speed_of_light)) # 299800000.0 <class 'float'>

one_ml = 1e-3 # 1乘以10的-3次方
print(one_ml,type(one_ml)) # 0.001 <class 'float'>
one_mg = 1E-3 # 1乘以10的-3次方
print(one_mg,type(one_mg)) # 0.001 <class 'float'>
print('------------------7')

# 7.字符串的4种定义方式
# 单引号和双引号的写法是等价的,二者都不能直接换行(要用圆括号才能换行),单引号用的多
msg1 = 'msg1'
msg11 = ('msg'
         '11')
msg2 = "msg2"
# 三个双引号的写法,可以直接运行,并且可以作为多行注释使用。还可以作为文档字符串使用。
msg3 = """msg3"""
# 三个单引号的写法,可以直接运行,并且可以作为多行注释使用。
msg4 = '''msg4'''
print(msg1, msg11, msg2, msg3, msg4)

a4 =  233_000 + 6684
print(f'233_000 + 6684 = {a4}')
print('------------------71')

# 8.字符串格式化输出
name = '张三'
gender = '男'
weight = 66.6
age = 12
# 写法1:直接用 + 进行拼接。写起来麻烦,而且只能是字符串之间进行拼接。
print('我叫' + name + ',我是' + gender)
# 写法2:使用占位符 %s 字符串  %f 浮点数 %i 整数 %d 十进制整数    万能占位符 %s
print('我叫%s,我是%s, 我的体重是%f,年龄是%i, 年龄是%d' % (name, gender, weight, age, age))
print('我叫%s,我是%s, 我的体重是%s,年龄是%s' % (name, gender, weight, age))
# 写法3:使用f-string,是目前Python最推荐的一种方式。
print( f'我叫{name},我是{gender}生,我体重是{weight},年龄是{age}')

print('------------------81')
# 8.1 字符串占位符精度控制
# %m.ns
# m 控制整体宽度(整体宽度=整数宽度+小数点+小数宽度)。位数不够空格来补,位数小于整体宽度,则自动生效。整数是右对齐,负数是左对齐。
# n 精度控制,保留n位小数(n的默认值是6),不够0来补,截断时会四舍五入
weight1 = 65.55
print('我叫%-4.1s,我是%3.2s生, 我的体重是%-9.3f,年龄是%6.4d' % (name, gender, weight1, age))
print('------------------9')

# 9.转义字符
# 使用 \' 输出 '
print('Python中,可以使用单引号包裹一个字符串')
print("Python中,可以使用'包裹一个字符串")
print('Python中,可以使用\'包裹一个字符串')
# 使用 \" 输出 "
print('Python中,可以使用双引号包裹一个字符串')
print("Python中,可以使用\"包裹一个字符串")
# 使用 \n 进行换行
print('注册会员需要以下信息: \n姓名\n年龄\n手机号')
# 使用 \\ 输出 \
print('D:\\ndoc') # D:\ndoc
# 使用 \b 删除前一个字符
print('helloo\b') # hello
# 使用 \r 使光标回到本行开头,覆盖之前的输出
print('67%\r68%') # 68%
# 使用 \t 水平制表符(让光标跳到下一个制表位)
#                    12341234
print('ab\tcd') #    ab  cd
print('abc\td') #    abc d
print('abcde\tfg') # abcde	fg
print('我\t是\t阜阳师范\t大学') # 我	是	阜阳师范	大学
print('------------------10')

# 10.数据类型转换
# 字符型、整型、浮点型
# 10.1 使用str将指定数据转换成字符串 str(...)
m1 = str(12)
m2 = str(12.1)
m3 = str(1.8e3)
m4 = str(12_0000)
print(m1, type(m1)) # 12 <class 'str'>
print(m2, type(m2)) # 12.1 <class 'str'>
print(m3, type(m3)) # 1800.0 <class 'str'>
print(m4, type(m4)) # 120000 <class 'str'>
# 10.2 把指定数据转换成整型 int(...)
n1 = int(15.6)
# n11 = int('15.6')
n2 = int('79')
n3 = int(' 79 ')
# n33 = int(' 7 9 ')
n4 = int(48)
print(n1, type(n1)) # 15 <class 'int'>
# print(n11, type(n11)) # 错误
print(n2, type(n2)) # 79 <class 'int'>
print(n3, type(n3)) # 79 <class 'int'>
# print(n33,type(n33)) # 错误
print(n4, type(n4)) # 48 <class 'int'>
# 10.3 将指定数据转为浮点数 float(...)
f1 = float(18)
f2 = float('15.6')
f3 = float(' 5.7  ')
f4 = float(14.8)
f5 = float('48')
print(f1, type(f1)) # 18.0 <class 'float'>
print(f2, type(f2)) # 15.6 <class 'float'>
print(f3, type(f3)) # 5.7 <class 'float'>
print(f4, type(f4)) # 14.8 <class 'float'>
print(f5, type(f5)) # 48.0 <class 'float'>
# 10.4 将指定内容转换为布尔类型 bool()   ( 映射14.1 )
print(bool(0))  # False
print(bool(1))  # True
# Python中除了0以外的任何数,转为布尔值后都为True
print(bool(1))  # True
print(bool(0))  # False
print(bool(-1)) # True
# Python中除了空字符串以外的任何字符串,转为布尔值都是True
print(bool('a'))  # True
print(bool(''))   # False
print(bool('-9')) # True
print('------------------11')

# 11.数学运算符
# 假设变量 a=10,变量 b=21:
# +	    加 - 两个对象相加	a + b 输出结果 31
# -	    减 - 得到负数或是一个数减去另一个数	a - b 输出结果 -11
# *	    乘 - 两个数相乘或是返回一个被重复若干次的字符串	a * b 输出结果 210
# /	    除 - x 除以 y	b / a 输出结果 2.1
# %	    取模 - 返回除法的余数	b % a 输出结果 1
# **	幂 - 返回x的y次幂	a**b 为10的21次方
# //	取整除 - 往小的方向取整数  9//2 = 4  -9//2 = -5

a = 21
b = 10
c = 0

c1 = a + b
print(c1)  # 31
c2 = a - b
print(c2)  # 11
c3 = a * b
print(c3)  # 210
c4 = a / b
print(c4)  # 2.1

c5 = a % b
print(c5)   # 1

# 修改变量 a 、b 、c
a = 2
b = 3
c6 = a ** b
print(c6)   # 8

a = 10.1
a1 = -10.1
b = 5
c6 = a // b
c7 = a1 // b
print(c6)  # 2.0
print(c7)  # -3.0
print('------------------12')

# 12.赋值运算符
# 假设变量a为10,变量b为20:
# =	    简单的赋值运算符	c = a + b 将 a + b 的运算结果赋值为 c
# +=	加法赋值运算符	c += a 等效于 c = c + a
# -=	减法赋值运算符	c -= a 等效于 c = c - a
# *=	乘法赋值运算符	c *= a 等效于 c = c * a
# /=	除法赋值运算符	c /= a 等效于 c = c / a
# %=	取模赋值运算符	c %= a 等效于 c = c % a
# **=	幂赋值运算符	c **= a 等效于 c = c ** a
# //=	取整除赋值运算符	c //= a 等效于 c = c // a
# :=	海象运算符,这个运算符的主要目的是在表达式中同时进行赋值和返回赋值的值。Python3.8 版本新增运算符。
# 在这个示例中,赋值表达式可以避免调用 len() 两次:
# if (n := len(a)) > 10:
#     print(f"List is too long ({n} elements, expected <= 10)")

a = 21
b = 10
c = 0
c = a + b
print(c)    # 31

c += a
print(c)    #52

c *= a
print(c)    # 1092

c /= a
print(c)    # 52.0

c21 = 2.1
a21 = 10
c21 %= a21
print(c21)  # 2.1

c31 = 2
a31 = 3
c31 **= a31
print(c31)  # 8

c41 = 20
a41 = 3
c41 //= a41
print(c41)  # 6
print('------------------13')

# 13.比较运算符
# 假设变量 a 为 10,变量 b 为20:
# ==	等于 - 比较对象是否相等	(a == b) 返回 False。
# !=	不等于 - 比较两个对象是否不相等	(a != b) 返回 True。
# >	大于 - 返回x是否大于y	(a > b) 返回 False。
# <	小于 - 返回x是否小于y。所有比较运算符返回1表示真,返回0表示假。这分别与特殊的变量True和False等价。注意,这些变量名的大写。	(a < b) 返回 True。
# >=	大于等于 - 返回x是否大于等于y。	(a >= b) 返回 False。
# <=	小于等于 - 返回x是否小于等于y。	(a <= b) 返回 True。

a = 21
b = 10
c = 0

print(a == b)   # False
print(a != b)   # True
print(a < b)    # False
print(a <= b)   # False
print(a >= b)   # True
print(a >= b)   # True
# 以上这些运算符,也适用于 字符串 比较
# Python中字符串进行比较时,是依次比较每个字符的Unicode编码
# eq: a Unicode 97    我 Unicode  25105
# Python中可以使用如下方式进行字符与Unicode编码之间的转换。
# ① 使用ord()查看字符Unicode码
# ② 使用chr()将Unicode码转为字符
print(ord('a'))     # 97
print(ord('我'))    # 25105
print(chr(97))      # a
print(chr(25105))   # 我
print('------------------14')

# 14.布尔类型  boolean: True False
# 之前学的:
# ① 字符型:string 'a'
# ② 整型:int 108
# ③ 浮点型:float 14.5
# ④ 布尔类型  boolean: True False
d1 = True
d2 = False
print(d1, type(d1))     # True <class 'bool'>
print(d2, type(d2))     # False <class 'bool'>
d3 = 5 > 3
print(d3, type(d3))     # True <class 'bool'>
d4 = 7 < 2
print(d4, type(d4))     # False <class 'bool'>
# 布尔类型是int类型的子类型,底层的本质是用1表示True,用0表示False
print(int(True))    # 1
print(int(False))   # 0
print(4 + True)     # 5
print(8 - True)     # 7
print(True + True)      # 2
print(True - True)      # 0
print(False + False)    # 0
print(True - False)     # 1
print(7 > True)         # True
print(False <= 0)       # True

# 14.1 转换函数 -->跳转10.4
print('------------------15')

# 15.逻辑运算符
# 假设变量 a 为 10, b为 20:

# and	x and y	布尔"与" -
# (如果两边都是True,则为True)
# (and 会先看左边,如果左边为 '假' 就直接返回左边,否则返回右边)如果 x 为 False,x and y 返回 x 的值,否则返回 y 的计算值。	(a and b) 返回 20。

# or	x or y	布尔"或" -
# (如果两边至少1个为True,则为True)
# 如果 x 是 True,它返回 x 的值,否则它返回 y 的计算值。	(a or b) 返回 10。

# not	not x	布尔"非" -
# (对一个字值反)
#(or 会先看左边,如果左边为 '真' 就直接返回左边,否则返回右边) 如果 x 为 True,返回 False 。如果 x 为 False,它返回 True。	not(a and b) 返回 False

print(True and True)    # True
print(True and False)   # False
print(True or False)    # True
print(not True)         # False
print(8 > 7 and 10 < 2) # False
e1 = 10
e2 = 20
print(e1 and e2)        # 20
print(2 - 2 and True)   # 0
print('' and True)      # ''
print(True and 8 / 2 )  # 4.0
print(3 + 3 and 3 * 4)  # 12

print(e1 or e2)             # 10
print(7 - 2 or False)       # 5
print('你好' or '老陈')      # '你好'
print(False or 8 / 2 )      # 4.0
print(2 - 2 or 3 * 4)       # 12

print(not True)         # False
print(not False)        # True
print(not e1)           # False

# and 具备 '逻辑短路' 的能力
print( False and 3 / 0) # False
# print( True and 3 / 0) # 报错
# or 同样具备 '逻辑短路' 的能力
print( True or 3 / 0) # True
# print( False or 3 / 0) # 报错
print('------------------16')

# 16.进制
# 十进制 (Decimal) 无前缀 基数:10  数字范围:0-9
# 9527 = 9 * 10**3 + 5 * 10**2 + 2 * 10**1 + 7 * 10**0
e1= 10
print(e1)    # 10

# 二进制 (Binary) 前缀:0b 或 0B  基数:2 数字范围:0-1
# 1010  = 1 * 2**3 + 0 * 2**2 + 1 * 2**1 + 0 * 2**0  (十进制值为: 10)
e2 = 0b1010  # 十进制 10
print(e2)    # 输出: 10

# 八进制 (Octal) 前缀:0o 或 0O  基数:8 数字范围:0-7
# 12 = 1 * 8**1 + 2* 8**0  等于 10(十进制)
e3 = 0o12
print(e3)

# 十六进制 (Hexadecimal) 前缀:0x 或 0X 基数:16  数字范围:0-9, A-F
# 0xA = 10 * 16**0  十进制 10
e4 = 0xA
print(e4)  # 10
e5 = 0xA2
print(e5)  #  162 = 10 * 16**1 + 2 * 16**0

# 十进制转其他进制的【字符串】
# 十进制转二进制 方法
# 25/2 = 12 1
# 12/2 = 6  0
# 6/2 =  3  0
# 3/2 =  1  1
# 1/2 =  0  1
# 从下到上 依次取出 25(10) = 11001(2)
print(bin(25))  # 0b11001

# 十进制转八进制
# 540/8 = 67 4
# 67/8  =  8 3
# 8/8   =  1 0
# 1/8   =  0 1
# 从下到上 依次取出 1034
print(oct(540)) # 0o1034

# 十进制转十六进制
# 463/16 = 28 15
# 28/16  = 1  12
# 1/16   = 0  1
# 从下到上 依次取出 1CF
print(hex(463)) # 0x1cf

# 去掉前缀
number = 42
print(bin(number)[2:])  # 输出: 101010
print(oct(number)[2:])  # 输出: 52
print(hex(number)[2:])  # 输出: 2a

# 其他进制转十进制 的【数字】 : 使用int()函数,指定原进制
print(int('101010', 2))   # 二进制转十进制: 42
print(int('52', 8))       # 八进制转十进制: 42
print(int('2a', 16))      # 十六进制转十进制: 42
print(int('2A', 16))      # 不区分大小写: 42
print('------------------17')

# 17.输入 input()
# 使用input()获取用户的输入
name = input('请输入你的姓名')
age = input('请输入你的年龄')
# input获取到的内容全部是字符串类型
print(type(name))   # <class 'str'>
print(type(age))    # <class 'str'>
# 将age转为整数
age = int(age)
# 打印信息
print(f'{name},你今年的年龄是{age},你明年的年龄是:{age+1}')

2.流程控制

目录:

1.分支

2.循环

  1. break 和 continue
python 复制代码
# --------------------------------------
# 目录:
# 1.分支
# 2.循环
# 3. break 和 continue


# 流程控制语句:
# 顺序: 从上到下 (略)
# 分支: 单分支 双分支 多分支 嵌套分支
# 循环: while循环 for循环

# 1.分支
# 1.1 单分支: if
age = 19
# if True:
# if -1:
# if 0:
# if '':
# if '哈哈':
if age > 18:
    print('成年')
    print('成年的世界')
print('与if平级')

# 1.2 双分支:
# if
# else
age2 = 14
if age2 > 18:
    print('成年')
    print('成年的世界')
else:
    print('未成年')
print('与if-else平级')

# 1.3 多分支
# if
# elif
# elif
# else
age3 = 19
if age3 <= 10:
    print('条件1')
elif age3 <= 20:
    print('条件2')
elif age3 <= 30:
    print('条件3')
else:
    print('条件4')

# 1.4 嵌套循环 在嵌套 if 语句中,可以把 if...elif...else 结构放在另外一个 if...elif...else 结构中。
# if 表达式1:
#     语句
#     if 表达式2:
#         语句
#     elif 表达式3:
#         语句
#     else:
#         语句
# elif 表达式4:
#     语句
# else:
#     语句

# num = int(input("输入一个数字:"))
num = 12
if num % 2 == 0:
    if num % 3 == 0:
        print("你输入的数字可以整除 2 和 3")
    else:
        print("你输入的数字可以整除 2,但不能整除 3")
else:
    if num % 3 == 0:
        print("你输入的数字可以整除 3,但不能整除 2")
    else:
        print("你输入的数字不能整除 2 和 3")
print('------------------1')

# 2.循环
# 2.1 while循环
# while 判断条件(condition):
#     执行语句(statements)......
g1 = 1
while g1 <= 5:  # 防止死循环 eq: while True
    print('hello Python')
    g1 += 1    # 防止死循环 eq:  # g1 += 1

# 2.2 while 循环使用 else 语句
count = 7
while count < 5:
    print(count, " 小于 5")
    count = count + 1
else:
    print(count, " 大于或等于 5")

# for循环
# for <variable> in <sequence>:
#     <statements>
# else:
#     <statements>

# 循环实例:
sites = ["Baidu", "Google", "Runoob", "Taobao"]
for site in sites:
    print(site)

nums = [1,2,3]
for i in nums:
    # nums.append(4) # nums中拼接4 [1,2,3,4]
    print(i)

# 打印字符串中的每个字符
word = 'runoob'
for letter in word:
    print(letter)

# 1 到 5 的所有数字: 整数范围值可以配合 range() 函数使用:
# range(..)函数:
# 值为: 左闭右开 range(1, 6) 包含1,不包含6 值为: 0 1 2 3 4 5
# range(6) =  range(0, 6)
for number in range(1, 6):
    print(number) # 1 2 3 4 5

print('--------')
# for...else
# for item in iterable:
#     # 循环主体
# else:
#     # 循环结束后执行的代码
# x = 0 Python中可以不用先定义
for x in range(6): # range(6) =  range(0, 6)
  print(x)  # 0 1 2 3 4 5
else:
  print("Finally finished!")  # Finally finished!

sites = ["Baidu", "Google", "Runoob", "Taobao"]
for site in sites:
    if site == "Runoob":
        print("菜鸟教程!")
        break
    print("循环数据 " + site)
else:
    print("没有循环数据!")
print("完成循环!")
# 循环数据 Baidu
# 循环数据 Google
# 菜鸟教程!
# 完成循环!
print('--------')
# print()介绍
# print()函数有一个end参数,默认值是\n(换行符):
print('a1')
print('a11', end= '\n')
print('a2', end='')   # a2a3
print('a3', end=' ')  # a2a3 a4
print('a4', end= '/') # a2a3 a4/a5
print('a5', end=',')  # a2a3 a4/a5,a6
print('a6')
# 逗号分隔的多个值,默认用空格分隔
print('a', 'b', 'c')  # a b c
# 使用sep参数修改分隔符
print("a", "b", "c", sep='-')  # 输出:a-b-c
print("2024", "01", "15", sep='/')  # 输出:2024/01/15
# 同时使用sep和end
print("a", "b", "c", sep='-', end='!!!\n')  # 输出:a-b-c!!!

# 99乘法表
for i in range(1, 10):
    for j in range(1, i+1):
        print(f"{j}×{i}={i*j:2d}", end="  ")
    print()  # 换行

for i in range(1, 10):
    for j in range(1, i+1):
        print(f'{j}*{i}={i*j}', end='\t')
    print()  # 换行
print('------------------2')

# 3. break 和 continue
# break 立即终止循环,不再执行后续循环。语句可以跳出 for 和 while 的循环体。如果你从 for 或 while 循环中终止,任何对应的循环 else 块将不执行。
# continue 跳过当前循环块中的剩余语句,然后继续进行下一轮循环。

n = 5
while n > 0:
    n -= 1
    if n == 2:
        break
    print(n)
print('循环结束1。')
# 4
# 3
# 循环结束1。

n = 5
while n > 0:
    n -= 1
    if n == 2:
        continue
    print(n)
print('循环结束2。')
# 4
# 3
# 1
# 0
# 循环结束2。

for i in range(1, 6):  # 1 2 3 4 5
    if i == 2:
        break
    print(i)
print('循环结束3')
# 1
# 循环结束3

for i in range(1, 6):  # 1 2 3 4 5
    if i == 2:
        continue
    print(i)
print('循环结束4')
# 1
# 3
# 4
# 5
# 循环结束4

for n in range(2, 10):
    for x in range(2, n):
        if n % x == 0:
            print(n, '等于', x, '*', n // x)
            break
    else:
        # 循环中没有找到元素
        print(n, ' 是质数')
# 2  是质数
# 3  是质数
# 4 等于 2 * 2
# 5  是质数
# 6 等于 2 * 3
# 7  是质数
# 8 等于 2 * 4
# 9 等于 3 * 3

3.函数

目录:

1.函数介绍:

2.函数的基本使用

3.函数中函数的使用

4.关键字参数

5.限制传参方式

6.参数的默认值

7.可变参数

8.特殊字面量 - None

9.函数的返回值

10.全局作用域vs局部作用域

11.函数的嵌套调用

12.递归调用

13.说明文档

python 复制代码
# --------------------------------------
# 目录:
# 1.函数介绍:
# 2.函数的基本使用
# 3.函数中函数的使用
# 4.关键字参数
# 5.限制传参方式
# 6.参数的默认值
# 7.可变参数
# 8.特殊字面量 - None
# 9.函数的返回值
# 10.全局作用域vs局部作用域
# 11.函数的嵌套调用
# 12.递归调用
# 13.说明文档


# 1.函数介绍:
# 是组织好的,可重复使用的,用于执行特定代码的代码块。
# Python中的函数是一段有名字的代码块。我们提前编写好函数中要执行的代码。调用函数,函数中的代码就会执行。

# 函数的作用:
# ① 代码复用: 提高代码复用性,减少重复代码
# ② 结构清晰:让程序更易读,更易维护。
# ③ 模块化开发:把程序分成若干个小功能,便于团队合作。

# 函数的分类
# ① 内置函数:无需任何操作,直接使用。 eq: print() type() range()...
# ② 模块提供的函数:需要导入指定的模块后,才能使用。eq: math.sqrt() math.floor()...
# ③ 自定义函数:程序员自己定义的函数(先定义,再使用)

# 2.函数的基本使用
# 定义函数
# welcome() 报错  Python中的函数必要要先定义,再调用
def welcome():
    print('welcome python')
# 调用函数
welcome()
welcome()
print('------------------2')

# 3.函数中函数的使用
# 函数的位置参数: 调用函数时,根据参数在函数定义中出现的顺序,把实参的值依次传递给对应的形参。
def order(num, dish): # 形参
    print(f'你点的是:{num}份{dish}')
order(1,'辣子鸡')    # 实参
order(2,'青椒炒肉')  # 实参
# print(num) 报错:不能在函数外使用形参
# order(2)  # 实参 参数个数不能多也不能少
print('------------------3')

# 4.关键字参数
# 调用函数时,通过 形参名 = 值 的形式传递实参。
def order1(num, dish, wel): # 形参
    print(f'你点的是:{num}份{dish},{wel}')
order1(dish='面条', num=3, wel = '哈哈') # 可以不按照形参顺序传参。
order1(4, wel='呼呼', dish='青椒鱼') # 关键字和位置参数混用,使用关键字参数时,位置参数必须在关键字参数之前。
print('------------------4')

# 5.限制传参方式
# 具体规则: /前面只能用位置参数, * 后面只能用关键字参数
def greet1(name, / , gender, *, age, height): # name 必须用位置参数, age,height必须是关键字参数
    print(f'我叫{name},性别{gender},年龄是{age},身高是{height}cm')
greet1('张三', '男', age= 18, height = 172) # 我叫张三,性别男,年龄是18,身高是172cm
greet1('张三', gender='男', age= 18, height = 172) # 我叫张三,性别男,年龄是18,身高是172cm

# def greet1(name, * , gender, /, age, height):  //这种写法是错误的,/ 和 * 同时使用时,/ 必须在 * 的前面。
print('------------------5')

# 6.参数的默认值
# 定义函数时,通过 形参名=值 的形式,为参数制定一个默认值。
# 注意:默认参数必须要放在必选参数的后面,或者说: 某个形参,一旦设置了默认值,那它后面的所有形参,也必须要写默认值。
def order2(num=5 , dish ='炒粉'): # 形参
    print(f'你点的是:{num}份{dish}')
order2()
order2(6)
order2(7, '剁椒鱼头')
order2(dish= '剁椒鱼头2', num= 8)

# def order2(num=5 , dish): # 报错
print('------------------6')

# 7.可变参数
# 7.1 可变位置参数
# 定义函数时,在形参名前面加 * , 可以接受任意数量的位置参数,并打包成一个元组。
def test1(*args):
    print(args)
test1('张三', '男', 18, 172) # ('张三', '男', 18, 172)
# args是一个形参名,可以随意更改
# args中存放的是:函数调用时,所传入的所有位置实参
# args的数据类型是元组

# 7.2 可变关键字参数
# 定义函数时,在形参名前加 ** , 可以接受任意数量的关键字参数,并打包成一个字典。
def test2(**kwargs):
    print(kwargs)
test2(name='张三', gender='男', age= 18, height = 172)  # {'name': '张三', 'gender': '男', 'age': 18, 'height': 172}
# kwargs是一个形参名,可以随意更改
# kwargs中存放的是:函数调用时,所传入的所有关键字实参
# kwargs的数据类型是字典

# 可变位置参数,可变关键字参数,可以同时使用,但 必须先写可变位置参数
def test3(*args, **kwargs):
    print(args)     # ('张三', '男')
    print(kwargs)   # {'age': 18, 'height': 172}
test3('张三', '男', age= 18, height = 172)
# 可变位置参数,可变关键字参数,也能和 其他类型的参数一起使用
def test4(a, b, *args, c= '阜阳师范大学', **kwargs):
    print(a)        # 张三
    print(b)        # 男
    print(args)     # ('抽烟', '喝酒')
    print(c)        # fuyang
    print(kwargs)   # {'age': 18, 'height': 172}
test4('张三', '男', '抽烟', '喝酒', c = 'fuyang', age= 18, height = 172)
print('------------------7')

# 8.特殊字面量 - None
# ① 字符型:string 'a'
# ② 整型:int 108
# ③ 浮点型:float 14.5
# ④ 布尔类型  boolean: True False
# ⑤ NoneType类型:None

# ① None是一个特殊的字面量,它表示: 空值/无值/无意义
#   msg = None
#   定义了msg的变量,但是目前还不知道它的具体类型。
#   不写None, 写 0 或 '' 或 False 可以吗? --- 最好别写
#   None更中立,更开放,None是不暗示类型的。
#   如果确定msg以后是字符串类型,那就可以写 ''
# ② None的类型是 NoneType.
print(type(None))   # <class 'NoneType'>
# ③ None转为布尔值时,值是 False
msg1 = None
bool(msg1)
print(msg1) # None
msg1 = bool(msg1)
print(msg1) # False
# ④ None不能参与数学运算,也不能与字符串拼接
# msg2 = None
# result1 = msg2 +1 # 报错
# result2 = msg2 + '你好' # 报错
# ⑤ 如果我们不给函数设置返回值,函数会默认返回 None
print('------------------8')

# 9.函数的返回值
# 函数执行完毕后,会把执行结果交给调用者,这个执行结果就是 返回值。
# return 关键字; return会结束函数执行,并把return后面的值,作为函数的 返回值。
def add1(a, b):
    # a + b     # None
    return a + b  # 3
    # print('a') # return 后面的代码不可执行
    # return None
result6 = add1(1,2)
print(result6)
# 如果一个函数返回值为None,我们也经常说这个函数没有返回值。
result7 = print('hello Python')
print(result7) # None print函数是没有返回值的
print('------------------9')

# 10.全局作用域vs局部作用域
# 变量能起作用的范围(变量在哪里能用,在哪里不能用)
# 全局作用域:整个.py文件最外层的范围,就是全局作用域。
# 局部作用域:函数的内部范围,就是局部作用域。
r1 = 100   # 全局作用域:在当前文件的任意位置都能使用
r2 = 'hello'
def def0():
    r3 = 10000  # 局部作用域:只能在当前函数中内部使用。
    r4 = 'hello Python'
    r1 = 1000
    print(r1)  # 1000 就近取值原则
    print(r2)  # hello
    print(r3)  # 10000
def0()
# print(r3)  # 报错
print(r1) # 100

# global用法:
r5 = 1
def def1():
    global r5 # r5开启全局变量。此时下面的 r5 = 2 就是在修改 r5 = 1 的值,后续(全局+局部)都得到修改后的值
    r5 = 2
    print(r5)  # 2
def1()
print(r5) # 2

# 10.1 局部作用域 和 局部变量,会在函数调用时创建,在函数 执行结束后 销毁。即:多次调用函数,执行结果是一样的。
def def2():
    r6 = 10
    r6 += 1
    print(r6)
def2()   # 11
def2()   # 11

# 10.2 全局作用域 和 全局变量, 会在程序开始时创建,在程序结束后销毁
r6 = 20
def def3():
    global r6
    r6 += 1
    print(r6)
def3()
print(r6) # 21 21
def3()
print(r6) # 22 22
print('------------------10')

# 11.函数的嵌套调用
# 在一个函数的执行过程中,去调用了另外一个函数
def def4():
    print('def4')
def def5():
    def4()
    print('def5')
def def6():
    def5()
    print('def6')
def6()
# def4
# def5
# def6
print('------------------11')

# 12.递归调用
# 指函数自己调用自己的一种操作
# 把一个很大的事,拆解为无数个套路一样的小事。
def def7(n):
    if n < 8:
        print(f'hello dog-{n}')
        n += 1
        def7(n)
    else:
        print('end')
def7(5)

def def8(n):
    if n > 1:
        def8(n -1)
    print(f'hello, {n}')  # 5 4 3 2 1 栈 底--> 顶
def8(5)
# hello, 1
# hello, 2
# hello, 3
# hello, 4
# hello, 5

def def9(n):
    if n > 1:
        def9(n -1)
        print(f'hello, {n}')  # 5 4 3 2 栈 底--> 顶
def9(5)
# hello, 2
# hello, 3
# hello, 4
# hello, 5

# 使用递归求一个数的阶乘
def fac(num):
    if num == 0:
        return 1
    return num * fac(num-1)
result10 = fac(5)
print(result10) # 120
print('------------------12')

# 13.说明文档
# 说明文档:写在函数里的文字说明,用来描述:函数的功能,需要哪些参数,返回什么结果。
def def10(n1, n2):
    """
    计算2个数相加的结果
    :param n1: 第一个数
    :param n2: 第二个数
    :return: 二者相加的结果
    """
    return n1 + n2
def10(1, 2)


# 案例; 计算1个人7天的总运动量,平均值,是否达到目标
def calc_total(*nums):
    return sum(nums)

def calc_avg(total, days=7):
    return total / days

def check_success(total, goal =120):
    if total > goal:
        return 'SUCCESS'
    else:
        return 'FAIL'

def main(title, duration):
    """
    主函数
    :param title: 比赛名
    :param duration: 持续时间
    :return: 返回结果
    """
    print(f'【{title}】【{duration}】挑战赛(请输入每天的运动量)')
    num1 = int(input('第1天:'))
    num2 = int(input('第2天:'))
    num3 = int(input('第3天:'))
    num4 = int(input('第4天:'))
    num5 = int(input('第5天:'))
    num6 = int(input('第6天:'))
    num7 = int(input('第7天:'))
    # 计算运动总量
    total = calc_total(num1, num2, num3, num4, num5, num6, num7)
    # 计算平均值
    avg = calc_avg(total)
    # 判断是否挑战成功
    result11 = check_success(total)
    print(f'{total}, {avg:.1f}, {result11}')   # :1f 保留1位小数

main('仰卧起坐', 7)

4.1 数据容器1

目录:

数据容器

1.数据容器-列表(list)

1.1 列表中下标(索引值)

1.2 新增(Create)操作

1.3 删除(Delete)操作

1.4 修改(Update)操作

1.5 查询(Read)操作

1.6 列表的常用方法

1.7 常用内置函数

1.8 列表-循环遍历

1.9 列表-案例练习

1.10 列表-特点总结

2.数据容器-元组(tuple)

2.1 介绍

2.2 定义元组:

2.3 注意事项

2.4 定义空元组

2.5 定义只有一个元素的元组

2.6 元组中的常用方法:

2.7 元组中常用的内置函数

2.8 元组的循环遍历

2.9 元组总结

3.函数-解包列表或元组传参

4.数据容器-字符串(str)

4.1 介绍

4.2 常用方法

4.3 常用内置函数

4.4 字符串的循环遍历

5.序列

5.1 序列的切片操作

5.2 序列的其他操作-相加

5.3 序列的其他操作-相乘

python 复制代码
# --------------------------------------
# 目录:
# 数据容器
# 1.数据容器-列表(list)
    # 1.1 列表中下标(索引值)
    # 1.2 新增(Create)操作
    # 1.3 删除(Delete)操作
    # 1.4 修改(Update)操作
    # 1.5 查询(Read)操作
    # 1.6 列表的常用方法
    # 1.7 常用内置函数
    # 1.8 列表-循环遍历
    # 1.9 列表-案例练习
    # 1.10 列表-特点总结
# 2.数据容器-元组(tuple)
    # 2.1 介绍
    # 2.2 定义元组:
    # 2.3 注意事项
    # 2.4 定义空元组
    # 2.5 定义只有一个元素的元组
    # 2.6 元组中的常用方法:
    # 2.7 元组中常用的内置函数
    # 2.8 元组的循环遍历
    # 2.9 元组总结
# 3.函数-解包列表或元组传参
# 4.数据容器-字符串(str)
    # 4.1 介绍
    # 4.2 常用方法
    # 4.3 常用内置函数
    # 4.4 字符串的循环遍历
# 5.序列
    # 5.1 序列的切片操作
    # 5.2 序列的其他操作-相加
    # 5.3 序列的其他操作-相乘




# 数据容器
# 一种存放多个数据的数据类型。
# 好处:可以更高效的管理成批的数据,且便于存储,访问。
# ① 容器中的每一个数据,又称为: 每一个元素
# ② Python中有很多种数据容器
#   包含: 列表(list) 元组(tuple) 字符串(str) 集合(set) 字典(dict)

print('------------------列表')
# 1.数据容器-列表(list)
# 定义列表:     list1 = [元素0, 元素1, 元素2, ...]
# 序列中的每个值都有对应的位置值,称之为索引,第一个索引是 0,第二个索引是 1,依此类推。
# 列表的数据项不需要具有相同的类型
list1 = []  # 定义空列表:列表中的数据,后期会通过特定写法填充。
list2 = [34, 35, 36, 37]
list3 = ['北京', True, 36, 37]
list4 = [12, '北京', False, None, 12.13, [1, 3]]  # 嵌套列表 [1, 3]
print(f'{list1}\n{list2}\n{list3}\n{list4}')
# []
# [34, 35, 36, 37]
# ['北京', True, 36, 37]
# [12, '北京', False, None, 12.13, [1, 3]]

# 1.1 列表中下标(索引值)
# 下标(索引值):列表中的元素的位置编号。
# 正索引:从左往右数, 其实元素是 0 随后是1,2,3,4...
nums1 = [1,2,3,4]  # 正索引依次为:0,1,2,3
# 负索引:从右往左数,其实元素是 -1, 随后-2,-3,-4...
nums2 = [1,2,3,4]  # 负索引依次为:-1,-2,-3,-4...
# 作用:通过 [下标] 的形式,去除对应元素。
print(nums1[0])     # 1
print(nums1[-4])    # 1
# print(nums1[4])   # 报错:IndexError: list index out of range
print(list4[-1][1]) # 3
print('------------------1.1')

# 列表中操作: 新增(Create),查询(Read),删除(Delete),修改(Update)

# 1.2 新增(Create)操作
# 方式1:
# 语法:列表.append(元素)  解释:在列表尾部追加一个元素。
# append() 方法
# 方法 vs 函数 : 当一个函数隶属于某个对象时,那该函数就是该对象的一个方法。
# b() 调用b函数  a.b() 调用a的b方法
nums3 = [1, 2, 3, 4]
nums3.append(5)
print(nums3) # [1, 2, 3, 4, 5]

# 方式2:
# 语法: 列表.insert(下标, 元素) 解释:在列表指定下标处添加一个元素。 原元素向后移动1位
nums4 = [1, 2, 3, 4]
nums4.insert(2,666)
print(nums4) # [1, 2, 666, 3, 4]

# 方式3:
# 语法:列表.extend(可迭代对象)   解释:将 可迭代对象 中的内容依次取出,追加到列表尾部。
# 可迭代对象:字符串 '陈振' / range(1, 4) / [1,2,3,4]
nums5 = [1, 2, 3, 4]
nums5.extend('阜师院')  # 依次取出: 阜 师 院
nums5.extend([5, 6, 7, '陈陈'])  # 无返回值
print(nums5)  # [1, 2, 3, 4, '阜', '师', '院', 5, 6, 7, '陈陈']
print('------------------1.2')

# 1.3 删除(Delete)操作
# 方式1:
# 语法: 列表.pop(下标)   解释:删除指定位置的元素,并将删除的元素返回。
nums6 = [10, 20, 30, 40]
n6 = nums6.pop(2)
print(nums6)  # [10, 20, 40]
print(n6)   # 30

# 方式2:
# 语法: 列表.remove(值)   解释:删除列表中第一次出现的指定值
nums7 = [10, 20, 30, 40, 10]
n7 = nums7.remove(10)   # 无返回值 None
print(nums7)  # [20, 30, 40, 10]
print(n7)   # None

# 方式3:
# 语法: 列表.clear()   解释:删除列表中的所有元素(变成一个空列表)
nums8 = [10, 20, 30, 40, 10]
n8 = nums8.clear() # 无返回值 None
print(nums8)    # []
print(n8)       # None!

# 方式4:
# 语法: del 列表[下标]  解释:删除指定位置的元素。
nums9 = [10, 20, 30, 40]
del nums9[2] # 无返回值
print(nums9) # [10, 20, 40]
print('------------------1.3')

# 1.4 修改(Update)操作
# 语法: 列表[下标] = 值  解释:通过下标修改指定位置的元素。
nums10 = [10, 20, 30, 40, 10, 60]
nums10[2] = 300
print(nums10)   # [10, 20, 300, 40, 10, 60]
print('------------------1.4')

# 1.5 查询(Read)操作
# 语法:列表[下标]  解释:通过下标获取制定位置的元素。
nums11 = [10, 20, 30, 40, 10, 60]
n11 = nums11[2]
print(n11)   # 30
print('------------------1.5')

# 1.6 列表的常用方法

# 方法1: 元素下标 = 列表.index(值)
# 解释:查找指定元素在列表中第一次出现的下标
# 返回值: 元素下标
nums12 = [10, 20, 30, 40, 10, 20, 60, [100, 70]]
n12 = nums12.index(20)
print(n12) # 1
# index 不会深入到嵌套列表中查询。
# nums12.index(100)  # 报错:ValueError: 100 is not in list

# 方法2:元素出现的次数 = 列表.count(值)
# 解释:统计某个元素在列表中出现的次数。
# 返回值: 元素出现的次数
nums13 = [10, 20, 30, 40, 10, 20, 60, [100, 70]]
n13 = nums13.count(20)
print(n13) # 2
n100 = nums13.count(100)
# count统计列表中不存在的元素时,不会报错,会返回 0
# count 不会深入到嵌套列表中查询。
print(n100)  # 0

# 方法3:列表.reverse()
# 解释: 反转列表(会该表原列表本身),无需参数
# 返回值: 无
nums14 = [10, 20, 30, 40, 10, 20, 60, [100, 70]]
nums14.reverse()
# reverse 不会深入到嵌套列表中查询。
print(nums14) # [[100, 70], 60, 20, 10, 40, 30, 20, 10]

# 方法4:列表.sort(reverse=布尔值)
# 解释:对列表排序(默认: 从小到大,会改变原列表本身)
# reverse 用于控制排序方式。 reverse = True 从大到小
# 返回值: 无
# 场景1:若列表中的元素: 都是数字,则按照数字的大小顺序进行排序。
nums15 = [10, 20, 30, 40, 10, 20, 60]
nums15.sort()
print(nums15)   # [10, 10, 20, 20, 30, 40, 60]
nums15.sort(reverse=True)
print(nums15)   # [60, 40, 30, 20, 20, 10, 10]

# 场景2:若列表中的元素: 既是数字,又有字符串,那就会报错。
nums15 = [10, 20, 30, 40, 10, 20, 60, '老陈']
# nums15.sort()
# print(nums15)   # 报错 TypeError: '<' not supported between instances of 'str' and 'int'

# 场景3:若列表中元素: 都是字符串,则按照字符串的 Unicode编码 大小进行排序。(默认: 从小到大)
str1 = ['李传涛','陈振','李赛赛','王伟健','张俊彦','张雪峰']
# sort()方法不会深入到嵌套循环内部
str1.sort()
print(ord('张'), ord('李'), ord('王'), ord('陈'))
print(str1)  # ['张俊彦', '张雪峰', '李传涛', '李赛赛', '王伟健', '陈振']

# 注意: 所有的列表方法,都只作用于'当前层'的元素(浅层操作)。不会自动进入'嵌套的' '里层'结构中!!!
print('------------------1.6')

# 1.7 常用内置函数 (适用于所有 数据容器)
# 方法1: sorted(数据容器, reverse= 布尔值)
# 解释:对容器排序(从小到大,不会改变原容器)
# reverse用于控制排序方式
# 返回值:经过排序后的新容器
# 场景1:若列表中的元素: 都是数字,则按照数字的大小顺序进行排序。
# 场景2:若列表中的元素: 既是数字,又有字符串,那就会报错。
# 场景3:若列表中元素: 都是字符串,则按照字符串的 Unicode编码 大小进行排序。(默认: 从小到大)
nums16 = [10, 20, 30, 40, 10, 20, 60]
nums166 = sorted(nums16)
print(nums16)   # [10, 20, 30, 40, 10, 20, 60]
print(nums166)  # [10, 10, 20, 20, 30, 40, 60]
num1666 = sorted(nums16, reverse=True)
print(num1666)  # [60, 40, 30, 20, 20, 10, 10]

# 方法2:len(数据容器)
# 解释:获取容器中元素的个数
# 返回值: 元素个数
nums17 = [10, 20, 30, 40, 10, 20, 60, [80,90]]
count177 = len(nums17)
print(count177)  # 8

# 方法3: max(数据容器)
# 解释: 返回容器中 或 多个值中的最大值。
# 返回值: 最大值
nums18 = [10, 20, 30, 40, 10, 20, 60]
max188 = max(nums18)
print(max188)  # 60
# 如果容器中的元素:都是字符串,那max(也适用于min)会返回:Unicode编码最大的字符。
nums1888 = ['张','李','王','陈']
max18888 = max(nums1888)
print(ord('张'), ord('李'), ord('王'), ord('陈')) # 24352 26446 29579 38472
print(max18888)  # 陈

# 方法4: min(数据容器)
# 解释: 返回容器中 或 多个值中的最小值。
# 返回值: 最小值
nums19 = [10, 20, 30, 40, 10, 20, 60]
min199 = min(nums19)
print(min199)  # 10

# 方法5: sum(数据容器)
# 解释: 对容器中所有的元素求和(只能是数值类型,字符串不能使用sum)!
# 返回值:所有元素之和
nums20 = [10, 20, 30, 40, 10, 20, 60]
# nums20 = [10, 20, 30, 40, 10, 20, 60, '陈'] # 报错
# nums20 = [10, 20, 30, 40, 10, 20, 60,[70, 40]] # 报错
sum200 = sum(nums20)
print(sum200) # 190
print('------------------1.7')

# 1.8 列表-循环遍历
# 遍历: 把容器中的元素依次取出,并对它们进行处理的过程.
# 分为2种: while循环 / for循环
# 1.81 while循环遍历
nums21 = [10, 20, 30, 20, 60]
index = 0   # 这里index不能写 = 1, 不能nums21[0]数据就丢失了
while index < len(nums21):
    print(nums21[index], end=' ')  # 10 20 30 20 60
    index += 1
print()

# 1.82 for循环遍历
# 方式1:
nums22 = [10, 20, 30, 20, 60]
for item in nums22:
    print(item, end=' ')  # 10 20 30 20 60
print()

# 方式2:
for index in range(len(nums22)):
    print(nums22[index], end=' ')   # 10 20 30 20 60
print()

# 方式3: 同时打印下标和数据元素
for index, item in enumerate(nums22, start= 10):  # start 只是修改用于展示 '编号' index,而不是列表中真实的索引。
    print(index, item, nums22[1], end=' ')
    print()
# 10 10 20
# 11 20 20
# 12 30 20
# 13 20 20
# 14 60 20
print('------------------1.8')

# 1.9 列表-案例练习
print('请输入学生成绩,输入"结束"停止可录入')
score_list= []
# 持续循环,让用户输入学生成绩
while True:
    # data = input('请输入成绩:') # 测试时,请放开此行,注释掉下面一行。
    data = '结束'
    if data == '结束':
        break
    else:
        score_list.append(int(data))
# 如果score_list中有数据,则开始统计
if score_list:
    # 统计平均分
    avg = sum(score_list)/len(score_list)
    # 合格人数
    pass_count = 0
    # 优秀人数
    excellent_count = 0
    # 遍历列表,开始统计
    for item in score_list:
        if item > 60:
            pass_count += 1
        if item > 90:
            excellent_count += 1
    # 合格率
    pass_rate = pass_count / len(score_list) * 100
    # 优秀率
    excellent_rate = excellent_count / len(score_list) * 100
    # 打印信息
    print('************统计信息如下:***************')
    print(f'总人数为:{len(score_list)}')
    print(f'最高分为:{max(score_list)}')
    print(f'最低分为:{min(score_list)}')
    print(f'合格人数为:{pass_count}人')
    print(f'合格率为:{pass_rate:.1f}%')
    print(f'优秀人数为:{excellent_count}')
    print(f'优秀率为:{excellent_rate:.1f}')
    print(f'平均分数为:{avg:.1f}')
else:
    print('你没有输入任何成绩!')
print('------------------1.9')

# 1.10 列表-特点总结
# 1. 可存放不同类型的元素。
# 2. 元素是有序存储的(正索引,负索引)。
# 3. 列表中的元素允许重复。
# 4. 元素是运行修改的(增、删、改、查、其他操作)
# 5. 长度不固定,可以随着操作自动调整大小。
# 列表是最常用的数据容器,当遇到要 '存储一批数据' 的场景时,首选列表。

print('------------------元组')
# 2.数据容器-元组(tuple)
# 2.1 介绍
# 定义: 元组是一种和列表类似的数据容器,它和列表的区别是:元组中的元素 不可修改。
# 列表: 增删改查   元组:查
# t = (元素1, 元素2, 元素3, ...)

# 2.2 定义元组:
t1 = (1, 2, 3, 5, 67, 243)
t2 = ('陈','李','王','张')
t3 = (100, True, '陈', None)
t4 = (100, True, '陈', None, (12, 22, '陈2'))
# 通过下标取值:
print(t1[2])    # 3
print(t2[-1])   # 张   访问下标为 -1 的元素

# 2.3 注意事项
# 注意1:元组中的元素不可修改。
# t1[1] = 100 # 报错:TypeError: 'tuple' object does not support item assignment

# 注意2:元组中如果存放了可变类型(列表),那可变类型中的内容任可修改。
t5 = (1, 2, 3, 67, 243, ['陈', 10, (10, 20)])
# t5[1] = 20  # 报错
# t5[5] = 30 # 报错
t5[5][1] = 100
print(t5)   # 可以修改,输出: (1, 2, 3, 67, 243, ['陈', 100, (10, 20)])
# t5[5][2][0] = 100 # 报错
print('------------------2.3')

# 2.4 定义空元组
t6 = ()
t7 = tuple()
print(type(t6)) # <class 'tuple'>
print(type(t7)) # <class 'tuple'>
print('------------------2.4')

# 2.5 定义只有一个元素的元组
# 需要加 逗号  因为Python会认为是一个字符串,而不是元组
t8 = ('你好',)
t9 = (18,)
print('------------------2.5')

# 2.6 元组中的常用方法:
# 方法1: index()方法: 获取指定元素在元组中第一次出现的下标。
t10 = (1, 2, 3, 5, 67, 243)
t100 = t10.index(67)
print(t100)  # 4
# 方法2: count()方法:统计指定元素在元组中出现的次数。
t11 = (1, 2, 3, 243, 3, 243, 5, 67, 243)
t111 = t11.count(243)
print(t111) # 3
print('------------------2.6')

# 2.7 元组中常用的内置函数
# 方法1: max函数,返回元组中的最大值
t12 = (10, 20, 30, 40, 10, 20, 60)
t122 = max(t12)
print(t122)  # 60
# 如果容器中的元素:都是字符串,那max(也适用于min)会返回:Unicode编码最大的字符。
t13 = ('张','李','王','陈')
t133 = max(t13)
print(ord('张'), ord('李'), ord('王'), ord('陈')) # 24352 26446 29579 38472
print(t133)  # 陈

# 方法2:min函数,返回元组中的最小值
t13 = (10, 20, 30, 40, 10, 20, 60)
t133 = min(t13)
print(t133)  # 10

# 方法3:len函数,返回元组中元素的个数(元组长度)
t14 = (10, 20, 30, 40, 10, 20, 60)
t144 = len(t14)
print(t144)  # 7

# 方法4:sorted函数,对元组进行排序(不修改原元组,返回一个 新的列表)
t15 = (10, 20, 30, 40, 10, 20, 60)
t155 = sorted(t15) # 返回的是 列表
print(t155)  # [10, 10, 20, 20, 30, 40, 60]
t1555 = tuple(t155) # 列表 转成 元组
print(t1555)  # (10, 10, 20, 20, 30, 40, 60)

# 方法5: sum函数,统计元组中所有元素的和(元素必须是数字)
t16 = (10, 20, 30, 40, 10, 20, 60)
t166 = sum(t16)
print(t166) # 190

# 实际开发中的元组,不一定是我们自己定义的,eq: 函数的可变参数 *args 就是一个元组
def demo(*args):
    print(type(args))   # <class 'tuple'>
    return sum(args)
result = demo(1,2,3,4,5,6,7,8,9,10)
print(result) # 55

# 2.8 元组的循环遍历
t17 = (10, 20, 30, 40, 10, 20, 60)
index = 0
while index < len(t17):
    print(t17[index], end=' ')  # 10 20 30 40 10 20 60
    index += 1

print()

for item in t17:
    print(item, end=' ') # 10 20 30 40 10 20 60
print()
print('------------------2.8')

# 2.9 元组总结
# 1. 可存放不同类型的元素。
# 2. 元素是有序存储的(正索引,负索引)。
# 3. 元组中的元素允许重复。
# 4. 元素不允许修改(不能:增删改 只能:查)
# 5. 长度固定(一旦创建,元素个数不能增减)。
# 元组是一种 '只读' 的数据容器,想保持一批 '不会改变的数据' 时,首选元组。
print('------------------2.9')


# 3.函数-解包列表或元组传参
# 定义函数时,使用 *args(变量不一定非要用args,比如写 *data也可以),将收到的多个参数,打包成一个元组
def test(*args):
    print(f'我是test函数,我收到的参数是: {args},参数类型是:{type(args)}')
list1 = [100, 200, 300, 400]
tuple1 = ('陈', '张', '李')
# 函数调用时,正向传递: 列表或元组
# test(list1) # 我是test函数,我收到的参数是: ([100, 200, 300, 400],),参数类型是:<class 'tuple'>
# test(tuple1)  # 我是test函数,我收到的参数是: (('陈', '张', '李'),),参数类型是:<class 'tuple'>
# 函数调用时,使用 * 对: 列表 或 元组 进行解包后,再传递参数。
test(*list1) # 我是test函数,我收到的参数是: (100, 200, 300, 400),参数类型是:<class 'tuple'>
# test(*tuple1) # 我是test函数,我收到的参数是: ('陈', '张', '李'),参数类型是:<class 'tuple'>
print('------------------3')

# 4.数据容器-字符串(str)
# 4.1 介绍
# 数据容器的角度:字符串就是存放多个字符的容器。
# 字符串支持下标(索引值)
#      0123456
msg = 'welcome'
print(msg[3])   # c
print(msg[-1])  # e

# 字符串中的字符不可修改
msg1 = 'welcome'
# msg[0] = 'a'    # 报错

# 字符串不能嵌套(字符串中的每一个元素都是单个字符).以下都不是字符串嵌套
# msg2 = 'h'ell'0'  # 报错
msg3 = 'h"ell"o'
msg4 = 'h\'ell\'o'
print(msg3, msg4, end= ' ')
print('------------------4.1')

# 4.2 常用方法
# 方法: index 方法: 获取指定字符在字符串中第一次出现的下标。
msg5 = 'welcome to shanghai'
msg55 = msg5.index('o')
print(msg55) # 4

# 方法2:split 方法:将字符串按照指定字符进行分割,并返回一个列表。
# 如果按照一个不存在的字符分割,返回 原元素列表
# 按照空格分割也可以。
msg6 = 'welcome@to@shanghai'
msg66 = msg6.split('@')
print(msg66) # ['welcome', 'to', 'shanghai']
msg666 = msg6.split('d')
print(msg666)   # ['welcome@to@shanghai']

# 方法3: replace 方法:将字符串中的某个 字符串片段, 替换成 目标字符串。
msg7 = 'welcome to shanghai'
msg77 = msg7.replace('o', 'O')
print(msg77)    # welcOme tO shanghai
msg77 = msg7.replace('shanghai', 'hefei')
print(msg77)    # welcome to hefei

# 方法4: count方法: 统计指定字符在字符串中出现的次数。
msg8 = 'welcome to shanghai'
msg88 = msg8.count('o')
print(msg88)    # 2

# 方法5: 统计指定字符在字符串中出现的次数。
msg9 = 'welcome to shanghai'
msg99 = msg9.count('o')
print(msg99)    # 2

# 方法6:strip方法:从某个字符串中删除指定字符串中的任意字符。
# 规则:从字符串的两端开始删除,直到遇到第一个不在指定字符串中的字符就停下。
# strip方法是不改变原字符串的,会返回一个新的字符串。
msg10 = '666welcome to shanghai666'
msg1010 = msg10.strip('6')
print(msg10)    # 666welcome to shanghai666
print(msg1010)  # welcome to shanghai

msg11 = '1234welcome to shanghai4321'
msg1111 = msg11.strip('1324')
print(msg1111)  # welcome to shanghai

msg12 = '34215welcome12 to34 shanghai4132'
msg1212 = msg12.strip('5432')
print(msg1212)  # 15welcome12 to34 shanghai41
# 常用方法:去除字符串两边空格。
msg13 = ' 上海 '
msg1313 = msg13.strip()
print(msg13)    #  上海
print(msg1313)  # 上海
print('------------------4.2')

# 4.3 常用内置函数
# 方法1; len函数:统计字符串中,字符的个数。(字符串长度) 包含空格。
msg14 = 'welcome to shanghai'
msg1414 = len(msg14)
print(msg1414)  # 19
# 不常用的函数
# max函数:返回字符串中Unicode编码值最大的那个字符(不是下标)
# min函数:返回字符串中Unicode编码值最小的那个字符(不是下标)
# sorted函数:将字符串中Unicode编码值排序,返回新列表(不改变原字符串)
print('------------------4.3')

# 4.4 字符串的循环遍历
msg15 = 'welcome to shanghai'
# while循环遍历
index = 0
while index < len(msg15):
    print(msg15[index], end=' ')    # w e l c o m e   t o   s h a n g h a i
    index += 1
print()

# for循环遍历
for item in msg15:
    print(item, end=' ')            # w e l c o m e   t o   s h a n g h a i
print()
print('------------------4')

# 5.序列
# 序列:能连续存放元素的数据容器,元素有先后循序,且可以通过下标访问。
# 列表,元组,字符串 都是序列。

# 5.1 序列的切片操作
# 切片:从序列中按照指定范围,取出一部分元素,形成一个新的序列的操作。
list1 = [100, 200, 300, 400, 500, 600, 700]
list2 = [300, 400, 500, 600]
# 语法: 序列[起始索引:结束索引:步长]
# 步长:步长为n : 每次隔 n-1位取出
# 比如: 步长为 1 从起始索引 到 结束索引(不包括结束索引)跳过0位(依次)取出数据组成新的序列。
# 比如: 步长为 2 从起始索引 到 结束索引(不包括结束索引)跳过1位取出数据组成新的序列。

# 5.11 对列表进行切片
list3 = [10, 20, 30, 40, 50, 60, 70]
list4 = list3[0:5:1]
# 索引:            0   1   2   3   4   5   6
print(list3)    # [10, 20, 30, 40, 50, 60, 70]
print(list4)    # [10, 20, 30, 40, 50]
list5 = list3[0:5:2]
print(list5) # [10, 30, 50]
print('----------1')
list55 = list3[::]
print(list55)   # [10, 20, 30, 40, 50, 60, 70]
list555 = list3[:999:]
print(list555)  # [10, 20, 30, 40, 50, 60, 70]
list5555 = list3[2::]
print(list5555)     # [30, 40, 50, 60, 70]
list55555 = list3[::2]
print(list55555)    # [10, 30, 50, 70]
print('----------2')

# 注意: 当起始索引 大于 结束索引时,步长必须为负数,否则结果是空列表。
# 索引:  0   1   2   3   4   5   6
list6 = [10, 20, 30, 40, 50, 60, 70]
list7 = list6[5:0:-1]
print(list7)    # [60, 50, 40, 30, 20]
list8 =list6[5:0:-2]
print(list8)    # [60, 40, 20]

# 注意:一个特殊情况:当同时省略起始索引和结束索引时,如果步长为负数,那Python会自动对调:起始位置和结束位置。
list9 = [10, 20, 30, 40, 50, 60, 70]
list99 = list9[::-1]
print(list99)   # [70, 60, 50, 40, 30, 20, 10]

# 5.12 对元组进行切片
tuple1 = (10, 20, 30, 40, 50, 60, 70)
tuple2 = tuple1[0:5:1]
print(tuple2) # (10, 20, 30, 40, 50)

# 5.13 对字符串进行切片
msg16 = 'welcome to shanghai'
msg17 = msg16[0:5:1]
print(msg17)    # welco
print('------------------5.1')

# 5.2 序列的其他操作-相加
# 新序列 = 序列1 + 序列2
# 注意: 两个同类型的序列才能相加。 eq: 字符串+字符串   列表+列表  元组+元组
# 字符串相加
str1 = 'hello'
str2 = 'Python'
str3 = str1 + str2
print(str3)  # helloPython

# 列表相加
li1 = [10, 20, 30]
li2 = [10, 20, 40]
li3 = li1 + li2
print(li3)  # [10, 20, 30, 10, 20, 40]

# 元组相加
t1 = (10, 20, 30)
t2 = (10, 20, 40)
t3 = t1 + t2
print(t3)  # (10, 20, 30, 10, 20, 40)
print('------------------5.2')

# 5.3 序列的其他操作-相乘
# 新序列 = 序列 * n
# 注意: n必须是整数,不能是浮点数。
# 字符串相乘
str4 = 'hello'
str5 = str4 * 3
print(str5)  # hellohellohello

# 列表相乘
li4 = [10, 20, 30, 20]
li5 = li4 * 3
print(li5) # [10, 20, 30, 20, 10, 20, 30, 20, 10, 20, 30, 20]

# 元组相乘
t4 = (10, 20, 30, 20)
t5 = t4 * 3
print(t5)  # (10, 20, 30, 20, 10, 20, 30, 20, 10, 20, 30, 20)

4.2 数据容器2

目录:

6.数据容器-集合(set / frozenset)

6.1 介绍

6.2 定义有内容的(可变集合)

6.3 创建不可变集合:

6.4 定义空集合

6.5 集合嵌套

6.6 集合操作-增

6.7 集合操作-删

6.8 集合操作-改

6.9 集合操作-查

6.10 集合中常用方法

6.11 集合。数学运算

6.12 集合。循环遍历

6.13 集合。特点总结

7.数据容器-字典(dict)

7.1 介绍

7.2 字典-增删改查

7.3 字典-常用方法

7.4 字典-循环遍历

7.5 字典-特点总结

8.数据容器-总结

8.1 数据容器-通用操作

8.2 数据容器-小练习

8.3 数据容器-总结

总结2: 可变对象 和 不可变对象

python 复制代码
# 目录:
# 6.数据容器-集合(set / frozenset)
    # 6.1 介绍
    # 6.2 定义有内容的(可变集合)
    # 6.3 创建不可变集合:
    # 6.4 定义空集合
    # 6.5 集合嵌套
    # 6.6 集合操作-增
    # 6.7 集合操作-删
    # 6.8 集合操作-改
    # 6.9 集合操作-查
    # 6.10 集合中常用方法
    # 6.11 集合。数学运算
    # 6.12 集合。循环遍历
    # 6.13 集合。特点总结
# 7.数据容器-字典(dict)
    # 7.1 介绍
    # 7.2 字典-增删改查
    # 7.3 字典-常用方法
    # 7.4 字典-循环遍历
    # 7.5 字典-特点总结
# 8.数据容器-总结
    # 8.1 数据容器-通用操作
    # 8.2 数据容器-小练习
    # 8.3 数据容器-总结
# 总结2: 可变对象 和 不可变对象

print('------------------集合')
# 6.数据容器-集合(set / frozenset)
# 6.1 介绍
# 集合分为2种:
# 可变集合: 创建后可以增删元素。 set
# 不可变集合:创建后不可以增删元素。 frozenset

# 集合的特点:内部的元素无序(不保证顺序),不能通过下标访问元素,会自动去除重复元素。
# 可变集合: {元素0, 元素1, 元素2, 元素3,...}

# 6.2 定义有内容的(可变集合)
s1 = {10, 20, 20, 30, 40, 40, 50, 60, 60, 70, 80, 90, 100}
s2 = {'你好', 'hello', '你好', 'shanghai', '北京'}
s3 = {10, '你好', 1, True, 12.4}
print(s1, type(s1))   # {100, 70, 40, 10, 80, 50, 20, 90, 60, 30} <class 'set'>
print(s2, type(s2))   # {'shanghai', '北京', 'hello', '你好'} <class 'set'>
print(s3, type(s3))   # {1, 10, 12.4, '你好'} <class 'set'>  # 1 和 True 随意保留1个
s2.add('陈振')
print(s2)
print('------------------6.2')

# 6.3 创建不可变集合:  frozenset({元素0, 元素1, 元素2, 元素3,...})
s11 = frozenset({10, 20, 20, 30, 40, 40, 50, 60, 60, 70, 80, 90, 100})
s22 = frozenset({'你好', 'hello', '你好', 'shanghai', '北京'})
s33 = frozenset({10, '你好', 1, True, 12.4})
print(s11, type(s11))   # frozenset({100, 70, 40, 10, 80, 50, 20, 90, 60, 30}) <class 'frozenset'>
print(s22, type(s22))   # frozenset({'shanghai', '北京', 'hello', '你好'}) <class 'frozenset'>
print(s33, type(s33))   # frozenset({1, 10, 12.4, '你好'}) <class 'frozenset'>  # 1 和 True 随意保留1个
# s22.add('陈振')   # 报错: AttributeError: 'frozenset' object has no attribute 'add'
# frozenset接收的参数,可以是任意可迭代对象,但最终返回的一定是 【不可变集合】
s111 = frozenset([40, 50, 60, 60, 70, 80, 90])
s222 = frozenset((40, 50, 60, 60, 70, 80, 90))
s333 = frozenset('hello')   # 把字符串中每一次字符都拿出来作为元素
print(s111, type(s111))     # frozenset({70, 40, 80, 50, 90, 60}) <class 'frozenset'>
print(s222, type(s222))     # frozenset({70, 40, 80, 50, 90, 60}) <class 'frozenset'>
print(s333, type(s333))     # frozenset({'o', 'h', 'e', 'l'}) <class 'frozenset'>
print('------------------6.3')

# 6.4 定义空集合
# 可变集合
s4 = set()
print(s4, type(s4))     # set() <class 'set'>
s5 = {} # 这样定义出来的是: 空字典
print(s5, type(s5))    # {} <class 'dict'>
# 不可变集合(一般很少这样用,因为定义不可变空集合后,不能添加元素)
s6 = frozenset()
print(s6, type(s6)) # frozenset() <class 'frozenset'>
print('------------------6.4')

# 6.5 集合嵌套
#  集合中不能嵌套【可变集合】,但是可以嵌套【不可变集合】
# 通俗的理解是:只有'不可变'的东西,才能安全的放进集合里。
# 原因:集合不支持下标,但底层依然需要给其中的每个元素,分配一个'编号',这个编号可以用来:快速定位元素,并且这个编号是 哈希值。
#      哈希值:是根据内容计算出来的。
#      内容一旦变化,哈希值就会变化,对于集合来说,元素的哈希值一旦变化,就无法再通过原来的哈希值找到这个元素。
s1 = {10, 20, 20, 30, 40, 40, 50}  # 可变集合
s2 = frozenset({10, 20, 20, 30, 40, 40, 50})  # 不可变集合
l1 = [666, 777, 888] # 列表 (可变)
t1 = ('hello','world','Python') # 元组  (不可变)
# s3 = {11, 22, s1}  # 报错
s4 = {11, 22, s2}
print(s4, type(s4))     # {11, 22, frozenset({50, 20, 40, 10, 30})} <class 'set'>
# s5 = {11, 22, l1}  # 报错
s6 = {11, 22, t1}
print(s6, type(s6))     # {11, ('hello', 'world', 'Python'), 22} <class 'set'>
print('------------------6.5')

# 6.6 集合操作-增
# 方法1: 集合.add(元素)
# 作用:向集合中添加元素
# 返回值: 无
s1 = {10, 20, 40, 50}
s1.add(60)
print(s1) # {40, 10, 50, 20, 60}

# 方法2: 集合.update(元素)
# 作用:向集合中批量添加元素(接收可迭代对象,例如:列表,元组,集合等)
# 返回值: 无
s1 = {10, 20, 40, 50}
s1.update([60, 70])  # 列表
s1.update((80, 90))  # 元组
s1.update({100, 200})
s1.update(range(300, 305))
print(s1)   # {100, 70, 40, 200, 10, 300, 301, 302, 303, 80, 304, 50, 20, 90, 60}
print('------------------6.6')

# 6.7 集合操作-删
# 方法1: 集合.remove(元素)
# 作用:从集合中移除指定元素(若元素不存在,会报错)
# 返回值: 无
s1 = {10, 20, 40, 50}
s1.remove(20)
print(s1)  # {40, 10, 50}
# s1.remove(60) # 报错

# 方法2: 集合.discard(元素)
# # 作用:从集合中移除指定元素(若元素不存在,不报错)
# # 返回值: 无
s1 = {10, 20, 40, 50}
s1.discard(20)
print(s1)  # {40, 10, 50}
s1.discard(60)   # 不报错

# 方法3:集合.pop()
# 作用:从集合中移除一个任意元素
# 返回值;移除的那个元素
s1 = {10, 20, 40, 50}
s11 = s1.pop()
print(s11)  # 40
print(s1)   # {10, 20, 50}

# 方法4: 集合.clear()
# 作用: 清空集合
# 返回值:无
s1 = {10, 20, 40, 50}
s1.clear()
print(s1)  # set()
print('------------------6.7')

# 6.8 集合操作-改
# 集合没有下标,也不支持replace方法,所有集合没有专门用于 '改' 的方法。
# 可以用: remove + add 的组合,来达到 '修改' 的效果。
# 需求:把集合中的 40 修改为 60
s1 = {10, 20, 40, 50}
s1.remove(40)
s1.add(60)
print(s1)   # {10, 50, 20, 60}
print('------------------6.8')

# 6.9 集合操作-查
# 由于集合没有下标,也不支持切片操作,所以集合 不具备 按位置访问的能力。
# 后面我们会说到: 成员运算符(in / not in), 通过成员运算符可以判断: 某个元素是否在集合中。
s1 = {10, 20, 40, 50}
result1 = 20 in s1
print(result1)  # True
result2 = 40 not in s1
print(result2)  # False
print('------------------6.9')

# 6.10 集合中常用方法
# 方法1; 集合A.difference(集合B)
# 作用:找出集合A中,不同于集合B的元素。(集合A 与 集合B都不变)
# 返回值:一个新集合
s1 = {10, 20, 30, 40, 50}
s2 = {30, 40, 50, 60, 70}
r1 = s1.difference(s2)
print(s1)   # {50, 20, 40, 10, 30}
print(s2)   # {50, 70, 40, 60, 30}
print(r1)   # {10, 20}

# 方法2; 集合A.difference_update((集合B)
# 作用:从集合A中,删除集合B中存在的元素(集合A会被修改,而集合B不会)
# 返回值:无
s1 = {10, 20, 30, 40, 50}
s2 = {30, 40, 50, 60, 70}
s1.difference_update(s2)
print(s1)   # {20, 10}
print(s2)   # {50, 70, 40, 60, 30}

# 方法3; 集合A.union((集合B)
# 作用:合并两个集合,集合A和集合B都不变
# 返回值:一个新集合
s1 = {10, 20, 30, 40, 50}
s2 = {30, 40, 50, 60, 70}
s3 = s1.union(s2)
print(s1)   # {50, 20, 40, 10, 30}
print(s2)   # {50, 70, 40, 60, 30}
print(s3)   # {70, 40, 10, 50, 20, 60, 30}

# 方法4:集合A.issubset(集合B)
# 作用:判断集合A是否为集合B的子集。
# 规则:如果集合A中的所有元素,都在集合B中,那就返回True,否则返回False
# 返回值:布尔值
s1 = {10, 20, 30, 40, 50}
s2 = {30, 40, 50, 60, 70}
s3 = {30, 40, 50}
r3 = s3.issubset(s1)
print(r3)   # True
r4 = s2.issubset(s1)
print(r4)   # False

# 方法5:集合A.issuperset(集合B)
# 作用:判断集合A是否为集合B的超集。
# 规则:如果在集合A中,包含了集合B中所有元素,那就返回True,否则返回False
# 返回值:布尔值
s1 = {10, 20, 30, 40, 50}
s2 = {30, 40, 50, 60, 70}
s3 = {30, 40, 50}
r3 = s1.issuperset(s3)
print(r3)   # True
r4 = s2.issubset(s1)
print(r4)   # False

# 方法6:集合A.isdisjoint(集合B)
# 作用:判断集合A和集合B是否没有交集。
# 规则:如果没有交集,返回True. 但凡有1个公共元素,就返回False
# 返回值: 布尔值
s1 = {10, 20, 30, 40, 50}
s2 = {30, 40, 50, 60, 70}
s3 = {80, 90}
r3 = s1.isdisjoint(s3)
print(r3)   # True
r4 = s2.isdisjoint(s1)
print(r4)  # False
print('------------------6.10')

# 6.11 集合。数学运算
# 方法1: 集合A | 集合B   解释:并集(返回一个新集合,,包括集合 x 和 y 中所有元素)
# 方法2: 集合A & 集合B   解释:交集(只找出集合A和集合B中,共有的元素)
# 方法3: 集合A - 集合B   解释:差集(得到属于集合A,但不属于集合B的元素)
# 方法4: 集合A ^ 集合B   解释:对称差集(排除两个集合的交集,然后合并剩下的元素)
s1 = {10, 20, 30, 40, 50, 60}
s2 = {40, 50, 60, 70, 89, 90}
# 并集
r1 = s1 | s2
print(r1)       # {70, 40, 10, 50, 20, 89, 90, 60, 30}
r2 = s1 & s2
print(r2)       # {40, 50, 60}
r3 = s1 - s2
print(r3)       # {10, 20, 30}
r4 = s1 ^ s2
print(r4)       # {70, 10, 20, 89, 90, 30}
print('------------------6.11')

# 6.12 集合。循环遍历
# 集合因为没有下标,所有集合中不能使用使用while循环直接遍历,但是可以使用for循环进行遍历。
s1 = {10, 20, 30, 40, 50, 60}
# 集合不能使用while循环遍历,错误示例:
# index = 0
# while index < len(s1):
#     print(index, end = ' ') # 0 1 2 3 4 5
#     # print(s1[index])    # 报错
#     index += 1

# 使用for循环遍历
for item in s1:
    print(item, end = ' ')  # 50 20 40 10 60 30

print()
print('------------------6.12')

# 6.13 集合。特点总结
# 1、无序:集合中的元素没有固定顺序,无法通过下标访问
# 2、不重复:集合会自动去重,同一个元素只会保留一份
# 3、集合分为2种:可变集合(set) 和 不可变集合(frozenset)
# 4、集合中的元素必须是不可变类型(如:数字,字符串,元组)
# 5、集合支持:并集/交集/差集/对称差集等数学操作。
# 集合是可以去重的数据容器,当只关心元素是否存在,而不在乎顺序时,首选集合。
print('------------------字典')

# 7.数据容器-字典(dict)
# 7.1 介绍
# 字典是另一种可变容器模型,且可存储任意类型对象。
# 字典的每个键值 key:value 对用冒号 : 分割,每个键值对之间用逗号 , 分割,整个字典包括在花括号 {} 中
# 对应关系:键(key)与 值(value)的对应关系。

# 定义有内容的字典
d3 = {'张三': 72, '李四': 22, '王五': 79}
print(d3, type(d3)) # {'张三': 72, '李四': 22, '王五': 79} <class 'dict'>

# 字典中的key不能重复,若出现重复,则后写的会覆盖之前写的。
d1 = {'张三': 72, '李四': 22, '王五': 79, '张三': 72}
print(d1)   # {'张三': 72, '李四': 22, '王五': 79}

# 定义空字典
d1 = {}
d2 = dict()
print(d1, type(d1))     # {} <class 'dict'>
print(d2, type(d2))     # {} <class 'dict'>

# 字典中的key必须是不可变类型,但value可以是任意类型。
# 通俗的讲:只有不可变的东西,才能作为key
d3 = {250: 72, '李四':60, '王五':85}
print(d3, type(d3))     # {250: 72, '李四': 60, '王五': 85} <class 'dict'>
d4 = {('抽烟','喝酒'): 72, '李四':60, '王五':85}
print(d4, type(d4))     # {('抽烟', '喝酒'): 72, '李四': 60, '王五': 85} <class 'dict'>
# d5 = {['抽烟','喝酒']: 72, '李四':60, '王五':85}
# print(d5, type(d5))     # 报错: TypeError: unhashable type: 'list'

# 字典可以嵌套
stu_dict = {
    20260317001: {
        'name': '张三',
        'age': 19,
        '成绩': 99,
        '爱好': ['抽烟', '喝酒', '烫头']
    },
    20260317002: {
        'name': '李四',
        'age': 21,
        '成绩': 98
    },
    20260317003: {
        'name': '王五',
        'age': 17,
        '成绩': 100
    }
}
# {20260317001: {'name': '张三', 'age': 19, '成绩': 99, '爱好': ['抽烟', '喝酒', '烫头']}, 20260317002: {'name': '李四', 'age': 21, '成绩': 98}, 20260317003: {'name': '王五', 'age': 17, '成绩': 100}}
print(stu_dict)
print('------------------7.1')

# 7.2 字典-增删改查
# 方法1: 查询
d6 = {'张三': 72, '李四': 22, '王五': 79}
# 直接取值,若键(key)不存在,会报错。
r6 = d6['李四']
print(r6)   # 22
# r7 = d6['奥特曼'] # 报错
# 安全取值,若键(key)不存在,会返回默认值(若没有设置默认值,则会返回None)
r8 = d6.get('奥特曼', '抱歉,key不存在!')
print(r8)   # 抱歉,key不存在!

# 方法2:新增
d9 = {'张三': 72, '李四': 22, '王五': 79}
d9['李四'] = 222
print(d9)   # {'张三': 72, '李四': 222, '王五': 79}

# 方法3:修改
d10 = {'张三': 72, '李四': 22, '王五': 79}
# 修改的写法,与新增的写法一样,若字典中有对应的key,就是修改;若没有,就是新增
d10['陈振'] = 999
print(d10)  # {'张三': 72, '李四': 22, '王五': 79, '陈振': 999}
# 批量修改
d11 = {'张三': 72, '李四': 22, '王五': 79}
d11.update({'李四': 222, '王五': 799})
print(d11)  # {'张三': 72, '李四': 222, '王五': 799}

# 方法4:删除
# 删除指定key多对应的那组键值对
d12 = {'张三': 72, '李四': 22, '王五': 79}
del d12['李四']
print(d12)  # {'张三': 72, '王五': 79}
# 删除指定key所对应的那组键值对,并返回这个key所对应的值
d13 = {'张三': 72, '李四': 22, '王五': 79}
r13 = d13.pop('李四')
print(d13)  # {'张三': 72, '王五': 79}
print(r13)  # 22
# pop方法可以设置默认值
# 默认值可以保证:当删除的key不存在时,程序不会报错,并且返回这个默认值
d14 = {'张三': 72, '李四': 22, '王五': 79}
r14 = d14.pop('李四', '删除失败')
print(d14)  # {'张三': 72, '王五': 79}
print(r14)  # 22
r15 = d14.pop('奥特曼', '删除失败!')
print(r15)  # 删除失败!

# 方法5: 清空字典
d15 = {'张三': 72, '李四': 22, '王五': 79}
r15 = d15.clear()
print(d15)  # {}
print(r15)  # None
print('------------------7.2')

# 7.3 字典-常用方法
# 方法1; keys方法: 用于获取字典中所有的键
d16 = {'张三': 72, '李四': 22, '王五': 79}
k16 = d16.keys()
print(k16, type(k16))   # dict_keys(['张三', '李四', '王五']) <class 'dict_keys'>
# keys方法的返回值不是list,而是一种叫做dict_keys的类型。
# dict_keys和列表相似,可以被遍历。但是 它不能通过下标访问元素。
for item in k16:
    print(item, end=' ')    # 张三 李四 王五
# print(k16[0]) # 报错:TypeError: 'dict_keys' object is not subscriptable
# 借助内置的list函数,可以将dict_keys 转换成 list
l16 = list(k16)
print(l16, type(l16))   # ['张三', '李四', '王五'] <class 'list'>
# print(k16[0])   # 报错:TypeError: 'dict_keys' object is not subscriptable

# 方法2:values方法: 获取字典中所有的值
# values 方法返回值类型是: dict_values ,它的特点和 dict_keys 一样
v16 = d16.values()
print(v16, type(v16))   # dict_values([72, 22, 79]) <class 'dict_values'>
li162 = list(v16)
print(li162, type(li162)) # [72, 22, 79] <class 'list'>

# 方法3:items方法: 获取字典中所有的键值对(每组键值对以 元祖 的形式呈现)
# items方法 方法返回值类型是: dict_items ,它的特点和 dict_keys 一样
i16 = d16.items()
print(i16, type(i16))  # dict_items([('张三', 72), ('李四', 22), ('王五', 79)]) <class 'dict_items'>
li163 = list(i16)
print(li163, type(li163))   # [('张三', 72), ('李四', 22), ('王五', 79)] <class 'list'>
print('------------------7.3')

# 7.4 字典-循环遍历
# 字典不能使用while循环遍历,但可以使用for循环遍历
d17 = {'张三': 72, '李四': 22, '王五': 79}
for key in d17:
    print(f'{key}的成绩是{d17[key]}')
# 张三的成绩是72
# 李四的成绩是22
# 王五的成绩是79

for key in d17.keys():
    print(f'{key}的成绩是{d17[key]}')
# 张三的成绩是72
# 李四的成绩是22
# 王五的成绩是79
print('------------------7.4')

# 7.5 字典-特点总结
# 1、键值对结构:字典中的数据以key: value的形式存在,每个键都对应一个值。
# 2、键唯一:字典中的键(key)不能重复,若重复则后写的会覆盖前写的。
# 3、键不可变:键必须是不可变类型(如:数字,字符串,元祖等),而值可以是任意类型。
# 4、不支持下标:字典中的元素不能通过下标取值。
# 支持增删改查,支持for循环。
# 字典是一种以 '键' 找 '值'的映射型容器,当需要唯一标识 ---> 对应信息的结构时,首选字典。
print('------------------数据容器-通用操作')

# 8.数据容器-总结
# 8.1 数据容器-通用操作
# 以下5个函数: 既能定义对应的【空容器】,又能将【其他类型】转换成对应的数据类型。
# 8.1.1、list 函数:  ① 定义空列表   ② 将【可迭代对象】 转换成列表
res1 = list(range(8))   # 数字
res2 = list('hello, Python')    # 字符串
# res22 = list('陈','李','王','张')   # 报错:元祖不可以
res3 = list({10, 20, 30, 40})   # 集合
res41 = list({'张三': 72, '李四': 22, '王五': 79}) # 字典1
res42 = list({'张三': 72, '李四': 22, '王五': 79}.keys()) # 字典2
res43 = list({'张三': 72, '李四': 22, '王五': 79}.values()) # 字典3
res44 = list({'张三': 72, '李四': 22, '王五': 79}.items()) # 字典4
print(res1, type(res1))       # [0, 1, 2, 3, 4, 5, 6, 7] <class 'list'>
print(res2, type(res2))       # ['h', 'e', 'l', 'l', 'o', ',', ' ', 'P', 'y', 't', 'h', 'o', 'n'] <class 'list'>
print(res3, type(res3))       # [40, 10, 20, 30] <class 'list'>
print(res41, type(res41))     # ['张三', '李四', '王五'] <class 'list'>
print(res42, type(res42))     # ['张三', '李四', '王五'] <class 'list'>
print(res43, type(res43))     # [72, 22, 79] <class 'list'>
print(res44, type(res44))     # [('张三', 72), ('李四', 22), ('王五', 79)] <class 'list'>
print('------------------8.1.1')

# 8.1.2、tuple 函数: ① 定义空元祖   ② 将【可迭代对象】 转换成元组
res1 = tuple(range(8))   # 数字
res2 = tuple('hello, Python')    # 字符串
# res22 = tuple('陈','李','王','张')   # 报错:元祖不可以
res3 = tuple({10, 20, 30, 40})   # 集合
res41 = tuple({'张三': 72, '李四': 22, '王五': 79}) # 字典1
res42 = tuple({'张三': 72, '李四': 22, '王五': 79}.keys()) # 字典2
res43 = tuple({'张三': 72, '李四': 22, '王五': 79}.values()) # 字典3
res44 = tuple({'张三': 72, '李四': 22, '王五': 79}.items()) # 字典4
print(res1, type(res1))       # (0, 1, 2, 3, 4, 5, 6, 7) <class 'tuple'>
print(res2, type(res2))       # ('h', 'e', 'l', 'l', 'o', ',', ' ', 'P', 'y', 't', 'h', 'o', 'n') <class 'tuple'>
print(res3, type(res3))       # (40, 10, 20, 30) <class 'tuple'>
print(res41, type(res41))     # ('张三', '李四', '王五') <class 'tuple'>
print(res42, type(res42))     # ('张三', '李四', '王五') <class 'tuple'>
print(res43, type(res43))     # (72, 22, 79) <class 'tuple'>
print(res44, type(res44))     # (('张三', 72), ('李四', 22), ('王五', 79)) <class 'tuple'>
print('------------------8.1.2')

# 8.1.3、set 函数:   ① 定义空集合   ② 将【可迭代对象】 转换成集合
res1 = set(range(8))   # 数字
res2 = set('hello, Python')    # 字符串
# res22 = set('陈','李','王','张')   # 报错:元祖不可以
res3 = set({10, 20, 30, 40})   # 集合
res41 = set({'张三': 72, '李四': 22, '王五': 79}) # 字典1
res42 = set({'张三': 72, '李四': 22, '王五': 79}.keys()) # 字典2
res43 = set({'张三': 72, '李四': 22, '王五': 79}.values()) # 字典3
res44 = set({'张三': 72, '李四': 22, '王五': 79}.items()) # 字典4
print(res1, type(res1))       # {0, 1, 2, 3, 4, 5, 6, 7} <class 'set'>
print(res2, type(res2))       # {'t', 'l', ',', 'y', 'e', 'n', 'P', ' ', 'o', 'h'} <class 'set'>
print(res3, type(res3))       # {40, 10, 20, 30} <class 'set'>
print(res41, type(res41))     # {'张三', '王五', '李四'} <class 'set'>
print(res42, type(res42))     # {'张三', '王五', '李四'} <class 'set'>
print(res43, type(res43))     # {72, 22, 79} <class 'set'>
print(res44, type(res44))     # {('王五', 79), ('李四', 22), ('张三', 72)} <class 'set'>
print('------------------8.1.3')

# 8.1.4、str 函数:   ① 定义空字符串 ② 将【任意对象】 转换成字符串
res1 = str(range(8))   # 数字
res2 = str('hello, Python')    # 字符串
# res22 = str('陈','李','王','张')   # 报错:元祖不可以
res3 = str({10, 20, 30, 40})   # 集合
res41 = str({'张三': 72, '李四': 22, '王五': 79}) # 字典1
res42 = str({'张三': 72, '李四': 22, '王五': 79}.keys()) # 字典2
res43 = str({'张三': 72, '李四': 22, '王五': 79}.values()) # 字典3
res44 = str({'张三': 72, '李四': 22, '王五': 79}.items()) # 字典4
res5 =str(False)
res6 =str(True)
res7 =str(None)
res8 =str(100)
print(res1, type(res1))       # range(0, 8) <class 'str'>
print(res2, type(res2))       # hello, Python <class 'str'>
print(res3, type(res3))       # {40, 10, 20, 30} <class 'str'>
print(res41, type(res41))     # {'张三': 72, '李四': 22, '王五': 79} <class 'str'>
print(res42, type(res42))     # dict_keys(['张三', '李四', '王五']) <class 'str'>
print(res43, type(res43))     # dict_values([72, 22, 79]) <class 'str'>
print(res44, type(res44))     # dict_items([('张三', 72), ('李四', 22), ('王五', 79)]) <class 'str'>
print(res5, type(res5))       # False <class 'str'>
print(res6, type(res6))       # True <class 'str'>
print(res7, type(res7))       # None <class 'str'>
print(res8, type(res8))       # 100 <class 'str'>

print('------------------8.1.4')

# 8.1.5、dict 函数:  ① 定义空字典   ② 将【可迭代对象】 转换成字典
# 备注:交给dict函数的内容必须是键值对才可以,否则会报错。
res1 = dict({'张三': 72, '李四': 22, '王五': 79})
res2 = dict([('张三', 72), ('李四', 22), ('王五', 79)])
res3 = dict((('张三', 72), ('李四', 22), ('王五', 79)))
res4 = dict({('张三', 72), ('李四', 22), ('王五', 79)})
print(res1, type(res1))     # {'张三': 72, '李四': 22, '王五': 79} <class 'dict'>
print(res2, type(res2))     # {'张三': 72, '李四': 22, '王五': 79} <class 'dict'>
print(res3, type(res3))     # {'张三': 72, '李四': 22, '王五': 79} <class 'dict'>
print(res4, type(res4))     # {'张三': 72, '李四': 22, '王五': 79} <class 'dict'>
print('------------------8.1.5')

# 所有的数据容器:都支持成员运算符: in / not in 作用:判断某个 '元素' 是否在容器中。
hobby = ['抽烟','喝酒','烫头','打游戏']          # 列表
nums = (10, 20, 30, 40, 50)                    # 元组
message = 'hello Python'                       # 字符串
citys = {'北京','上海','广州','深圳'}            # 集合
score = {'张三': 72, '李四': 22, '王五': 79}     # 字典
print('喝酒' in hobby)             # True
print(20 in nums)                 #  True
print('hel' not in message)       # False
print('上海' in citys)             # True
print('陈振' in score)              # False
print('------------------8.1')

# 8.2 数据容器-小练习
# 练习1:水果清单
fruits = {
    '苹果': 4.5,
    '香蕉': 3.2,
    '橙子': 5.8,
    '草莓': 12.0,
    '哈密瓜': 8.,
}

# 需求1:打印所有的水果
for key in fruits:
    print(f'{key}:{fruits[key]}元/斤')
# 苹果:4.5元/斤
# 香蕉:3.2元/斤
# 橙子:5.8元/斤
# 草莓:12.0元/斤
# 哈密瓜:8.0元/斤

# 需求2:找到最贵的水果
maxKey = max(fruits)
print(maxKey)   # 香蕉
maxValue = max(fruits.values())
print(maxValue) # 12.0
maxItems = max(fruits.items())
print(maxItems) # ('香蕉', 3.2)
# 特殊写法:
resKey = max(fruits, key = fruits.get)
print(f'最贵的水果是{resKey},价值是:{fruits[resKey]}') # 最贵的水果是草莓,价值是:12.0
print('------------------8.1.1')

# 练习2:学生成绩表
students = [
    {
        'name': '张三',
        'scores': {'语文': 88, '数学': 92, '英语': 95}
    },
    {
        'name': '李四',
        'scores': {'语文': 75, '数学': 83, '英语': 80}
    },
    {
        'name': '张三',
        'scores': {'语文': 92, '数学': 95, '英语': 88}
    }
]
# 需求1:计算每位学生的平均分
for stu in students:
    # 获取当前学生的成绩列表
    score_list = stu['scores'].values()
    # 计算平均值
    avg = sum(score_list) / len(score_list)
    print(f'{stu["name"]}的平均成绩是:{avg:.1f}')   # name这个版本需要使用双引号,不然报错!
# 张三的平均成绩是:91.7
# 李四的平均成绩是:79.3
# 张三的平均成绩是:91.7

# 需求2:找到总分最高的学生
def find_best():
    # 记录分数最高的学生
    best_students = []
    # 记录最高分
    best_score = 0
    # 循环遍历
    for student in students:
        # 获取当前学生的总成绩
        total = sum(student['scores'].values())
        print(total)
        # 当前学生的成绩,如果 > best_score,就会更新数据
        if total > best_score:
            best_students = [student['name']]
            best_score = total
        # 当前学生的成绩与最高分相同,就加入列表
        elif total == best_score:
            best_students.append(student['name'])
    # 最高分为:275,取得最高分的学生有:['张三', '张三']
    print(f'最高分为:{best_score},取得最高分的学生有:{best_students}')

find_best()
print('------------------8.1.2')

# 练习3:评论内容
comment = '这家奶茶真好喝,环境也不错,就是价格有点贵,好喝好喝好喝!强烈推荐!'
# 需求1: 统计'好喝'出现的次数
c1 = comment.count('好喝')
print(c1) # 4

# 需求2: 将字符串中的 '贵' 替换为 '略高'
c2 = comment.replace('贵', '略高')
print(c2)   # 这家奶茶真好喝,环境也不错,就是价格有点略高,好喝好喝好喝!强烈推荐!

# 需求3:是否包含 '推荐' 两个字
c3 = '推荐' in comment
print(c3)   # True
print('------------------8.2')

# 8.3 数据容器-总结
# 1. 有序与无序:
#       有序:列表(list)/ 元组(tuple)/ 字符串(str)-- 元素有顺序,可通过下标访问元素
#       无序:集合(set)/ 字典(dict)-- 元素没有固定位置,不能用下标访问。
# 2. 可修改
#       可变:列表(list)/ 集合(set)/ 字典(dict)-- 可以对内容进行增删改操作
#       不可变:元组(tuple)/ 字符串(str)-- 内容固定,创建后无法修改。
# 3. 可重复:
#       允许重复:列表(list)/ 元组(tuple)/ 字符串(str)
#       不允许重复:集合(set)/ 字典(dict) 备注:字典中的key是唯一的,但value可重复。

# 1. 字符串(String)‌
# ‌特性‌:
#   不可变序列(字符组成)。
#   有序(支持索引和切片)。
#   支持多种操作(拼接、格式化、查找等)。
# ‌用途‌:文本处理、正则匹配。
# ‌示例‌:
s = "Hello"
print(s[1:4])  # 输出: ell

# 2. 列表(List)‌
# ‌特性‌:
#   可变序列(可动态增删改元素)。
#   有序(通过索引访问)。
#   可嵌套(支持多维列表)。
# ‌用途‌:动态数据集合、栈/队列实现。
# ‌示例‌:
nums = [1, 2, 3]
nums.append(4)  # 修改列表
print(nums)     # 输出: [1, 2, 3, 4]

# 3. 元组(Tuple)‌
# ‌特性‌:
#   不可变序列(创建后不能修改)。
#   有序(通过索引访问)。
#   可哈希(可作为字典的键)。
# ‌用途‌:存储固定数据(如坐标、数据库记录)。
# ‌示例‌:
point = (3, 5)
print(point[0])  # 输出: 3

# 4. 集合(Set)‌
# ‌特性‌:
#   无序、不重复元素的集合。
#   可变(set)或不可变(frozenset)。
#   支持数学集合操作(并集、交集等)。
# ‌用途‌:去重、成员检测。
# ‌示例‌:
unique_nums = {1, 2, 2, 3}
print(unique_nums)  # 输出: {1, 2, 3}

# 5. 字典(Dictionary)‌
# ‌特性‌:
#   键值对(key-value)结构,键必须唯一且不可变(如字符串、数字、元组)。
#   无序(Python 3.7+ 后按插入顺序保留)。
#   可变(可动态增删改键值对)。
# ‌用途‌:快速查找、映射关系、存储配置信息。
# ‌示例‌:
person = {"name": "Alice", "age": 25}
print(person["name"])  # 输出: Alice

# 总结2: 可变对象 和 不可变对象
# Python中不可变对象(Immutable)有: 特点: 创建后值不可变,任何'操作'都会生成新对象
# ① 数值类型:
#       整数 (int) : 42
#       浮点数 (float) : 3.14
#       复数 (complex) :  1+2j
#       布尔 (bool) : 是int的子类(True / False)
# ② 字符串 (str) :  'hello'
# ③ 元组 (tuple) : (1, 2, 3)
# ④ 冻结集合 (frozenset) : frozenset([1, 2])
# ⑤ 字节串 (bytes) : b'Python'

# Python中可变对象(Mutable)有:  特点: 创建后值可原地修改,内存地址不变.
# ① 列表 (list) : [1, 2, 3]
# ② 字典 (dict) : {'key': 'value'}
# ③ 集合 (set) : {1, 2, 3}
# ④ 字节数组 (bytearray) : bytearray(b'hello')
# ⑤ 自定义类实例: 用户定义的类对象(除非显式设计为不可变)

# 元组特殊性: 若元组包含可变元素(如列表),其内容仍可变.
t = (1, [1, 2])
t[1].append(3)
print(t) # (1, [1, 2, 3])

5.面向对象

目录:

1.面向对象

2 概念介绍-对象/类/实例

3 类的定义

4.创建实例

  1. 自定义方法

  2. 实例属性

  3. 类属性

  4. 实例方法

  5. 类方法

  6. 静态方法

  7. 继承

  8. 方法重写

  9. 两个常用方法

  10. 多重继承

  11. 三种访问权限

  12. getter和setter

  13. 魔法方法(魔术方法)

  14. object类

  15. 多态

  16. 抽象类

  17. 创建对象时内存分析

  18. 小练习-学生管理系统

python 复制代码
# 目录:
# 1.面向对象
# 2 概念介绍-对象/类/实例
# 3 类的定义
# 4.创建实例
# 5. 自定义方法
# 6. 实例属性
# 7. 类属性
# 8. 实例方法
# 9. 类方法
# 10. 静态方法
# 11. 继承
# 12. 方法重写
# 13. 两个常用方法
# 14. 多重继承
# 15. 三种访问权限
# 16. getter和setter
# 17. 魔法方法(魔术方法)
# 18. object类
# 19. 多态
# 20. 抽象类
# 21. 创建对象时内存分析
# 22. 小练习-学生管理系统


from datetime import datetime
from idlelib.config import idleConf


# 1. 面向对象:
# 2 概念介绍-对象/类/实例
# 一个拥有【属性】和【行为】的个体,是构成现实世界和程序世界的基本单位。
# 万物皆对象
# 任何一个具体存在的人或物,都可以看成一个对象。
# 通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
# 1.2 面向对象:
# 一种以对象为中心去思考和组织代码的方式,它更关注: '谁来做这件事'
# 面向过程思想: 关注 '过程'
# 面向对象思想: 关注 '谁来做这件事'
# 1.3 类(class):
# 用来描述一类事务的'模板',它规定了一类事物所具有的【属性】和 【行为】。
# 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
# 1.4 实例(instance):
# 根据【类】创建出来的一个具体的【对象】,就是【实例】(实例对象)。
# 类是抽象的,实例是具体的
# 1.5 实例化:
# 根据类'制造出'对象的过程,类的具体对象。就叫实例化。+
# 1.6 实例变量:
# 在类的声明中,属性是用变量来表示的,这种变量就称为实例变量,实例变量就是一个用 self 修饰的变量。
# 1.7 方法:
# 类中定义的函数。

# 3 类的定义
# 定义个Person类(类名通常使用: 大驼峰写法)
class Person:
    # 说明: 当一个函数被定义在了函数中时,那这个函数就被成伟: 方法
    # __init__方法:初始化方法。主要作用:给当前正在创建的实例对象添加属性。
    # __init__方法收到的参数:当前正在创建的实例对象(self)、其他的自定义参数。
    # 当我们以后编写代码去创建Person类实例的时候,Python会自动调用__init__
    def __init__(self, name, age, gender):
        # 给实例添加属性(语法为:self.属性么 = 值 )
        self.name = name
        self.age = age
        self.gender =gender

# 4.创建实例
# 定义一个Person类
class Person:
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender
# 创建Person类的实例对象。
p1 = Person('张三', 18, '男')
p2 = Person('李四', 22, '女')

# 如果直接打印一个实例的话,我们是看不到实例身上的属性
print(p1)   # <__main__.Person object at 0x000001C528009C10>
print(p2)   # <__main__.Person object at 0x000001C528009C50>

# 通过 点 语法可以访问或修改实例身上的属性
print(p1.name)  # 张三
print(p1.age)   # 18
print(p1.gender) # 男
print('_' * 20)  # 打印20个 - 字符串
print(p2.name)  # 李四
print(p2.age)   # 22
print(p2.gender)    # 女
p1.name = '张三弟弟'
print(p1.name)  # 张三弟弟

# 通过 实例.__dict__ 可以查看实例身上的所有属性
print(p1.__dict__)  # {'name': '张三弟弟', 'age': 18, 'gender': '男'}
print(p2.__dict__)  # {'name': '李四', 'age': 22, 'gender': '女'}

# 实例创建完毕后,依然可以通过 实例.属性名 = 值 去给实例追加属性
p1.address = '上海步行街'
print(p1.__dict__)  # {'name': '张三弟弟', 'age': 18, 'gender': '男', 'address': '上海步行街'}

# 通过type函数,可以查看某个实例对象,是由哪个类创建出来的
print(type(p1)) # <class '__main__.Person'>
print(type(p2)) # <class '__main__.Person'>
print('------------------4')


# 5. 自定义方法
# 定义一个Person类
class Person:
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender
    # 自定义方法 (给实例添加行为)
    # speak方法收到的参数是:调用speak方法的实例对象(self)、其他参数
    # speak方法只有一份,保存在Person类身上的,所有Person类的实例对象,都可以调用到speak方法
    def speak(self, msg):
        print(f'我叫{self.name}, 年龄是{self.age}, 性别是{self.gender},我想说:{msg}')

# 验证一下: speak方法是存在Person类身上的
print(Person.__dict__) # {'__module__': '__main__', '__init__': <function Person.__init__ at 0x000002238AC15DA0>, 'speak': <function Person.speak at 0x000002238AC15E40>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}

# 创建Person类的实例对象
p1 = Person('陈振', 25, '男')
p2 = Person('闫闫', 24, '女')
# 验证一下: Person的实例对象身上是没有speak方法的
print(p1.__dict__)  # {'name': '陈振', 'age': 25, 'gender': '男'}
print(p2.__dict__)  # {'name': '闫闫', 'age': 24, 'gender': '女'}
# 所有的Person类的实例对象,都可以调用到speak方法
# 当执行p1.speak()方法时,查找speak方法的过程:
#   1. 实例对象自身(p1) ==>
#   2. 实例的 '缔造者' 的身上(Person)
p1.speak('好好学习')    # 我叫陈振, 年龄是25, 性别是男,我想说:好好学习
p2.speak('天天向上')    # 我叫闫闫, 年龄是24, 性别是女,我想说:天天向上

# 验证一下上述的查找过程
def speak():
    print('把巴拉巴拉小魔仙')
p1.speak = speak
print(Person.__dict__)  # {'__module__': '__main__', '__init__': <function Person.__init__ at 0x0000025D5DD15D00>, 'speak': <function Person.speak at 0x0000025D5DD15DA0>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
print(p1.__dict__)  # {'name': '陈振', 'age': 25, 'gender': '男', 'speak': <function speak at 0x0000025D5D8404A0>}
print(p2.__dict__)  # {'name': '闫闫', 'age': 24, 'gender': '女'}
p1.speak()  # 把巴拉巴拉小魔仙
print('------------------5')

# 6. 实例属性
class Person:
    # 初始化方法
    def __init__(self, name, age, gender):
        # 通过 [实例.属性名 = 值]给实例添加的属性,就叫实例属性
        # 实例属性只能通过实例访问,不能通过类访问。
        # 每个实例都有自己的 【独一份的】实例属性,各个实例之间是互不干扰的
        self.name = name
        self.age = age
        self.gender = gender

# 创建Person类的实例对象
p1 = Person('陈振', 25, '男')
p2 = Person('闫闫', 24, '女')

# 实例属性只能通过实例访问,不能通过类方法
print(p1.name)      # 陈振
# print(Person.name)  # 报错: AttributeError: type object 'Person' has no attribute 'name'

p1.name = '陈铮'
print(p1.name)  # 陈铮
print(p2.name)  # 闫闫
print('------------------6')

# 7. 类属性
class Person:
    # max_age 、 planet
    # 类属性可以通过类访问,也可以通过实例访问
    # 类属性通常用于保存:公共数据
    max_age = 120
    planet = '地球'

    # 初始化方法
    def __init__(self, name, age, gender):
        # 给实例添加属性
        self.name = name
        self.gender = gender
        if age < self.max_age:
            self.age = age
        else:
            print(f'年龄超过了范围,已经将年龄设置为最大值:{Person.max_age}')
        self.age = Person.max_age

# 验证一下:类属性是保存在类身上的
print(Person.__dict__)  # {'__module__': '__main__', 'max_age': 120, 'planet': '地球', '__init__': <function Person.__init__ at 0x0000025722AE5F80>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}

# 创建Person类的实例对象
p1 = Person('陈振', 25, '男')
p2 = Person('闫闫', 24, '女')

# 验证一下:类属性可以通过类访问,也可以通过实例访问。
print(Person.max_age)   # 120
print(p1.max_age)       # 120 # 查找max_age的过程:1、实例自身(p1) ==》 2. 实例的'缔造者' (Person)
print(p2.max_age)       # 120

# 测试一下:年龄超过范围
p3 = Person('李白', 199, '男')  # 年龄超过了范围,已经将年龄设置为最大值:120
print(Person.__dict__)  # {'__module__': '__main__', 'max_age': 120, 'planet': '地球', '__init__': <function Person.__init__ at 0x000001A906335F80>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
print(p1.__dict__)      # {'name': '陈振', 'gender': '男', 'age': 120}
print(p2.__dict__)      # {'name': '闫闫', 'gender': '女', 'age': 120}
print('------------------7')

# 8. 实例方法
class Person:
    # 初始化方法(给实例添加属性)
    def __init__(self, name, age, gender):
        self.name = name
        self.gender = gender
        self.age = age

    # 下面的speak方法,run方法,都保存在类身上,但他们主要提供: 实例调用,所有他们都叫:实例方法
    # 自定义方法:(给实例添加行为)
    def speak(self, msg):
        print(f'我叫{self.name}, 年龄是{self.age}, 性别是{self.gender},我想说:{msg}')

    # 自定义方法:(给实例添加行为)
    def run(self, distance):
        print(f'{self.name}疯狂奔跑了{distance}米')

# 创建Person类的实例对象
p1 = Person('陈振', 25, '男')
p2 = Person('闫闫', 24, '女')

print(Person.__dict__)
print(p1.__dict__)
print(p2.__dict__)

# 通过实例调用实例方法
p1.speak('你好')  # 我叫陈振, 年龄是25, 性别是男,我想说:你好
p1.run(5000)    # 陈振疯狂奔跑了5000米

# 通过类去调用实例方法(能调用,但不推荐!)
# Person.run(100) # 报错: TypeError: Person.run() missing 1 required positional argument: 'distance'
Person.run(p1, 3000)    # 陈振疯狂奔跑了3000米
p1.run(3000)    # 陈振疯狂奔跑了3000米
print('------------------8')

# 9. 类方法
class Person:
    # 类属性
    max_age = 120
    planet = '地球'

    # 初始化方法(给实例添加属性)
    def __init__(self, name, age, gender):
        self.name = name
        self.gender = gender
        self.age = age

    # speak方法,run方法。他们都属于:实例方法
    # 自定义方法:(给实例添加行为)
    def speak(self, msg):
        print(f'我叫{self.name}, 年龄是{self.age}, 性别是{self.gender},我想说:{msg}')

    def run(self, distance):
        print(f'{self.name}疯狂奔跑了{distance}米')

    # 使用 @classmethod 装饰过的方法,就叫:类方法,类方法保存在类身上的
    # 类方法收到的参数:当前类本身 (cls) 、自定义的参数。
    # 因为收到了 cls 参数,所以类方法中可以访问类属性的
    # 类方法通常用于实现:与类有关的逻辑,例如:操作类级别的信息、一些工厂方法
    @classmethod
    def change_planet(cls, value):
        cls.planet = value

    @classmethod
    def create(cls, info_str):
        # 从 info_str中获取有效信息
        name, year, gender = info_str.split('-')
        # 获取当前的年份
        current_year = datetime.now().year
        # 计算年龄
        age = current_year - int(year)
        # 创建爱你并返回一个Person类的实例对象
        return cls(name, age, gender) # cls 相当于 Person

# 验证一下:类方法保存在类身上的
print(Person.__dict__)  # {'__module__': '__main__', 'max_age': 120, 'planet': '地球', '__init__': <function Person.__init__ at 0x000001D4C2F37A60>, 'speak': <function Person.speak at 0x000001D4C2F37C40>, 'run': <function Person.run at 0x000001D4C2F37420>, 'change_planet': <classmethod(<function Person.change_planet at 0x000001D4C2F37880>)>, 'create': <classmethod(<function Person.create at 0x000001D4C2F37E20>)>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}

# 类方法需要通过类调用
Person.change_planet('月球')
print(Person.__dict__)  # {'__module__': '__main__', 'max_age': 120, 'planet': '月球', '__init__': <function Person.__init__ at 0x0000020C77D17BA0>, 'speak': <function Person.speak at 0x0000020C77D17380>, 'run': <function Person.run at 0x0000020C77D177E0>, 'change_planet': <classmethod(<function Person.change_planet at 0x0000020C77D17D80>)>, 'create': <classmethod(<function Person.create at 0x0000020C77D17A60>)>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}

# 创建Person类的实例对象
p1 = Person('陈振', 25, '男')
p2 = Person('闫闫', 24, '女')

# 验证一下:类属性planet已经改变了
print(p1.planet)    # 月球
print(p2.planet)    # 月球

# 测试一下类方法-create
p33 = Person.create('林黛玉-2005-女')
print(p33.__dict__)  # {'name': '林黛玉', 'gender': '女', 'age': 21}

# 注意点: 类方法,也能通过实例调用到,但是非常不推荐
p4 = p1.create('贾宝玉-2006-男')
print(p4.__dict__)  # {'name': '贾宝玉', 'gender': '男', 'age': 20}
print('------------------9')

# 10. 静态方法
# 定义一个Person类
class Person:
    # 初始化方法(给实例添加属性)
    def __init__(self, name, age, gender):
        self.name = name
        self.gender = gender
        self.age = age

    @staticmethod
    def is_audit(year):
        # 获取当前的年份
        current_year = datetime.now().year
        # 计算年龄
        age = current_year - int(year)
        # 返回结果(成年True, 未成年False)
        return age > 18

    @staticmethod
    def mask_idcard(idcard):
        return idcard[:6] + '***' + idcard[-4:]

# 验证一下:静态方法也是保存在类身上的。
print(Person.__dict__) # {'__module__': '__main__', '__init__': <function Person.__init__ at 0x000002B3FF4B6CA0>, 'is_audit': <staticmethod(<function Person.is_audit at 0x000002B3FF4B6980>)>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}

# 静态方法需要通过类去调用
result = Person.is_audit(2020)
print(result)   # False
result2 = Person.mask_idcard('456789166945124186')
print(result2)  # 456789***4186

# 注意点:通过实例也能调用到静态方法,但非常不推荐。
p1 = Person('张三', 18, '男')
result3 = p1.mask_idcard('45646465469849615694')
print(result3)  # 456464***5694
print('------------------10')

# 11. 继承
class Person:
    def __init__(self, name, age, gender):
        self.name = name
        self.gender = gender
        self.age = age

    def speak(self, msg):
        print(f'我叫{self.name}, 年龄是{self.age}, 性别是{self.gender},我想说:{msg}')

class Student(Person):
    def __init__(self, name, age, gender, stu_id, grade):
        # 在子类中,有两种方式去调用父类的初始化方法,来实现对继承属性:name, age, gender 初始化操作。
        # 方式1: (更推荐)
        super().__init__(name, age, gender)

        # 方式2
        # Person.__init__(self, name, age, gender)

        # 子类独有的属性,需要自己手动完成初始化
        self.stu_id = stu_id
        self.grade = grade

    def study(self):
        print(f'我叫{self.name},我在努力地学习,争取做到{self.grade}。做到年级第一名')

# 创建speak方法的过程:1、实例自身(s1) ==》 2. Student类 ==> Person类
s1 = Student('陈振', 34, '男', '20250115', '大一')
print(s1.__dict__)  # {'name': '陈振', 'gender': '男', 'age': 34, 'stu_id': '20250115', 'grade': '大一'}
print(type(s1))     # <class '__main__.Student'>

# 查找speak方法的过程: 1.实例自身(s1)==> 2、Student类 ==> 3.Person类
s1.speak('hello') # 我叫陈振, 年龄是34, 性别是男,我想说:hello
print(s1.__dict__)  # {'name': '陈振', 'gender': '男', 'age': 34, 'stu_id': '20250115', 'grade': '大一'}

# 查找study方法的过程: 1.实例自身(s1)==> 2、Student类 ==> 3.Person类
s1.study()  # 我叫陈振,我在努力地学习,争取做到大一。做到年级第一名
print('------------------11')

# 12. 方法重写
class Person:
    def __init__(self, name, age, gender):
        self.name = name
        self.gender = gender
        self.age = age

    def speak(self, msg):
        print(f'我叫{self.name}, 年龄是{self.age}, 性别是{self.gender},我想说:{msg}')

class Student(Person):
    def __init__(self, name, age, gender, stu_id, grade):
        super().__init__(name, age, gender)
        self.stu_id = stu_id
        self.grade = grade

    def speak(self, msg):
        super().speak(msg)
        print(f'我是学生,我的学号是{self.stu_id}, 我正在读{self.grade},我想说{msg}')

s1 = Student('陈振', 34, '男', '20250115', '大一')
# 我叫陈振, 年龄是34, 性别是男,我想说:好好学习
# 我是学生,我的学号是20250115, 我正在读大一,我想说好好学习
s1.speak('好好学习')
print('------------------12')

# 13. 两个常用方法
class Person:
    def __init__(self, name, age, gender):
        self.name = name
        self.gender = gender
        self.age = age

class Student(Person):
    def __init__(self, name, age, gender, stu_id, grade):
        super().__init__(name, age, gender)
        self.stu_id = stu_id
        self.grade = grade

p1 = Person('张三', 19, '男')
s1 = Student('陈振', 34, '男', '20250115', '大一')
# 方法1:isinstance(instance, Class),作用:判断某个对象是否为指定类或其子类的实例。
print(isinstance(s1, Student))  # True
print(isinstance(p1, Person))   # True
print(isinstance(s1, Person))   # True
print(isinstance(p1, Student))  # False
# 方法2:issubclass(Class1, Class2), 作用:判断某个类是另一个类的子类。
print(issubclass(Student, Person))  # True
print(issubclass(Person, Student))  # False
print('------------------13')

# 14. 多重继承
class Person:
    def __init__(self, name, age, gender):
        self.name = name
        self.gender = gender
        self.age = age

    def speak(self):
        print(f'我叫{self.name}, 年龄是{self.age}, 性别是{self.gender}')

class Worker:
    def __init__(self, company):
        self.company = company

    def do_work(self):
        print(f'我叫{self.company}做兼职!')

class Student(Person, Worker):
    def __init__(self, name, age, gender, company, stu_id, grade):
        Person.__init__(self, name, age, gender)
        Worker.__init__(self, company)
        self.stu_id = stu_id
        self.grade = grade
    def study(self):
        print(f'我在努力的学生,争取做到{self.grade}年级的第一名')

s1 = Student('陈振', 34, '男', '京东', '20250115', '大一')
print(s1.__dict__)  # {'name': '陈振', 'gender': '男', 'age': 34, 'company': '京东', 'stu_id': '20250115', 'grade': '大一'}
s1.speak()  # 我叫陈振, 年龄是34, 性别是男
s1.do_work()    # 我叫京东做兼职!
s1.study()  # 我在努力的学生,争取做到大一年级的第一名

# 类的__mro__属性:用于记录属性和方法的查找顺序。
# 通过实例去查找属性或方法时,会先在实例身上去查找,如果没有,就按照__mro__记录的顺序去查找。
print(Student.__mro__)  # (<class '__main__.Student'>, <class '__main__.Person'>, <class '__main__.Worker'>, <class 'object'>)
print('------------------14')

# 15. 三种访问权限
class Person:
    def __init__(self, name, age, idcard):
        self.name = name    # 公共属性:当前类中,子类中,类外部,都可以访问
        self._age = age     # 受保护的属性:当前类中,子类中,都可以访问。
        self.__idcard = idcard  # 私有属性: 仅能在当前类中访问
    def speak(self):
        print(f'我叫:{self.name},年龄:{self._age}身份证{self.__idcard}')

class Student(Person):
    def hello(self):
        print(f'我是学生({self.name}-{self.age})')

p1 = Person('张三', 18, '4564641631456413')
print(p1.name)
# 在类的外部,如果强制访问【受保护的属性】也能访问,但十分不推荐!ddddddddddddd
print(p1._age)  # 18
# 在累的外部,如果强制访问【私有属性】不能访问到,而且会报错!
# print(p1.__idcard)  # 报错

# Python底层是通过重命名的方式,实现私有属性的。
print(p1.__dict__)  # {'name': '张三', '_age': 18, '_Person__idcard': '4564641631456413'}
# print(p1._Person_idcard)
print('------------------15')

# 16. getter和setter
class Person:
    def __init__(self, name, age, idcard):
        self.name = name    # 公共属性:当前类中,子类中,类外部,都可以访问
        self._age = age     # 受保护的属性:当前类中,子类中,都可以访问。
        self.__idcard = idcard  # 私有属性: 仅能在当前类中访问

    # 注册age属性getter方法,当访问Person实例的age属性时,下面的age方法就会被自动调用
    @property
    def age(self):
        return self._age

    # 注册age属性setter方法,当修改Person实例的age属性时,下面的age方法就会被自动调用
    @age.setter
    def age(self, value):
        if value < 188:
            self._age = value
        else:
            print('年龄非法,修改失败!')

    @property
    def idcard(self):
        # return self.__idcard
        return self.__idcard[:6] + '******' + self.__idcard[-4:]

    @idcard.setter
    def idcard(self, value):
        print('抱歉,身份证号码不允许修改,如有疑问,请联系管理员!')

p1 = Person('张三', 18, '4564641631456413')
print(p1.name)  # 张三
print(p1.age)   # 18
p1.age = 122
print(p1.age)   # 122
print(p1.idcard)    # 456464******6413
p1.idcard = '5645131683161563'  # # 抱歉,身份证号码不允许修改,如有疑问,请联系管理员!
print(p1.idcard) # 456464******6413
print('------------------16')

# 17. 魔法方法(魔术方法)
# 概念:以 __xxx__命名的特殊方法(双划线开头和结尾)
# 特点: 不需要我们手动调,我们只要准备好这些方法,Python会在特定的场景下,去自动调用。
class Person:
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

    # 当执行print(Person的实例对象)或 str(Person的实例对象)时调用
    def __str__(self):
        return f'{self.name}-{self.age}-{self.gender}'
    
    # 当执行len(Person的实例对象)时调用
    def __len__(self):
            return len(self.__dict__)

    # 当执行Person实例对象1 < Person实例对象2时调用
    def __lt__(self, other):
        return self.age < other.age

    # 当执行Person实例对象1 > Person实例对象2时调用
    def __gt__(self, other):
        return self.age > other.age

    # 当执行Person实例对象1 == Person实例对象2时调用
    def __eq__(self, other):
        return self.__dict__ == other.__dict__

    # 当访问Person实例对象身上不存在的属性时调用
    def __getattr__(self, item):
        return f'你访问的{item}属性不存在'

p1 = Person('陈振', 19, '男')
p3 = Person('陈振', 19, '男')
p2 = Person('闫闫', 18, '女')
print(p1)   # 陈振-19-男
print(p2)   # 闫闫-18-女
print(p1.__dict__)
res = len(p1)
print(res)
print(p1 < p2)  # False
print(p1.age < p2.age)  # False
print(p1 > p2)  # True
print(p1 == p3) # True # 原本比较的地址值是否相等。两个对象的地址值不一致返回False, __eq__重写了比较的内容
print(p1.address)   # 你访问的address属性不存在
print('------------------17')

# 18. object类
# Python中,所有的类都继承了object类,即:object类是所有类的顶层父类。
class Person:
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

# 验证一下:所有的类都继承了object类
print(issubclass(Person, object))   # True
print(issubclass(int, object))      # True
print(issubclass(str, object))      # True
print(issubclass(list, object))     # True
print(issubclass(bool, object))     # True
print(issubclass(tuple, object))    # True

# 因为object 是所有类的父类,所以Python中的所有对象,都间接是 object 类的实例。
p1 = Person('张收纳', 18,'男')
print(isinstance(p1, object))               # True
print(isinstance(100, object))              # True
print(isinstance('hello', object))          # True5
print(isinstance(True, object))             # True-
print(isinstance(None, object))             # True
print(isinstance([10, 20, 30], object))     # True
print(isinstance({'吃饭','睡觉'}, object))   # True

# 所有对象都继承了 object 类所提供的: 各种属性和方法,从而保证了每个对象都具备统一的基本能力。
for key in object.__dict__:
    print(key)      # __str__ ...

p2 = Person('李四', 19,'女')
print(p2.__dict__)  # {'name': '李四', 'age': 19, 'gender': '女'}
print(dir(p2))  # ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'gender', 'name']

print(p2.__str__()) # <__main__.Person object at 0x0000024C6CE25FD0>
print(p2)           # <__main__.Person object at 0x0000024C6CE25FD0>
print('------------------18')

# 19. 多态
# 多态的概念:同一个方法名,在不同的对象上调用时,能呈现出不同的行为。
# Python中支持:标准多态、 鸭子多态
# 19.1 标准多态
class Animal:
    def speak(self):
        print('动物正在发出声音!')

class Dog(Animal):
    def speak(self):
        print('汪汪汪')

class Cat(Animal):
    def speak(self):
        print('喵喵喵')

class Pig:
    def speak(self):
        print('哼哼哼')

def make_sound(animal:Animal): # 类型注解
    # 多条的体现
    animal.speak()

# 创建实例对象
a1 = Animal()
d1 = Dog()
c1 = Cat()
p1 = Pig()

make_sound(a1)  # 动物正在发出声音!
make_sound(d1)  # 汪汪汪
make_sound(c1)  # 喵喵喵
make_sound(p1)  # 哼哼哼    # 此行代码如果在其他语言中会报错。 Python不会报错,不推荐这样写!

# 19.2 鸭子多态
# 核心理念:如果一个东西看起来像鸭子,叫起来也像鸭子,那它就是鸭子。
# 鸭子类型是一种编程风格: 它不检查对象的类型,只关注对象能否'做某件事'(是否有对于的方法)
class Dog(Animal):
    def speak(self):
        print('汪汪汪')

class Cat(Animal):
    def speak(self):
        print('喵喵喵')

class Pig:
    def speak(self):
        print('哼哼哼')

class Fish:
    def speak(self):
        print('咕噜')

class Computer:
    def speak(self):
        print('吱吱吱')

def make_sound(animal): # 类型注解
    # 多条的体现
    animal.speak()

# 创建实例对象
d1 = Dog()
c1 = Cat()
p1 = Pig()
f1 = Fish()
cm1 = Computer()

make_sound(d1)  # 汪汪汪
make_sound(c1)  # 喵喵喵
make_sound(p1)  # 哼哼哼
make_sound(f1)  # 咕噜
make_sound(cm1) # 吱吱吱
print('------------------19')

# 20. 抽象类
# 【抽象类】是一种不能直接实例化的类,它通常作废'规范',让子类去继承,并实现其中定义的【抽象方法】
from  abc import ABC, abstractmethod
# MustRun类一旦继承了ABC类,那么MustRun类就是抽象类了。
class MustRun(ABC):
    @abstractmethod
    def run(self):
        pass

    def speak(self):
        print(f'你好,我叫:{self.name}')

class Person(MustRun):
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

    def run(self):
        print(f'我叫{self.name},我在努力的奔跑!')

p1 = Person('陈振', 18, '男')
p1.run()    # 我叫陈振,我在努力的奔跑!
print('------------------20')

# 21. 创建对象时内存分析
# 创建不可变对象时:(int float bool str tuple frozenset None)
#       ① 创建后值不可修改,每次 '修改' 实际会生产新对象。
#       ② Python会缓存部分不可变对象(如:小整数,短字符串)。复用相同的内存地址
# 创建可变对象时:(list dict set 自定义类的实例对象)
#       ① 值可原地修改,内存地址不会变
#       ② 每次创建新对象时分配独立内存,即使内容相同。

a = 666
print(id(a)) # id() 内存地址    # 2237181901712
print(hex(id(a)))  # hex() 16进制地址   # 0x208e26ebb90
b = a
print(id(b))    # 2237181901712
print(hex(id(b)))   # 0x208e26ebb90
a = 888 # int类型是不可变对象
print(a)    # 888
print(id(a))    # 2237181893904  a内存地址变了
print(b)    # 666
print(id(b))    # 2237181901712  b内存地址没变
del b

# Python中常见的不可变对象有:int float bool str tuple frozenset None
# Python中常见的可变对象有:  list dict set 自定义类的实例对象
stu_list = ['张三', '李四', '王五']
print(id(stu_list))     # 1764059403328
print(id(stu_list[0]))  # 1764058754256
stu_list[0] = '张三2'
print(id(stu_list))     # 1764059403328
print(id(stu_list[0]))  # 1764058755504
print('------------------21')

# 22. 小练习-学生管理系统
# 需求: 学生管理系统
# 包含:1.添加学生 2.删除学生 3.查看所有学生 4, 录入成绩 5.退出  请输入操作序号
class Person():
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

class Student(Person):
    count = 0

    def __init__(self, name, age, gender):
        super().__init__(name, age, gender)
        Student.count += 1
        self.stu_id = f'{datetime.now().year}{Student.count:03d}'
        self.scores = {}  # {'数学': 90, '语文': 80, '英语': 70}

    # 给当前学生添加成绩
    def add_score(self, subject, score):
        self.scores[subject] = score

    # 计算平均分
    def calcu_avg(self):
        if self.scores:
            return sum(self.scores.values()) / len(self.scores)
        else:
            return 0

    # 魔法方法
    def __str__(self):
        return f'{self.name}-{self.age}-{self.gender},成绩是:{self.scores}, 平均分{self.calcu_avg():.1f}'

s1 = Student('陈1', 18, '男')
s2 = Student('陈2', 19, '女')
s3 = Student('陈3', 20, '男')
print(s1.__dict__)  # {'name': '陈1', 'age': 18, 'gender': '男', 'stu_id': '2026001', 'scores': {}}
print(s2.__dict__)  # {'name': '陈2', 'age': 19, 'gender': '女', 'stu_id': '2026002', 'scores': {}}
print(s3.__dict__)  # {'name': '陈3', 'age': 20, 'gender': '男', 'stu_id': '2026003', 'scores': {}}

s4 = Student('陈1', 18, '男')
s4.add_score('数学', 90)
s4.add_score('语文', 80)
s4.add_score('英语', 70)
r4 = s4.calcu_avg()
print(f'平均成绩为:{r4:.1f}')
# 打印s4会调用 __str__ 方法
print(s4)   # 陈1-18-男,成绩是:{'数学': 90, '语文': 80, '英语': 70}, 平均分80.0

class Manager:
    def __init__(self):
        self.stu_list = []

    # 添加学生
    def add_student(self):
        name = input('请输入姓名:')
        age = input('请输入年龄:')
        gender = input('请输入性别:')
        stu = Student(name, age, gender)
        self.stu_list.append(stu)
        print(f'添加成功!学号是:{stu.stu_id}')

    # 删除学生
    def del_student(self):
        sid = input('请输入学号:')
        target = None
        for stu in self.stu_list:
            if stu.stu_id == sid:
                target = stu
        if target:
            self.stu_list.remove(target)
            print('删除成功!')
        else:
            print('学号有误,删除失效!')

    # 展示所有学生信息
    def show_all_student(self):
        if self.stu_list:
            for stu in self.stu_list:
                print(stu)
        else:
            print('暂无学生数据')

    # 给指定学生设置成绩
    def set_score(self):
        sid = input('请输入学号:')
        for stu in self.stu_list:
            if stu.stu_id == sid:
                score_str = input('请输入成绩(学科-分数,学科-分数)')
                score_list = score_str.replace(',', ',').split(',')
                for item in score_list:
                    subject, score = item.split('-')
                    subject = subject.strip()
                    score = float(score.strip())
                    stu.add_score(subject, score)
                print('添加成功!')
                return
        print('学号有误!')

    #
    def run(self):
        while True:
            print('*************学生管理系统***********************')
            print('1. 添加学生')
            print('2. 删除学生')
            print('3. 查询所有学生')
            print('4. 录入成绩')
            print('5. 退出')

            chocie = input('请输入操作编号:')
            if chocie == '1':
                self.add_student()
            elif chocie == '2':
                self.del_student()
            elif chocie == '3':
                self.show_all_student()
            elif chocie == '4':
                self.set_score()
            elif chocie == '5':
                print('再见')
                break
            else:
                print('输入有误')
# 运行系统
m1 = Manager()
m1.run()

6. 函数进阶

目录:

函数进阶内容

1.重新认识函数

  1. 函数的多返回值 & 参数的打包与解包

  2. 高阶函数

  3. 条件表达式

  4. 匿名函数

  5. 数据处理函数

6.1 map 函数

6.2 filter函数

6.3 sorted函数

6.4 reduce函数

  1. 列表推导式

  2. 常用内置函数

  3. 赋值 和 浅拷贝 和 深拷贝

  4. 四种作用域 LEGB

  5. 闭包

12.装饰器

12.1 函数装饰器

12.2 类装饰器

  1. 类型注解

13.1 变量类型注解

13.2 函数类型注解

python 复制代码
# 目录:
# 函数进阶内容
# 1.重新认识函数
# 2. 函数的多返回值 & 参数的打包与解包
# 3. 高阶函数
# 4. 条件表达式
# 5. 匿名函数
# 6. 数据处理函数
#   6.1 map 函数
#   6.2 filter函数
#   6.3 sorted函数
#   6.4 reduce函数
# 7. 列表推导式
# 8. 常用内置函数
# 9. 赋值 和 浅拷贝 和 深拷贝
# 10. 四种作用域 LEGB
# 11. 闭包
# 12.装饰器
#   12.1 函数装饰器
#   12.2 类装饰器
# 13. 类型注解
#   13.1 变量类型注解
#   13.2 函数类型注解


# 1.重新认识函数
# 1.1 函数也是对象
a1 = 100        # int类的实例对象
a2 = 'hello'    # str类的实例对象
a3 = [10, 20, 30]   # list类的实例对象

def welcome():
    print('hello python')   # welcome函数是function类的实例对象

print(type(a1)) # <class 'int'>
print(type(a2)) # <class 'str'>
print(type(a3)) # <class 'list'>
print(type(welcome))    # <class 'function'>

# 1.2 函数可以动态添加属性
def welcome():
    print('hello python')
welcome.desc = '这是一个打招呼的函数'
welcome.version = '1.0.0'
print(welcome.desc)     # 这是一个打招呼的函数
print(welcome.version)  # 1.0.0

# 1.3. 函数可以赋值给变量
def welcome():
    print('hello python')
welcome.desc = '这是一个打招呼的函数'
welcome.version = '1.0.0'

say_hello = welcome
say_hello()
print(say_hello.desc)     # 这是一个打招呼的函数
print(say_hello.version)  # 1.0.0

# 1.4 可变参数 vs 不可变参数
# 不可变参数
a = 666
def welcome(data):
    print('data修改前', data, id(data))
    data = 888
    print('data修改后', data, id(data))

print('函数调用前', a, id(a))  # 666
welcome(a)  # 666  888
print('函数调用后', a, id(a)) # 666  # int类型是不可变对象。 data是888

# 可变对象
a = [10, 20, 30]
def welcome(data):
    print('data修改前', data, id(data))
    data[2] = 99
    print('data修改后', data, id(data))

print('函数调用前', a, id(a))  # [10, 20, 30]
welcome(a)  # [10, 20, 30]  [10, 20, 99]
print('函数调用后', a, id(a)) # [10, 20, 99]  # a类型是可变对象。引用地址没变

# 1.5 函数也可以作为参数
def welcome():
    print('hello')

def caller(f):
    print('caller函数调用了')
    f()

caller(welcome)
# caller函数调用了
# hello

# 1.6 函数也可以作为返回值
def welcome():
    print('hello')
    def show_msg(msg):
        print(msg)
    return show_msg

result = welcome()
result('尚硅谷')
# hello
# 尚硅谷
print('------------------1')

# 2. 函数的多返回值 & 参数的打包与解包
# 2.1 函数的多返回值
def calculate(x, y):
    res1 = x + y
    res2 = x - y
    return res1, res2


result = calculate(1, 2)
print(result)   # (3, -1)
r1, r2 = calculate(1, 2)
print(r1, r2)   # 3 -1

# 2.2 参数的打包与解包
# ①. 打包接收参数:
# *args : 打包所有的位置参数(会形成一个元组)
# **kwargs : 打包所有的关键字参数(会形成一个字典)
def show_info(*args, **kwargs):
    print(args)
    print(kwargs)
# (10, 20, 30)
# {'name': '张三', 'age': 18, 'gender': '男'}
show_info(10, 20, 30, name = '张三', age = 18, gender = '男')

# ②.解包传递参数
# *变量名 :将元组拆解成一个一个独立的位置参数。
# **变量名: 将字典拆解一个一个 key = value 形式的关键字参数。
def show_info(num1, num2, num3, name, age, gender):
    print(num1, num2, num3)
    print(name, age, gender)

nums = [1, 2, 3]
person = {'name':'张三', 'age':18, 'gender':'男'}

# 1 2 3
# 张三 18 男
show_info(*nums, **person)

# ③.打包接收参数 和 解包传递参数,一起使用。
def show_info(*args, **kwargs):
    print(args)
    print(kwargs)

nums = [1, 2, 3]
person = {'name':'张三', 'age':18, 'gender':'男'}

# (1, 2, 3)
# {'name': '张三', 'age': 18, 'gender': '男'}
show_info(*nums, **person)
print('------------------2')

# 3. 高阶函数
# 当一个函数的 【参数是函数】 或 【返回值是函数】那该函数就是 【高阶函数】
# 高阶函数的意义:
# ① 代码复用性高:可以把行为 '独立出去', 传入不同函数实现不同逻辑
# ② 能让函数更灵活,更通用
# ③ 高阶函数是: 装饰器,闭包的基础(后续讲)

def info(msg):
    print('[提示]' + msg)

def warn(msg):
    print('[警告]' + msg)

def error(msg):
    print('[错误]' + msg)

def log(func, text):
    func(text)

log(info, '文件保存成功!')    # [提示]文件保存成功!
log(warn, '磁盘空间不足!')    # [警告]磁盘空间不足!
log(error, '该用户不存在!')   # [错误]该用户不存在!
print('------------------3')

# 4. 条件表达式
# 表达式: 执行后能得到值的代码,就是表达式(表达式最终会形成一个值,可以写在任何需要值的地方)
a1 = 3 + 5
a2 = 'abc' * 3
print(5 > 3)
int('y' in  'Python')
a5 = len('hello')
# 条件表达式:根据条件的真假,在两个值中二选一的表达式(又称: 三元表达式、三目运算符)。
age = 21
# 传统的if-else去写
if age >= 18:
    text = '成年'
else:
    text = '未成年'
print(text)
# 条件表达式去写: 值1 if 条件 else 值2
text = '成年' if age >= 18 else '未成年'
print(text)
print('------------------4')

# 5. 匿名函数
# 概念:所谓【匿名函数】,就是没有名字的函数,它无需使用 def 关键字去定义。
# 语法: Python中使用 lambda 关键字去定义【匿名函数】。
# 格式为: lambda arguments:expression
# lambda是 Python 的关键字,用于定义 lambda 函数。
# arguments 是参数列表,可以包含零个或多个参数,但必须在冒号(:)前指定。
# expression 是一个表达式,用于计算并返回函数的结果。
# 使用场景:当一个函数只用一次,只做一点点小事,使用匿名函数会更简洁。

# 使用普通函数实现计算效果。
# def add(x, y):
#     return x + y
#
# def sub(x, y):
#     return x - y
#
# def cal(func, a, b):
#     print(f'计算结果为:{func(a, b)}')
#
# cal(add, 30 ,20)    # 计算结果为:50
# cal(sub, 30 ,20)    # 计算结果为:10

# 匿名函数 示例
add1 = lambda x, y : x + y
add2 = lambda x : x + x
add3 = lambda: '我是add3函数'

result1 = add1(30, 10)
result2 = add2(30)
result3 = add3()
print(result1, result2, result3)    # 40 60 我是add3函数

# 改造: 使用普通函数实现计算效果。
add = lambda x, y : x + y
sub = lambda x, y : x - y

cal = lambda func, a, b : print(f'计算结果为:{func(a, b)}')
cal(add, 1 , 2 )    # 计算结果为:3
cal(sub, 1 , 2 )    # 计算结果为:-1

def cal1(func, a, b):
    print(f'计算结果为:{func(a, b)}')

cal1(add, 3, 5)     # 计算结果为:8
cal1(sub, 3, 5)     # 计算结果为:-2

cal1(lambda x, y: x + y, 30, 10)    # 计算结果为:40
cal1(lambda x, y: x - y, 30, 10)    # 计算结果为:20

# 注意点:
# 1. 只能写一行,不能写多行代码
# 2. 不能写代码块 (if, for, while)
# 3. 冒号右边必须是表达式,且只能写一个表达式。
# 4. 表达式结果自动作为返回值。

# 三元表达式
is_adult = '成年' if age >= 18 else '未成年'
# lambda表达式
is_adult1 = lambda age: '成年' if age >= 18 else '未成年'
print(is_adult1(19))        # 成年
print(is_adult1(14))        # 未成年
print('------------------5')


# 6. 数据处理函数
# 6.1 map 函数
# 对一组刷数据中的每一个元素,统一执行某种操作(加工),并生成一组新数据。不改变原数据。
# 语法: map(操作函数, 可迭代对象)

# ① 统一数据处理
nums = [1, 2, 3]
def double(x):
    return x * 10
# map函数返回值是一个迭代器对象,需要我们手动去遍历,或者手动转换类型。
result = map(double, nums)
for item in result:
    print(item, end= ' ')   # 10 20 30

# 转换成list 列表
print(list(result))  # 实际会打印 []   不打印 [10, 20, 30]的原因:
# 在Python中, map对象是一个迭代器(Iterator),它的特点是: 只能遍历一次。 当第一次通过for循环遍历result后,
# 迭代器已经到达末尾,此时再次调用 list(result) 会得到一个空列表。

# 使用匿名函数改造
result = map(lambda x: x * 2, nums)
print(list(result)) # [2, 4, 6]
print(nums) # [1, 2, 3]  为改变原来的数据。

# ② 字符串转换
names  = ('Python', 'Java', 'JS')  # 元组
result = map(lambda x : x.upper(), names)
print(tuple(result))    # 元组: ('PYTHON', 'JAVA', 'JS')
# print(list(result))   # 列表: ['PYTHON', 'JAVA', 'JS']
# print(set(result))    # 集合:不保证顺序  {'JS', 'JAVA', 'PYTHON'}
print(names)            # ('Python', 'Java', 'JS')

# ③ 类型转换
str_number = {'1', '2', '3'}
result = map(int, str_number)  # result = map(int, str_number) 直接使用int()作为映射函数, 结果与下面lambda相同
# result = map(lambda x: int(x), str_number)
print(set(result))  # {1, 2, 3}

# map函数注意点:
# 1、延迟执行: map不会立刻计算,只有在 '需要结果'时才执行计算。
# 2、返回的是迭代器对象,且一旦遍历完成,就会被 '耗尽'
# 3、map不会影响元素数量。只会对各个原始的元素做操作。
print('------------------6.1')

# 6.2 filter函数
# 从一组数据中,筛选出符合条件的元素(过滤),并组成一组新数据。不改变原数据。
# 语法格式: filter(过滤函数, 可迭代对象)

# ① 筛选 数值
nums = [1, 2, 3, 4, 5]
result = filter(lambda x:x > 3, nums)
print(list(result))     # [4, 5]
print(nums)             # [1, 2, 3, 4, 5]  原有数据不变

# ② 筛选 成年人
persons = [
    {'name':'陈1', 'age': 15, 'gender': '男'},
    {'name':'陈2', 'age': 20, 'gender': '女'},
    {'name':'陈3', 'age': 18, 'gender': '男'},
    {'name':'陈4', 'age': 12, 'gender': '女'},
    {'name':'陈5', 'age': 27, 'gender': '男'}
]
result = filter(lambda p: p['age'] >= 18, persons)
print(tuple(result)) # ({'name': '陈2', 'age': 20, 'gender': '女'}, {'name': '陈3', 'age': 18, 'gender': '男'}, {'name': '陈5', 'age': 27, 'gender': '男'})

# ③ 过滤 非法字符串
names = ['张三', '','李四', None, '王五']
result = filter(lambda n: n, names)   # 梳理下: 张三进入转为bool值为 'True' 保留。  但是: '' 和 None转为 bool 为 'False' ,被过滤掉了
print(tuple(result))    # ('张三', '李四', '王五')
print(tuple(result))    # ()

# filter函数注意点:
# 1、延迟执行:filter不会立刻筛选,只有在 '需要结果' 时,才执行。
# 2、返回的是迭代器对象,且一旦遍历完成就会被 '耗尽'
# 3、filter可能会影响元素数量。

# filter函数的特殊用法:
# 如果不传递过滤函数,那么会自动过滤掉 '假值'
datas = [0, 1, '', 'hello', None, [], {}, (), 5]
result = filter(None, datas)
print(list(result))     # [1, 'hello', 5]
print('------------------6.2')

# 6.3 sorted函数
# 对一组数据进行排序,返回一组新数据。
# 语法格式L sorted(可迭代对象, key = xxx, reverse = xxx)

# ① 数字排序
nums = [30, 40, 20, 10]
r = sorted(nums)
print(r)  # [10, 20, 30, 40]
r1 = sorted(nums, reverse=True)
print(r1)   # [40, 30, 20, 10]

# ② 按照字符串的长度去排序
names = ['python', 'sql', 'java']
r3 = sorted(names, key=len, reverse=False)
print(r3)   # ['sql', 'java', 'python']
r4 = sorted(names, key=len, reverse=True)
print(r4)   # ['python', 'java', 'sql']
r5 = sorted(names, key=lambda x:len(x))
print(r5)   # ['sql', 'java', 'python']

# ③ 根据字典中的某个字段进行排序
persons = [
    {'name':'陈1', 'age': 15, 'gender': '男'},
    {'name':'陈2', 'age': 20, 'gender': '女'},
    {'name':'陈3', 'age': 18, 'gender': '男'},
    {'name':'陈4', 'age': 12, 'gender': '女'},
    {'name':'陈5', 'age': 27, 'gender': '男'}
]
p1 = sorted(persons, key = lambda x:x['age'], reverse = False)
print(p1) # [{'name': '陈4', 'age': 12, 'gender': '女'}, {'name': '陈1', 'age': 15, 'gender': '男'}, {'name': '陈3', 'age': 18, 'gender': '男'}, {'name': '陈2', 'age': 20, 'gender': '女'}, {'name': '陈5', 'age': 27, 'gender': '男'}]

# max函数,min函数,也可以传递key参数,用于设置筛选依据
m1 = max(persons, key = lambda x:x['age'])
print(m1)   # {'name': '陈5', 'age': 27, 'gender': '男'}

mi1 = min(persons, key = lambda x:x['age'])
print(mi1)   # {'name': '陈4', 'age': 12, 'gender': '女'}
print('------------------6.3')

# 6.4 reduce函数
# 将一组数据不断 '合并', 最终归并成一个结果。
# 语法格式: reduce(合并函数, 可迭代对象,初始值)
# 备注:reduce函数需要从 functools 模块中引入才能使用。

# 从functools模块中引入 reduce
from functools import reduce
# ① 数值统计
nums = [1, 2, 3, 4, 5]
result = reduce(lambda x,y: x + y, nums, 10)
print(result)   # 25

# ② 字符串拼接
str_list = ['ab', 'cd', 'ef']
s = reduce(lambda x, y: x +y, str_list)
print(s)    # abcdef
print('------------------6')

# 7. 列表推导式
# 定义: 用一条简洁语句,从可迭代对象中,生成新列表的语法结构。
# 备注:列表推导式本质是对 for 循环 + append() 的一种简写形式。
# 语法格式: [表达式 for 变量 in 可迭代对象]

# 7.1 列表推导式 最常用
# ① 需求:让列表中每个元素,都变为原来的2倍, 得到是一个新的列表。

# 方式1:用map函数
nums = [1, 2, 3, 4, 5]
r = map(lambda x: x * 2, nums)
print(list(r))  # [2, 4, 6, 8, 10]
print(nums) # [1, 2, 3, 4, 5]

# 方式2:用for循环 + append
r = []
for item in nums:
    r.append(item * 2 )
print(r)    # [2, 4, 6, 8, 10]

# 方式3:用列表推导式
r1 = [n * 2 for n in nums]
print(r1)   # [2, 4, 6, 8, 10]

# ② 带条件的列表推导式
r2 = [n * 10  for n in nums if n >2]
print(r2)   # [30, 40, 50]

# 7.2 字典推导式
names = ['陈1', '陈2', '陈3']
scores = [70, 80, 90]
r3 = {names[i]:scores[i] for i in range(len(names))}
print(r3)   # {'陈1': 70, '陈2': 80, '陈3': 90}

# 7.3 集合推导式
names = {'陈1', '陈2', '陈3'}
r4 = { i + '!' for i in names}
print(r4)   # {'陈3!', '陈2!', '陈1!'}

# 注意:Python中没有元组推导式,下面这种写法叫: 生成器(后面讲)
names = ('陈1', '陈2', '陈3')
r5 = ( i+'!' for i in names)
print(r5)   # <generator object <genexpr> at 0x0000028FD9AAB6B0>
print('------------------7')

# 8. 常用内置函数
# 8.1 输入和输出
# ① print() : 输出指定内容
# 完整参数: print(*object, sep=' ', end='\n', file=sys.stdout, flush=False):
# 参数详解:
# 1. objects ; 要输出的内容
# 2. sep : 分割符
# 3. end : 结束符
# 4. file : 输出位置
# 5. flush : 是否立即刷新

# f =  open('a.txt','w', encoding='utf-8')
# print(10, 20, 30, 40, sep='-', end = '!', file = f)   # a.txt文件中输入  10-20-30-40!

import  time
# 第一种进度条
print('加载中', end='')
for index in range(5):
    print(',', end='', flush=True)
    time.sleep(0.01)
print('完成!', end='')
# 输出: 加载中,,,,,完成!

print()
# 第二种进度条
for index in range(1, 101):
    print(f'\r已加载{index}%', end= '', flush=True)
    time.sleep(0.01)
# ② input() : 获取用户输入

# 8.2 类型转换
# int()   : 转为整数
# float() : 转为浮点数
# str()   : 转为字符串
# bool    : 转为布尔值
# list()  : 转为列表
# tuple() : 转为元组
# set()   : 转为集合
# dict()  : 转为字典

# 8.3 数学相关
# abs()     : 取绝对值
print(abs(-9))      # 9
print(abs(-2.5))    # 2.5
print(abs(3 - 5 ))   # 2

# round     : 四舍五入
# 注意: round函数的四舍五入,是银行家舍入法: 小于5就舍, 大于5就入, 等于5看奇偶(奇入偶舍) (为了数据平衡)
print(round(3.4))   # 3
print(round(4.6))   # 5
print(round(6.5))   # 6
print(round(7.5))   # 8

# pow()     : 次方
print(pow(2, 3))        # 8   (2的3次方)
print(pow(2, -1))       # 0,5   (2的-1次方)
print(pow(2, 0.5))      # 1.4142135623730951    (2的开平方)
print(pow(2, 3, 5))     # 3   (2的3次方对5取模)

# divmod()  : 商和余数
# max()     : 最大值(支持 key 函数)
# min()     : 最小值(支持 key 函数)
# sum()     : 求和
# map()     : 加工一组数据
# filter()  : 按条件过滤数据(支持 key 函数)
# reduce()  : 合并计算(需导入 functools)
# sorted()  : 排序 (支持 key 函数)

# 8.4 数据容器相关
# len()     : 获取容器内元素的个数
# range()   : 生成一个数字序列(可用于循环)
for index in range(0, 10, 2):  # 2 为步长
    print(index, end= ' ')  # 0 2 4 6 8

# enumerate()   : 给序列添加索引
# zip()         : 将多个序列一一配对
names = ('陈1', '陈2', '陈3')
scores = [70, 80, 90]
r = zip(names, scores)
for index in r:
    print(index, end= ' ')      # ('陈1', 70) ('陈2', 80) ('陈3', 90)

# 8.5 类型判断与对象相关
# type()        : 查看类型
# isinstance()  : 判断类型
# issubclass()  : 判断两个类的继承关系
# id()          ; 查看对象的内存地址

# 8.6 逻辑判断相关
# all()     : 全为 真 返回 True
l1 = [10, '陈振', {1, 2, 3}, -9]
print(all(l1))  # True

# ang()     : 有一个为真即可
l2 = [0, '', None, False, 10]
print(any(l2))  # True

# 8.7 字符串相关
# ord()     : 获取字符串的 Unicode编码值
# chr()     : 将 Unicode编码值转为字符串
print('------------------8')

# 9. 赋值 和 浅拷贝 和 深拷贝
# ① 赋值  =
# 解释: 赋值操作只是创建了一个新的引用,指向同一个对象.
# 特点:
#   1.新变量和原变量指向了同一个内存地址.
#   2.修改其中一个会影响另一个
#   3.对于可变对象(列表,字典,集合等)尤其需要注意
a = [1, 2, [3, 4]]
b = a  # 赋值操作
a[0] = 10
print(a)  # 输出: [10, 2, [3, 4]]
print(b)  # 输出: [10, 2, [3, 4]] - b也跟着改变了

# ② 浅拷贝 copy  (Shallow Copy)
# 浅拷贝会创建一个新对象(顶层容器),但嵌套对象(子对象)的引用保持不变.
# 新对象与原对象共享子对象的内存地址.
# 修改子对象的值会影响原对象, 但修改顶层容器的引用不会影响原对象.
# 简单来说: 浅拷贝会创建一个新对象,但只拷贝第一次内容,对于嵌套的对象仍然使用引入.
import copy

a = [1, 2, [3, 4]]
b = copy.copy(a)  # 浅拷贝

a[0] = 10
print(b)  # 输出: [1, 2, [3, 4]] - b的第一层不受影响

a[2][0] = 30
print(b)  # 输出: [1, 2, [30, 4]] - 嵌套列表被修改了

# 底层原理:
# 浅拷贝仅复制顶层容器的结构,不会递归复制子对象.
# 新对象的每个元素是原对象的引入(即指向相同的内存地址).

# 创建拷贝的方法:
# 1、copy.copy()
# 2、列表的切片操作 list[:]
# 3、字典的 dict.copy() 方法
# 4、集合的 set.copy() 方法
# 5、构造函数

# ③ 深拷贝: deepcopy (Deep Copy)
# 深拷贝会创建一个完全独立的新对象, 递归复制所有嵌套对象,确保新对象与原对象及其子对象无任何引用关系.
# 修改原对象的任何层级都不会影响原对象。
# 简单来说:深拷贝会递归地拷贝所有嵌套对象,创建一个完全独立的新对象。

# 底层原理:
# 通过递归遍历原对象的所有层级,逐层复制每个子对象.生成全新的内存地址.
# 对于不可变对象(如 数值类型,字符串等)不会复制, 而是直接引用原值.(因为不可变对象无法修改)
# 对于可变对象(如 列表, 字典, 集合),深拷贝会逐层复制,确保完全独立.

import copy

a = [1, 2, [3, 4]]
b = copy.deepcopy(a)  # 深拷贝

a[0] = 10
a[2][0] = 30
print(a)  # 输出: [10, 2, [30, 4]]
print(b)  # 输出: [1, 2, [3, 4]] - b完全不受影响

# 深拷贝的特点:
# 创建完全独立的对象副本
# 修改原对象不会影响拷贝后的对象
# 对于嵌套结构特别有用
# 性能开销比浅拷贝大

# 总结: 3种拷贝方式的使用场景
# 赋值: 当你确实需要共享同一个对象时
# 浅拷贝: 当对象没有嵌套结构 或 你确定需要共享嵌套对象时
# 深拷贝: 当需要完全独立的副本,特别是对象包含嵌套结构时.
import copy

original = [1, [2, 3], {'a': 4}]

# 赋值
assigned = original

# 浅拷贝
shallow_copied = copy.copy(original)

# 深拷贝
deep_copied = copy.deepcopy(original)

# 修改原始对象
original[0] = 10
original[1][0] = 20
original[2]['a'] = 40

# [10, [20, 3], {'a': 40}]
# [1, [20, 3], {'a': 40}]
# [1, [2, 3], {'a': 4}]

print("赋值后的:", assigned)        # 完全跟随变化            [10, [20, 3], {'a': 40}]
print("浅拷贝的:", shallow_copied)  # 第一层不变,嵌套对象变化  [1, [20, 3], {'a': 40}]
print("深拷贝的:", deep_copied)     # 完全不变                [1, [2, 3], {'a': 4}]

# 注意:
# 元组中如果只包含不可变对象,则深拷贝没有效果(地址值一样)
nums1 = (10, 20, 30, 40, 50)
nums2 = copy.deepcopy(nums1)
print(id(nums1))    # 2538809262784
print(id(nums2))    # 2538809262784

# 包含可变对象(列表),地址值不一样
nums3 = (10, 20, 30, 40, 50, [60, 70])
nums4 = copy.deepcopy(nums3)
print(id(nums3))    # 2919098428352
print(id(nums4))    # 2919098426720
print('------------------9')

# 10. 四种作用域 LEGB
# ① 局部作用域 (Local Scope)
# 定义: 函数或方法内部定义的变量
# 特点: 仅在函数内有效,函数执行结束后销毁.

# ② 嵌套作用域 (Enclosing Scope)
# 定义: 闭包函数中,外层函数(非全局)的变量
# 特点: 通过nonlocal关键字修改

# ③ 全局作用域 (Global Scope)
# 定义: 模块顶层定义的变量.
# 特点: 通过 gloabl 关键字在函数内修改.

# ④ 内置作用域 (Built-in Scope)
# 定义: Python内置函数和异常(如 print / len / ValueError)
# 特点: 无需定义,即可全局(所有.py文件)调用.

# 作用域优先级: ( LEGB 规则)
# Python按 L -> E -> G -> B的顺序查找变量.
# Local‌(局部) → 2. Enclosing‌(嵌套) → 3. Global‌(全局) → 4. Built-in‌(内置)。
# ‌变量覆盖‌:局部变量与全局变量同名时,优先使用局部变量。
# ‌修改作用域‌:通过global和nonlocal关键字显式声明。

a = 100
def test(b):
    print('我是test函数')
    print('test中打印的a是', a)  # 100
    print('test中收到的参数b是', b)    # 66
    c = 200
    d = 300
    print('test中的c和d是: ', c, d) # 200 300
    def inner():
        a = 888
        e = 400
        nonlocal c
        c = 999
        print('inner中的e是', e)   # 400
        print('inner中打印的c是', c) # 999
        print('#############', a)   # 100
    inner()
    print('#############', c)  # 999
print('全局打印的a是', a) # 100
test(66)
# 全局打印的a是 100
# 我是test函数
# test中打印的a是 100
# test中收到的参数b是 66
# test中的c和d是:  200 300
# inner中的e是 400
# inner中打印的c是 999
# ############# 100
# ############# 999
print('------------------10')

# 11. 闭包
# 前置知识1
# 每次调用函数时,Python都会为函数创建一个新的局部作用域
# 函数执行完毕后,这个局部作用域会被销毁,其中的局部变量也会随之被释放.
# def outer():
#     num = 10
#     num += 1
#     print(num)
#
# outer() # 11
# outer() # 11
# outer() # 11

# 前置知识2
# 在Python中, [内层函数]可以访问其[外层函数]作用域中的变量
# 访问外层函数变量,无需使用nonlocal 但是修改外层变量时要是有 nonlocal
# def outer():
#     num = 10
#     def inner():
#         nonlocal num
#         num = 99
#         print(1, num)   # 1 99
#     inner()
#     print(2, num)       # 2 99
#
# outer()

# 闭包:   闭包 =  内层函数 + 被内层函数所引用的外层变量
# 闭包 = 嵌套函数 + 引用外部函数变量 + 外部函数返回嵌套函数
#   在一个外部函数里定义一个内部函数
#   内部函数使用了外部函数的变量 / 参数
#   外部函数把内部函数作为返回值返回
#   这个被返回的内部函数 + 它引用的外部变量,就叫闭包

# 闭包产生的条件:
# 要有函数嵌套
# 在 [内层函数] 中,要访问[外层函数] 的变量
# 并且 [外层函数] 要返回 [内层函数]  --- 只有返回了内层函数,闭包才能 '活下来'
def outer():
    num = 10

    def inner():
        nonlocal num
        num += 1
        print(num)
    print(inner.__closure__)
    return inner

f = outer()  # (<cell at 0x00000139F3868520: int object at 0x00007FFB3696E448>,)
f() # 11
f() # 12
f() # 13
f2 = outer()    # (<cell at 0x00000139F3869B10: int object at 0x00007FFB3696E448>,)
f2() # 11
f2() # 12
f2() # 13

# 结论:
# outer函数中,被inner所使用到的那些变量,会被封存到 [闭包单元 cell]中.
# 这些 cell 会组成一个 __closure__ 元组,最终放到了 inner 函数身上.

# 打印__closure__元组
print(f.__closure__)    # (<cell at 0x00000139F3868520: int object at 0x00007FFB3696E4A8>,)
# 打印__closure__元组中的某一项
print(f.__closure__[0]) # <cell at 0x00000139F3868520: int object at 0x00007FFB3696E4A8>
# 打印__closure__元组中的某一项的具体值
print(f.__closure__[0].cell_contents)   # 13

# 注意:
# 调用n次外层函数,就会得到n个不同的闭包,并且这些闭包之前互不影响.
# 内层函数中用到的外层变量是可变函数,多个闭包之间依然互不影响.

def outer():
    num = []

    def inner(value):
        nonlocal num
        num.append(value)
        print(num)
    return inner

f = outer()
f(10) # [11]
f(20) # [11, 22]
f(30) # [11, 22, 33]
f2 = outer()
f2(666) # 66

# 闭包的优点:
# 可以 '记住' 状态: 不用全局变量.也不用写类,就能在多次调用之间保存数据
# 可以做 '配置过的函数' : 先传一部分参数,把环境固定住,得到一个定制版函数.
# 可以实现简单的 '数据隐藏' :外层变量对外不可见,只能通过内层函数访问.
# 是装饰器 (decorator)等高级用法的基础.

def beauty(char, n):
    def show_msg(msg):
        print(char * n + msg + char * n)
    return show_msg

s1 = beauty('*' , 3)
s1('闫闫')    # ***闫闫***
s1('陈陈')    # ***陈陈***

s2 = beauty('@' , 5)
s2('铮')    # @@@@@铮@@@@@
s2('宝')    # @@@@@宝@@@@@

# 闭包的缺点:
# 理解成本较高: 对初学者不太友好,滥用会让代码难读
# 如果闭包引用了很大的对象,又长期不释放,可能会增加内存占用
# 很多场景下, 其实用 [ 类 + 实力属性 ]会更清晰,闭包不一定是最优解

class Beauty:
    def __init__(self, char, n):
        self.char = char
        self.n = n
    def show(self, msg):
        print(self.char * self.n + msg + self.char * self.n)

s2 = Beauty('*', 3)
s2.show('闫闫')  # ***闫闫***
s2.show('陈陈')  # ***陈陈***

# 闭包 vs 类
# 保存少量状态
#   闭包:推荐,轻量,代码少
#   类:重量级
# 复杂功能
#   闭包:不适合
#   类:适合
# 装饰器
#   闭包:必须用闭包
#   类:无法替代


# 闭包的核心用途:
# 保存函数状态 (计数器, 缓存, 记录历史)
# 装饰器底层实现(Python装饰器本质就是闭包)
# 工厂函数(批量生产功能相似的函数)
# 数据封装(替换简单类,比类更轻量)

# 避坑1
# 循环中创建闭包,变量延迟绑定
def f():
    funcs = []
    for i in range(3):
        def func():
            return i

        funcs.append(func)
    return funcs

f1, f2, f3 = f()
print(f1(), f2(), f3())  # 全部输出 2(不是预期 0,1,2)
# 原因:闭包延迟绑定,循环结束后 i=2,所有函数共用同一个 i
# 解决方法:给内部函数加默认参数(立即绑定)
def f():
    funcs = []
    for i in range(3):
        def func(n=i):  # 立即绑定当前 i
            return n

        funcs.append(func)
    return funcs

f1, f2, f3 = f()
print(f1(), f2(), f3())  # 0 1 2

# 避坑2 : 修改外部变量忘记加 nonlocal
# 修改必须加 nonlocal 否则会报错/创建局部变量

print('------------------11')

# 12.装饰器
# 装饰器是一种 [可调用对象](通常是函数).它能接收一个函数作为参数,并且会返回一个新函数.
# 装饰器可以在不修改原函数代码的前提下,增强或改变原函数的功能.

# 12.1 函数装饰器
# 关键点:
# 接收被装饰的函数,同时返回新函数 (wrapper)   [原函数---> 装饰器 ---> 新函数 ]
# 装饰器 '吐出来'的是wrapper函数,以后别人调用的也是 wrapper函数
# 为了保证参数的兼容性,wrapper函数要通过 *args 和 ** kwargs接收参数
# wrapper 函数中主要做的是: 调用原函数(被装饰的函数),执行其他逻辑,但是记得将原函数的返回值 return 出去.

# 实际应用:
# 在不改变原函数的前提下,给函数统一加上:日志记录、计时、校验、缓存、性能分析、权限控制
# 日志记录: 装饰器可用于记录函数的调用信息、参数和返回值。
# 性能分析: 可以使用装饰器来测量函数的执行时间。
# 权限控制: 装饰器可用于限制对某些函数的访问权限。
# 缓存: 装饰器可用于实现函数结果的缓存,以提高性能。

def say_hello(func):
    def wrapper(*args, **kwargs):
        print('hello Python')
        return func(*args, **kwargs)
    return wrapper

@say_hello
def add(x, y):
    res = x + y
    print(f'{x}和{y}相加的结果是:{res}')
    return res

# 正常调用add函数
# r = add(1, 2)
# print(r)  # 3

# 需求: 在不改变add函数的前提下,给add函数增加一些额外的功能,例如: 计算前先打印一句欢迎语
# 实现方案: 使用装饰器
r = add(1, 2)
print(r)
# hello Python
# 1和2相加的结果是:3
# 3

# 进阶1: 带参数的装饰器
# 三层嵌套,外层接收配置,中间层接收函数,内层接收具体参数
def say_hello(msg):
    def outer(func):
        def wrapper(*args, **kwargs):
            print('hello Python')
            return func(*args, **kwargs)
        return wrapper
    return outer

@say_hello('加法')
def add(x, y):
    res = x + y
    print(f'{x}和{y}相加的结果是:{res}')
    return res

@say_hello('减法')
def sub(x, y):
    res = x - y
    print(f'{x}和{y}相减的结果是:{res}')
    return res

r2 = add(3, 4)
print(r2)
# hello Python
# 3和4相加的结果是:7
# 7

r3 = sub(5, 6)
print(r3)
# hello Python
# 5和6相减的结果是:-1
# -1

# 进阶2: 多个装饰器一起使用
def test1(func):
    def wrapper(*args, **kwargs):
        print('hello test1')
        return func(*args, **kwargs)
    return wrapper

def test2(func):
    def wrapper(*args, **kwargs):
        print('hello test2')
        return func(*args, **kwargs)
    return wrapper

@test1
@test2
def add(x, y):
    res = x + y
    print(f'{x}和{y}相加的结果是:{res}')
    return res

r5 = add(7, 8)
print(r5)
# hello test1
# hello test2
# 7和8相加的结果是:15
# 15

# 12.2 类装饰器
# 包含__call__ 方法发类,就是类装饰器
# 像调用函数一样,去调用类装饰器的实例对象,就会触发__call__ 方法的调用
# __call__ 方法通常接收一个函数作为参数,并且会返回一个新函数
class SayHello:
    def __init__(self, msg):
        self.msg = msg

    def __call__(self, func):
        def wrapper(*args, **kwargs):
            print(f'hello Python- {self.msg}')
            return func(*args, **kwargs)
        return wrapper

# 使用@语法糖使用类装饰器
@SayHello('加法')
def add(x, y):
    res = x + y
    print(f'{x}和{y}相加的结果是:{res}')
    return res

r11 = add(12, 13)
print(r11)
# hello Python- 加法
# 12和13相加的结果是:25
# 25

# 多个类装饰器的使用
class Test1:
    def __call__(self, func):
        def wrapper(*args, **kwargs):
            print(f'hello Test1')
            return func(*args, **kwargs)
        return wrapper

class Test2:
    def __call__(self, func):
        def wrapper(*args, **kwargs):
            print(f'hello Test2')
            return func(*args, **kwargs)
        return wrapper

@Test1()
@Test2()
def add(x, y):
    res = x + y
    print(f'{x}和{y}相加的结果是:{res}')
    return res

r12 = add(14, 15)
print(r12)
# hello Test1
# hello Test2
# 14和15相加的结果是:29
# 29
print('------------------12')

# 13. 类型注解
# 类型注解就是在代码中注明数据类型的语法
# 目的:
# 提高代码可读性:让他人(以及未来的你)一眼就能看懂代码的意图
# 便于静态检查:在运行代码前,通过工具发现潜在的类型错误
# 增强IDE支持:让代码编辑器提供更准确的自动补全和提示

# 13.1 变量类型注解
# 给变量加上类型说明,可增强代码的可读性,让IDE的提示更友好
num: int = 100
price: float = 12.5
message: str = '陈振'
is_vip: bool = True
result: None = None # 语法上这样写没问题,但这么写没意义

# 注意: 可以先写变量的类型注解,以后再赋值
school: str
print('--')
school = 'fsf'

# hobby是列表,并且列表中的所有元素必须是 str类型
hobby1: list[str] = ['喝酒', '抽烟', '烫头']
# hobby是列表,并且列表中的所有元素必须是 str 或 int类型
hobby2: list[str | int] = ['喝酒', '抽烟', '烫头', 10]
# 上面这行代码的旧写法如下:
from typing import Union
hobby3: list[Union[str, int]] = ['喝酒', '抽烟', '烫头', 10]

# cits是集合,并且集合中所有元素必须是 str 类型
citys1: set[str] = {'北京', '上海', '深圳'}
# cits是集合,并且集合中所有元素必须是 str 或 float 或 bool 类型
citys2: set[str | float | bool] = {'北京', '上海', '深圳'}

# persons 是字典.键是str类型, 值是 int类型
persons1: dict[str, int] = {'张三': 19, '李四': 20, '王五': 18}
# persons 是字典.键是str 或 int类型, 值是 int类型
persons2: dict[str | int, int] = {'张三': 19, '李四': 20, '王五': 18}

# 注意: 元组的类型声明有点特殊
# scores 是元组, 并且元组中仅包含1个int类型的元素
scores: tuple[int] = (60,)
# scores 是元组, 并且元组中仅包含3个int类型的元素
scores: tuple[int, int, int] = (60, 70, 80)
# scores 是元组, 并且元组中仅包含任意个数的元素,但每个元素的类型必须是int
scores: tuple[int, ...] = (60, 70, 80, 90)
# scores 是元组, 并且元组中仅包含任意个数的元素,但每个元素的类型必须是int 或 str
scores: tuple[int | str, ...] = (60, 70, 80, 90, 'sb')

# Python会根据初始赋值推导变量的类型.
# 对于普通变量: 后续如果改变类型,不会警告
# 对于容器变量: 要求内部元素类型必须与推导出来的一致,否则就会警告.
x = 100
x = 'hello'
y = [10, 20, 30]
y.append('hah') # 有警告

# 13.2 函数类型注解
# 给函数的[参数]和[返回值]添加类型说明
# 语法格式: 函数名(参数1:类型,参数2:类型) -> 返回值类型:

# 示例1:设置参数类型注解,设置返回值类型注解
def add(x: int, y: int) -> int:
    return x + y

# 示例2: 参数有默认值(Python可以推导出参数的类型),设置返回值类型
def add2(x=1, y=2) -> int:
    return x + y

# 示例3:设置多个返回值的类型注解
def show_nums_info(nums: list[int]) -> tuple[int, int, float]:
    max_num = max(nums)
    min_num = min(nums)
    result = max_num - min_num
    return max_num, min_num, result

# 示例4: 设置 *args 的类型注解,要求 args 中的每个蟾酥都必须是 int类型
def add3(*args: int) -> int:
    return sum(args)

# 示例5: 设置 **kwargs 的类型注解,要求kwargs中的每组参数的值,必须是 str 或 int类型
def show_info(**kwargs: str | int):
    print(kwargs)

# 获取函数的注解信息
print(show_info.__annotations__)    # {'kwargs': str | int}

7.错误与异常

目录:

1.错误与异常

1.1 介绍

1.2 一些开发中常见的异常

1.3 Python中异常层次结构

2.异常处理

3.手动抛出异常

4.异常传递机制

5.自定义异常

python 复制代码
# 目录:
# 1.错误与异常
#   1.1 介绍
#   1.2 一些开发中常见的异常
#   1.3 Python中异常层次结构
# 2.异常处理
# 3.手动抛出异常
# 4.异常传递机制
# 5.自定义异常


import traceback

# 1.错误与异常
# 1.1 介绍
# 错误;
# 代码本身有语法错误,解释器无法执行代码. -无法通过异常处理机制解决.
# 异常:
# 代码在语法上没有问题,但执行过程中出现了问题. - 可以通过异常处理机制解决

# 1.2 一些开发中常见的异常:

# ① ZeroDivisionError : 当除数为0时触发
# num1 = 100
# num2 = 0
# result = num1 / num2

# ② TypeError : 当操作的数据类型不正确或不兼容时触发.
# result = '10' + 5

# ③ AttributeError : 当对象没有指定的属性或方法时触发.
# 演示1:
# class Person:
#     def __init__(self, name, age):
#         self.name = name
#         self.age = age
# p1 = Person('张三', 18)
# print(p1.gender)
# 演示2:
# nums = [10, 20, 30]
# nums.add(100)

# ④ IndexError : 当索引超出范围而(索引越界)时触发.
# nums = [10, 20, 30, 40]
# print(nums[4])

# ⑤ NameError : 当使用了不存在的变量时触发
# print(school)

# ⑥ KeyError : 当访问了字典中不存在的 key 时触发.
# persons = {'name': 'zs', 'age': 18}
# print(persons['gender'])

# ⑦ ValueError : 当值不合法,但类型正确时触发.
# int('hello')

# 1.3 Python中异常层次结构:

# BaseException
#  ├── BaseExceptionGroup
#  ├── GeneratorExit
#  ├── KeyboardInterrupt
#  ├── SystemExit
#  └── Exception  ---> 开发中常见的异常,基本都属于Exception
#       ├── ArithmeticError
#       │    ├── FloatingPointError
#       │    ├── OverflowError
#       │    └── ZeroDivisionError
#       ├── AssertionError
#       ├── AttributeError
#       ├── BufferError
#       ├── EOFError
#       ├── ExceptionGroup [BaseExceptionGroup]
#       ├── ImportError
#       │    └── ModuleNotFoundError
#       ├── LookupError
#       │    ├── IndexError
#       │    └── KeyError
#       ├── MemoryError
#       ├── NameError
#       │    └── UnboundLocalError
#       ├── OSError
#       │    ├── BlockingIOError
#       │    ├── ChildProcessError
#       │    ├── ConnectionError
#       │    │    ├── BrokenPipeError
#       │    │    ├── ConnectionAbortedError
#       │    │    ├── ConnectionRefusedError
#       │    │    └── ConnectionResetError
#       │    ├── FileExistsError
#       │    ├── FileNotFoundError
#       │    ├── InterruptedError
#       │    ├── IsADirectoryError
#       │    ├── NotADirectoryError
#       │    ├── PermissionError
#       │    ├── ProcessLookupError
#       │    └── TimeoutError
#       ├── ReferenceError
#       ├── RuntimeError
#       │    ├── NotImplementedError
#       │    ├── PythonFinalizationError
#       │    └── RecursionError
#       ├── StopAsyncIteration
#       ├── StopIteration
#       ├── SyntaxError
#       │    └── IndentationError
#       │         └── TabError
#       ├── SystemError
#       ├── TypeError
#       ├── ValueError
#       │    └── UnicodeError
#       │         ├── UnicodeDecodeError
#       │         ├── UnicodeEncodeError
#       │         └── UnicodeTranslateError
#       └── Warning
#            ├── BytesWarning
#            ├── DeprecationWarning
#            ├── EncodingWarning
#            ├── FutureWarning
#            ├── ImportWarning
#            ├── PendingDeprecationWarning
#            ├── ResourceWarning
#            ├── RuntimeWarning
#            ├── SyntaxWarning
#            ├── UnicodeWarning
#            └── UserWarning

# 2.异常处理
# 2.1 为什么要进行异常处理?
# ① 程序运行过程中出现的异常,如果得不到处理,那程序就会立即崩溃,导致后续代码无法执行
# ② 异常处理不是让异常消失,而是将异常捕获到,随后根据异常的具体情况,来执行指定的逻辑
# print('欢迎来到本程序')
# a = int(input('请输入第一个数:'))
# b = int(input('请输入第二个数:'))
# result = a / b
# print(f'{a}除以{b}的结果是:{result}')
# print('******我是后续操作1*******')
# print('******我是后续操作2*******')

# 2.2 异常处理(初级)
# ① 将可能出现异常的代码放在 try中,出现异常后的处理代码写在except中
# ② 如果 try 中的代码出现异常,那try中的后续代码将不会执行,并自动跳转到 except中处理异常
# ③ 如果try中的代码没有异常,那except中的代码就不会执行
# ④ 无论是否发生异常,try-except后面的代码都会继续执行.
# print('欢迎来到本程序')
# try:
#     a = int(input('请输入第一个数:'))
#     b = int(input('请输入第二个数:'))
#     result = a / b
#     print(f'{a}除以{b}的结果是:{result}')
# except:
#     print('抱歉,程序出现了异常!')
# print('******我是后续操作1*******')
# print('******我是后续操作2*******')

# 2.3 异常处理(捕获指定的类型的异常)
# print('欢迎来到本程序')
# try:
#     a = int(input('请输入第一个数:'))
#     b = int(input('请输入第二个数:'))
#     result = a / b
#     print(f'{a}除以{b}的结果是:{result}')
# except ZeroDivisionError:
#     print('程序异常,0不能作为除数!')
# except ValueError:
#     print('程序异常,你输入的必须是数字!')
# print('******我是后续操作1*******')
# print('******我是后续操作2*******')

# 2.4 验证一下异常类之间的继承关系
print(issubclass(ZeroDivisionError, ArithmeticError))   # True
print(issubclass(ZeroDivisionError, Exception))         # True
print(issubclass(ValueError, Exception))                # True
print(issubclass(KeyboardInterrupt, Exception))         # False
print(issubclass(KeyboardInterrupt, BaseException))     # True

# 2.5 多个except从上往下匹配,匹配成功后不再往下匹配
# print('欢迎来到本程序')
# try:
#     a = int(input('请输入第一个数:'))
#     b = int(input('请输入第二个数:'))
#     print(x)
#     result = a / b
#     print(f'{a}除以{b}的结果是:{result}')
# except ZeroDivisionError:
#     print('程序异常,0不能作为除数!')
# except ValueError:
#     print('程序异常,你输入的必须是数字!')
# except Exception:
#     print('程序异常!')
# print('******我是后续操作1*******')
# print('******我是后续操作2*******')

# 2.6 获取异常的具体信息
# print('欢迎来到本程序')
# try:
#     a = int(input('请输入第一个数:'))
#     b = int(input('请输入第二个数:'))
#     print(x)
#     result = a / b
#     print(f'{a}除以{b}的结果是:{result}')
# except ZeroDivisionError:
#     print('程序异常,0不能作为除数!')
# except ValueError:
#     print('程序异常,你输入的必须是数字!')
# except Exception as e:
#     print(f'程序异常, 异常信息: {e}')
#     print(f'程序异常, 异常信息: {type(e)}')
#     print(f'程序异常, 异常信息: {e.args}')
#     print(f'程序异常, 异常信息: {e.__traceback__.tb_frame.f_code.co_filename}')
#     print(f'程序异常, 异常信息,具体行数: {e.__traceback__.tb_lineno}')
#     # 后续使用 trackback 打印异常信息(回溯异常)
#     import traceback
#     print(traceback.format_exc())
# print('******我是后续操作1*******')
# print('******我是后续操作2*******')

# 2.7 一个except, 也可以捕获不同的异常
# print('欢迎来到本程序')
# try:
#     a = int(input('请输入第一个数:'))
#     b = int(input('请输入第二个数:'))
#     # print(x)
#     result = a / b
#     print(f'{a}除以{b}的结果是:{result}')
# except(ZeroDivisionError, ValueError, Exception) as e:
#     import traceback
#
#     if isinstance(e, ZeroDivisionError):
#         print('程序异常,0不能作为除数!')
#         print(traceback.format_exc())
#     elif isinstance(e, ZeroDivisionError):
#         print('程序异常,你输入的必须是数字!')
#         print(traceback.format_exc())
#     else:
#         print(f'程序异常, 异常信息: {e}')
#         print(traceback.format_exc())
# print('******我是后续操作1*******')
# print('******我是后续操作2*******')

# 2.8 异常处理的完整写法:
# try:  尝试去做可能会出现的异常的事情
# except:   出现异常时的处理(出现异常时如何补救)
# else:     如果一切顺利(没有出现异常) 要做的事
# finally:  无论有没有异常,都要做的事

# print('欢迎来到本程序')
# try:
#     a = int(input('请输入第一个数:'))
#     b = int(input('请输入第二个数:'))
#     # print(x)
#     result = a / b
#     print(f'{a}除以{b}的结果是:{result}')
# except(ZeroDivisionError, ValueError, Exception) as e:
#     import traceback
#     if isinstance(e, ZeroDivisionError):
#         print('程序异常,0不能作为除数!')
#         print(traceback.format_exc())
#     elif isinstance(e, ZeroDivisionError):
#         print('程序异常,你输入的必须是数字!')
#         print(traceback.format_exc())
#     else:
#         print(f'程序异常, 异常信息: {e}')
#         print(traceback.format_exc())
# else:
#     print('every thing is ok')
# finally:
#     print("no matter what, it's over!")
# print('******我是后续操作1*******')
# print('******我是后续操作2*******')

print('------------------2')

# 3.手动抛出异常
# 当程序遇到不符合预期情况时,可以使用 raise 语句手动触发(抛出)异常.
print('欢迎使用年龄管理系统')
try:
    age = int(input('请输入你的年龄:'))
    if 18 <= age <= 120:
        print('成年')
    elif 0 <= age < 18:
        print('未成年')
    else:
        # print('你输入的年龄有误!(年龄应该是在 0-120的整数)')
        raise ValueError('年龄应该是在 0-120的整数')
except Exception as e:
    print(f'程序异常:{e}')
    print(traceback.format_exc())

# raise NameError('名称有误')
# raise KeyboardInterrupt('键盘打扰')

# 4.异常传递机制
# 如果异常没有被当前代码块捕获处理,那该异常就会沿着调用链,逐层传递给其调用者.
# 如果所有调用者,都没有捕获该异常,那最终程序将因[未处理异常]而意外终止.

# def test1():
#     print('test1')
#     result = '100' + 100
#     print('test11')
# def test2():
#     print('test2')
#     test1()
#     print('test22')
# def test3():
#     print('test3')
#     test2()
#     print('test33')
#
# try:
#     test3()
# except Exception as e:
#     print('程序异常:', e)

# 5.自定义异常
# 由开发人员自己定义的一个异常类,用来表示代码中 '更具体,更有业务含义'的异常.
# 具体规则: 定义一个类(类名通常以 Error 结尾)继承 Exception 类或它的子类.
class SchoolNameError(Exception):
    def __init__(self, msg):
        super().__init__('[校名异常]' + msg)

def check_school_name(name):
    if len(name) > 10:
        raise SchoolNameError('学校名字过长!')
    else:
        print('学校名字合法!')

check_school_name('阜阳师范大学')
check_school_name('阜阳师范大学1111111')
相关推荐
测试19982 小时前
Jmeter接口测试:使用教程(上)
自动化测试·python·测试工具·jmeter·职场和发展·测试用例·接口测试
七夜zippoe2 小时前
量子计算入门:Qiskit框架实战
python·算法·量子计算·ibm·qiskit
Zhansiqi2 小时前
day45
python
telllong2 小时前
Termux:在手机上跑Python AI应用的真实体验
人工智能·python·智能手机
威联通网络存储7 小时前
某高端显示面板制造企业:基于威联通 TS-h2490FU 的 AOI 检测数据治理实践
python·制造
FreakStudio10 小时前
不用装软件!这款MicroPython浏览器 IDE :让你在手机上也能调试树莓派 Pico
python·单片机·嵌入式·电子diy·tinyml
m0_7434703711 小时前
使用Python进行PDF文件的处理与操作
jvm·数据库·python
数据科学小丫13 小时前
Python 数据存储操作_数据存储、补充知识点:Python 与 MySQL交互
数据库·python·mysql
Knight_AL13 小时前
Nacos 启动问题 Failed to create database ’D:\nacos\nacos\data\derby-data’
开发语言·数据库·python