内容目录

算术运算符

运算符 描述 实例
+ 10 + 20 = 30
- 10 -20 = -10
* 10 * 20 = 200
/ 10 / 20 = 0.5
// 取整数 返回除法的整数部分(商)
% 取余数 返回除法的余数
** 2 ** 3=8
print(1/1)
1.0   # 使用算数运算符/,商一定是浮点数,且除数不能为0
a = 1/1
print(type(a))
<class 'float'>

# //取整数,取商的整数部分
a = 5
b = 2
print(a // b)
2

# %取余数,只取余数部分
# **幂

print(7.0 // 2)
3.0 # 使用算术运算符,其中若有浮点数,结果也会用浮点数表示
  • 运算规则:先乘除后加减;同级运算符从左往右计算;可以用()调整计算的优先级

赋值运算符

num1 = 5
num2 = 8
# 将一个变量的值赋给另外一个变量
num3 = num1
print(num3)

a = 1
a += 1
print(a)
2

n1 = 99
n2 = 60
n1 += n2
print(n1)
159

b = 1
b -= 1
print(b)
0
# 赋值运算符必须连着写,中间不能有空格

print(10 += 3) # 纯数字不能使用,因为赋值运算符是针对变量存在的

输入函数

input(prompt) # prompt是提示,会在空控制台中显示

input("请输入:")

name = input("请输入姓名:")
print(name)

转义字符

# \t 制表符,通常表示空四个字符,也称缩进
print('yu\tjing')
yu  jing

# \n 换行符
print('yu\njing')

# \r 回车,表示将当前位置移到本行开头
print('yu\rjing')
jing

# \\反斜杠符号
print('yu\\tjing')
yu\tjing
print('yu\\\tjing')

# r原生字符串,默认取消转义
print(r'yu\\\tjing')
yu\\\tjing

if判断

# if判断基本格式
age = 17
if age < 18:
    print('未成年不能上网')

score = input('请输入成绩:')
if score == '100':
    print('你真棒!')
if score == '60':
    print('还有继续加油!')

比较(关系)运算符

# == 比较的是两个变量的值是否相等,相等的话就返回True,不相等返回False
# != 比较的是两个变量的值是否相等,不相等的话就返回True,相等返回False

a = 666
b = 999
print(a == b)
False
print(a < b)
True

if a < b:
    print('a小于b')

逻辑运算符

# and 左右两边都符合才为真
a = '哈哈'
b = '嘻嘻'
if a == '哈哈' and b == '嘻嘻':
    print('a和b都在笑!')

# or 左右两边只需要一边符合就为真
fruit = '苹果'
if fruit == '苹果' or fruit == '水蜜桃':
    print('是水果')

# not 表示相反的结果
print(not 3 > 9)
True

if else

a = 666
if a == 666:
    print("你真棒")
else:   # 后面不能添加任何条件
    print("还有继续加油")
score = 35
if 85 <= score <= 100:
    print("优秀")
elif 60 <= score < 85:
    print("及格")
elif 0 <= score < 60:
    print("不及格")
else:
    print("分数无效")
ticket = True
temp = 38.5
if ticket == True:
    print("可以进站了-->",end="")
    if 36.3 <= temp <= 37.2:
        print("体温正常,安心回家")
    else:
        print("体温异常,被抓过去隔离")
else:
    print("没票不能进站")

循环语句

i = 1
while i <= 100:
    print(i,end="")
    print(" 好好学习,天天向上")
    i += 1
i = 1
while i <= 100:
    print(f"{i} 好好学习,天天向上")
    i += 1
i = 1
sum = 0
while i <= 100:
    sum += i
    i += 1
print(sum)
i = 1
while i <= 3:
    print(f"这是第{i}次外循环")
    j = 1
    while j <= 5:
        print(f"内循环{j}次")
        j += 1
    i += 1
# 可迭代对象就是要去遍历取值的整体
str = 'hellopython'
for i in str:
    print(i)
# 用来记录循环次数,相当于一个计数器
# range()里面只写一个数,这个书就是循环的次数,默认从0开始
# 写两个数,前面的数字代表开始位置,后面的数字代表结束位置
for i in range(1,6):
    print(i)
for i in range(5):
    print(i)
sum = 0
for i in range(1,101):
    sum += i
print(sum)
i = 1
while i <=5:
    print(f"小红在第{i}个吃苹果")
    if i == 3:
        print('吃饱了,不吃了!')
        break   # 结束break所在的循环
    i += 1
i = 1
while i <=5:
    print(f"小明在第{i}个吃苹果")
    if i == 3:
        print(f'吃到了一条大虫子,第{i}个苹果不吃了')
        i += 1
        continue
    i += 1
for i in range(5):
    if i == 3:
        continue
    print(i)

字符串编码

本质上就是二进制数据与语言文字的一一对应关系

unicode:所有字符都是2个字节,字符与数字之间转换速度更快一些,占用空间大。
UTF-8:精准,对不同的字符用不同的长度表示,节省空间,字符与数字的转换速度较慢,每次都需要计算字符要用多少字节来表示。

  1. 编码:encode(),将其他编码的字符串转换成unicode编码
  2. 解码:decode(),将unicode编码转换成其他编码的字符串
a = 'hello'
print(a,type(a))
a.encode()
a1 = a.encode()
print("编码后",a1)
print(type(a1))
print(a2,type(a2))
a2 = a1.decode()
hello <class 'str'>
编码后 b'hello'
<class 'bytes'>
hello <class 'str'>
st = "这里是广西玉林"
st1 = st.encode("utf-8")
print(st1,type(st1))
st2 = st1.decode("utf-8")
print(st2,type(st2))
b'\xe8\xbf\x99\xe9\x87\x8c\xe6\x98\xaf\xe5\xb9\xbf\xe8\xa5\xbf\xe7\x8e\x89\xe6\x9e\x97' <class 'bytes'>
这里是广西玉林 <class 'str'>

字符串运算符

# 字符串常见操作
# + 字符串拼接
print(10+10)
print("10"+"10")
name1="广西"
name2="玉林"
print(name1 + name2)
print(name1,name2,sep="")
20
1010
广西玉林
广西玉林
# 字符串常见操作
# * 重复输出
print("好好学习,天天向上\n"*5)
print('&\t'*10)
好好学习,天天向上
好好学习,天天向上
好好学习,天天向上
好好学习,天天向上
好好学习,天天向上

&   &   &   &   &   &   &   &   &   &
# 成员运算符
# in 如果包含的话,返回True,不包含返回False
# not in 如果不包含的话,返回True,包含返回True
name = '广西玉林'
print('南宁' in name)
print('玉林' in name)
print('广' not in name)
print('天' not in name)
False
True
False
True
# 下标从0开始,通过下标能够快速找到对应的数据
# 格式:字符串名[下标值]
# 取值的时候不要超出下标范围
# 从右往左,下标从-1开始
name = 'hello'
print(name[0])
print(name[-1])
h
o
# 切片指对操作的对象截取其中一部分的操作
# 语法:[开始位置:结束位置:步长]
# 包前不包后:即从起始位置开始,到结束位置的前一位结束
st = 'abcdefghijk'
print(st[0:4])
print(st[4:7])
print(st[3:])
print(st[:7])
abcd
efg
defghijk
abcdefg
# 步长表示选取间隔,默认是1
# 步长的绝对值大小决定切取的间隔,正负号决定切取方向
# 整数表示从左往右,负数表示从右往左
print(st[-1::-1])
print(st[-1:-5:-1])
kjihgfedcba
kjih

字符串常见操作

# find:检测某个子字符串是否包含在字符串中,如果在就返回这个子字符串开始位置的下标,否则返回-1
# find(子字符串,开始位置下标,结束位置下标)
# 开始和结束位置下标可以省略,表示在整个字符串中查找
name = 'guangxiyulin'
print(name.find('y'))
print(name.find('i',7))
print(name.find('i',7,10))
# 包前不包后
7
10
-1
# index:检测某个子字符串是否包含在字符串中,如果在就返回这个子字符串开始位置的下标,否则报错
# index(子字符串,开始位置下标,结束位置下标)
name = '我命由我不由天'
print(name.index("命"))
# print(name.index('命',2))
print(name.index("命",1,3))
# 和find的区别,find没找到,返回-1,index没找到就报错
1
1
# count:返回某个子字符串在整个字符串中出现的次数,没有就返回0
# count(子字符串,开始位置下标,结束位置下标)
# 开始和结束位置下标可以省略,表示在整个字符串中查找
name = 'guangxiyulin'
print(name.count('i'))
print(name.count('z'))
2
0
# startwith():是否以某个子字符串开头,是的话就返回True,否则返回False
st = 'yulin'
print(st.startswith('y'))
print(st.startswith('yu'))
True
True
# endwith():是否以某个子字符串结尾,是的话就返回True,否则返回False
st = 'yulin'
print(st.endswith('n'))
print(st.endswith('lin'))
True
True
# isupper():检测字符串中所有的字母是否都为大写,是的话就返回True
st = 'yulin'
print(st.isupper())
print(st.islower())
False
True
# replace():替换
# replace(旧内容,新内容,替换次数)
# 替换次数可以省略,默认全部替换
name = '好好学习,天天向上'
print(name.replace('天','时'))
print(name.replace('天','时',1))
好好学习,时时向上
好好学习,时天向上
# split():指定分隔符来切字符串
# 如果字符串中不包含分割内容,就不进行分割,会作为一个整体
st = 'hello,python'
print(st.split(','))
print(st.split('a'))
print(st.split('o'))
['hello', 'python']
['hello,python']
['hell', ',pyth', 'n']
# capitalize():第一个字符大写,其他都小写
st = 'yulin'
print(st.capitalize())
# lower():大写字母转为小写
st = 'YuLin'
print(st.lower())
# upper():小写字母转为大写
st = 'yulin'
print(st.upper())
Yulin
yulin
YULIN

列表

# 列表名 = [元素1,元素2,元素3..]
# 所有元数放在[]内,元素与元素之间用,隔开
# 元素的数据类型可以各不相同
li = [1, 2, 'a', 4]
print(li,type(li))
print(li[2])
# 列表也可以进行切片操作
print(li[0:3])
# 列表是可迭代对象,可以for循环遍历取值
for i in li:
    print(i)
[1, 2, 'a', 4] <class 'list'>
a
[1, 2, 'a']
1
2
a
4

列表相关操作

# 列表的常见操作
# 添加元素
# append() extend() insert()
li = ['one', 'two', 'three']
li.append('four')
li.extend('four')
li.insert(8, 'five')
# 指定位置如果有元素,原有元素就会后移
# 没有指定下标,会报错
print(li)
['one', 'two', 'three', 'four', 'f', 'o', 'u', 'r', 'five']
# 修改元素
# 直接通过下标就可以进行修改
li = [1, 2, 3]
li[2] = 'a'
print(li)
[1, 2, 'a']

# 查找元素
# in 判断指定元素是否在列表中,如果存在就返回True,不存在就返回False
# not in
li = ['a','b','c','d']
print('b' in li)
True

# 用户输入昵称,昵称重复则不能使用
# 定义一个列表,保存已经存在的昵称
name_list = ['hehe','xixi','haha']
while True:
    name = input("请输入您的昵称:")
    # 判断昵称是否已经存在
    if name in name_list:
        print(f"您输入的昵称{name}已经存在了")
    else:
        print(f"昵称{name}已经被您使用")
        name_list.append(name)
        print(name_list)
        break

# index:返回指定数据所在位置的下标,如果查找的数据不存在就会报错
# count:指定数据在当前列表出现的次数
# 跟字符串的用法相同
# del
li = ['a','b','c','d']
# del li  # 删除列表
del li[2]
print(li)
['a', 'b', 'd']

# pop:删除指定下标的数据,python3版本默认删除最后一个元素
li = ['a','b','c','d']
li.pop() # 默认删除最后一个元素
print(li)
li.pop(2)  # 下标不能超出范围
print(li)
['a', 'b', 'c']
['a', 'b']

# remove 根据元素的值进行删除
li = ['a','b','c','d']
li.remove('a')
li.remove('t') # 报错,列表中不存在这个元素
li.remove('b') # 默认删除最开始出现的指定元素
print(li)
# 排序
# sort:将列表按特定顺序重新排列,默认从小到大
li = [1,4,66,3,4,65]
li.sort()
print(li)
# reverse:倒序,将列表倒置
li.reverse()
print(li)
[1, 3, 4, 4, 65, 66]
[66, 65, 4, 4, 3, 1]
# 列表推导式
# 格式一:[表达式 for 变量 in 列表]
# in 后面不仅可以放列表,还可以放range()、可迭代对象
li = [1,2,3,4,5,6]
[print(i*5) for i in li]
li =[]
# for i in range(1,6):
#     li.append(i)
# print(li)
[li.append(i) for i in range(1,6)]
print(li)
# 格式二:[表达式 for 变量 in 列表 if 条件]
li = []
# for i in range(1,11):
#     if i % 2 == 1:
#         li.append(i)
# print(li)
[li.append(i) for i in range(1,11) if i % 2 == 1]
print(li)
# 列表嵌套
# 一个列表里面又有一个列表
li = [1, 2, 3,[4,5,6]]
print(li[3])
print(li[3][2])
[4, 5, 6]
6

元组

# 元组
# 基本格式:元组名 = (元素1,元素2,元素3..)
# 所有元素包含在小括号内,元素与元素之间用,隔开,不同元素也可以是不同的数据类型
tua = (1,2,3,'a',[1,2,3])
print(type(tua))
tub = (1,)  # 只有一个元素的时候,末尾必须加上,否则返回唯一的值的数据类型
print(tub)
# 元组与列表的区别
# 1.元组只有一个元素末尾必须加,列表不需要加
# 2.元组只支持查询操作,不支持增删改操作
li = [1,2,3]
li[1] = 'a'
print(li)
tua = (1,2,3,1)
print(tua[-1])  # 元组也有下标,从左往右,从0开始
# tua[2] = 'b'  # 报错,元组不支持修改操作
# count(),index(),len()跟列表的用法相同
print(tua.index(2))
print(tua.count(1))
print(len(tua))
print(tua[1:])
<class 'tuple'>
(1,)
[1, 'a', 3]
1
1
2
4
(2, 3, 1)
# 应用场景
# 函数的参数和返回值
# 格式化输出后面的()本质上就是一个元组
name = 'yulin'
area = 775
print("%s的区号是:%d" % (name, area))
info = (name, area)
print(type(info))
print("%s的区号是:%d" % info)
# 数据不可以被修改,保护数据的安全

字典

# 字典
# 基本格式:字典名 = {键1:值1,键2:值2...}
# 键值对形式保存,键和值之间用:隔开,每个键值对之间用,隔开
dic = {'name':'yulin','area':775}
print(type(dic))
# 字典中的键具备唯一性,但是值可以重复
dic2 = {'name':'yulin','name':'guilin'} # 不会报错,键名重复前面的值会被后面的值覆盖
print(dic2)
# 字典常见操作
# 查看元素
# 变量名[键名]
dic = {'name':'yulin','area':775}
print(dic['name'])  # 字典中没有下标,查找元素需要根据键名,键名相当于下标
print(dic['area'])  # 键名不存在会报错
# 变量名.get(键名)
print(dic.get('name'))
print(dic.get('tel'))  # 键名不存在,返回None
print(dic.get('tel','不存在'))
yulin
775
yulin
None
不存在

# 修改元素
# 变量名[键名] = 值
dic = {'name':'yulin','area':775}
dic['area'] = 778  # 列表通过下标修改,字典通过键名修改
print(dic)

# 添加元素
# 变量名[键名] = 值
# 键名存在就修改,不存在就新增
dic['tel'] = 7757
print(dic)
{'name': 'yulin', 'area': 778}
{'name': 'yulin', 'area': 778, 'tel': 7757}

# 删除元素
# del
# 删除整个字典,del 字典名
dic = {'name':'yulin','area':775}
# del dic
# print(dic)  # 报错,已经被删除了,找不到字典
# 删除指定键值对,键名不存在就会报错 del 字典名[键名]
del dic['area']
print(dic)
{'name': 'yulin'}

# clear():清空整个字典里面的东西,但保留了这个字典
dic = {'name':'yulin','area':775}
dic.clear()
print(dic)
{}

# pop():删除指定键值对,键不存在就会报错
dic = {'name':'yulin','area':775}
dic.pop('area')
print(dic)
{'name': 'yulin'}
# pop():删除指定键值对,键不存在就会报错
dic = {'name':'yulin','area':775}
dic.popitem()  # 默认删除最后一个键值对
print(dic)
{'name': 'yulin'}

# len()求长度
dic = {'name':'yulin','area':775}
print(len(dic))  # 字典中有2个键值对
li = [1,2,3,4,5]
print(len(li))
st = 'hello'
print(len(st))
2
5
5

# keys():返回字典里面包含的所有键名
dic = {'name':'yulin','area':775}
print(dic.keys())
for i in dic.keys():
    print(i)
dict_keys(['name', 'area'])
name
area

# values():返回字典里面包含的所有值
dic = {'name':'yulin','area':775}
print(dic.values())
for i in dic.values():
    print(i)
dict_values(['yulin', 775])
yulin
775

# items():返回字典里面包含的所有键值对,键值对以元组形式
dic = {'name':'yulin','area':775}
print(dic.items())
for i in dic.items():
    print(i,type(i))  # i是元组类型
dict_items([('name', 'yulin'), ('area', 775)])
('name', 'yulin') <class 'tuple'>
('area', 775) <class 'tuple'>

# 字典的应用场景
# 使用键值对,存储描述一个物体的相关信息

集合

# 集合
# 基本格式:集合名 = {元素1,元素2,元素3...}
s1 = {1,2,3}
print(s1,type(s1))
s1 = {}  # 定义空字典
print(s1,type(s1))
s1 = set()  # 定义空集合
print(s1,type(s1))
{1, 2, 3} <class 'set'>
{} <class 'dict'>
set() <class 'set'>

# 集合具有无序性
s1 = {'a','b','c','d','e','f'}
print(s1)  # 每次运行结果都不一样
s2 = {1,2,3,4,5,6}
print(s2)  # 数字运行结果一样

# 集合无序的实现方式涉及hash表
print(hash('a'))
print(hash('b'))
print(hash('c'))
# 每次运行结果都不同,hash值不同,那么在hash表中的位置也不同,这就实现了集合的无序性
print(hash(1))
print(hash(2))
print(hash(3))
# int整型的hash值就是它本身,在hash表中的位置不会发生改变,所以顺序也不会改变

# 无序性:不能修改集合中的值
# 集合具有唯一性,可以自动去重
s1 = {1,2,4,6,3,2,4}
print(s1)
{1, 2, 3, 4, 6}
# 集合的常见操作
# 添加元素
# add 添加的是一个整体
s1 = {1,2,3,4}
print("原集合:",s1)
# 集合的唯一性,决定了如果需要添加的元素在原集合中已经存在,就不进行任何操作
s1.add(5)
s1.add(1)
# s1.add(5,6)  # 一次只能添加一个元素
s1.add((6,7))
print("新集合:",s1)
原集合: {1, 2, 3, 4}
新集合: {1, 2, 3, 4, 5, (6, 7)}

# update 把传入的元素拆分,一个个放进集合中
s1 = {1,2,3,4}
print("原集合:",s1)
s1.update({5,6,7})  # 元素必须是能够被for循环取值的可迭代对象
print("添加后:",s1)
原集合: {1, 2, 3, 4}
添加后: {1, 2, 3, 4, 5, 6, 7}

# 删除元素
# remove:选择删除的元素,如果集合中有就删除,没有就会报错
s1 = {1,2,3,4}
s1.remove(3)
# s1.remove(5)  # 报错,集合中没有5这个元素
print("删除后",s1)
删除后 {1, 2, 4}

# pop对集合进行无序排列,然后将左边的第一个元素删除
s1 = {1,2,3,4}
print("原集合:",s1)
s1.pop()  # 默认删除根据hash表排序后的第一个元素
print("删除后:",s1)
原集合: {1, 2, 3, 4}
删除后: {2, 3, 4}

# discard:选择要删除的元素,有就会删除,没有则不会发送任何改变,即不会进行任何操作
s1 = {1,2,3,4}
print("原集合:",s1)
s1.discard(3)
print("删除后:",s1)
原集合: {1, 2, 3, 4}
删除后: {1, 2, 4}

交集和并集

# 交集 &
a = {1,2,3,4}
b = {3,4,5,6}
print(a & b)
b = {5,6,7,8}  # 没有共有的部分返回空集合set()
print(a & b)
{3, 4}
set()

# 并集 |
# 含义:所有的都放在一起,重复的不算(集合的唯一性)
a = {1,2,3,4}
b = {3,4,5,6}
print(a | b)
{1, 2, 3, 4, 5, 6}

类型转换

# int():转换为一个整数,只能转换为由纯数字组成的字符串
a = 1.2
print(type(a))
b = int(a)
print(b,type(b))
# 浮点型强转整型会去掉小数点及后面的数值,只保留整数部分
a = int('123')
print(a,type(a))
# print(int('yulin'))  # 报错
# 如果字符串中有数字和正负号以外的字符就会报错
print(int('-10'))
# +/-写在前面表示正负号,不可以写在后面
<class 'float'>
1 <class 'int'>
123 <class 'int'>
-10

# 用户从控制台输入,判断年龄
age = int(input("请输入您的年龄:"))  # input默认输入的是字符串类型
if age >= 18:
    print("成年了")
# float():转换为一个小数
print(float(11))  # 整型转换为浮点型,会自动添加一位小数
print(float(-11))
print(float("+11.345"))  # 如果字符串中有正负号、数字和小数点以外的字符,则不支持转换
11.0
-11.0
11.345

# str() 转换为字符串类型,任何类型都可以转换成字符串类型
n = 100
print(type(n))
n2 = str(n)
print(n2,type(n2))
st = str(-1.80)
print(st,type(st))  # float转换成str会去除末位为0的小数部分
li = [1,2,3]
st = str(li)
print(st,type(st)) 
<class 'int'>
100 <class 'str'>
-1.8 <class 'str'>
[1, 2, 3] <class 'str'>
# eval()
print(10+10)
print('10'+'10')
print('10+10')
print(eval('10+10'))  # 执行运算,并返回运算的值
20
1010
10+10
20

# eval()可以实现list、dict、tuple和str之间的转换
# str -> list
st1 = "[[1,2],[3,4],[5,6]]"
print(type(st1))
li = eval(st1)
print(li,type(li))
<class 'str'>
[[1, 2], [3, 4], [5, 6]] <class 'list'>

# eval()可以实现list、dict、tuple和str之间的转换
# str -> dict
str2 = "{'city':'yulin','area':775}"
dic = eval(str2)
print(dic,type(dic))
{'city': 'yulin', 'area': 775} <class 'dict'>

# eval()非常强大,但是不够安全,容易被恶意修改数据,不建议使用
# list() 将可迭代对象转换成列表
# 支持转换为list的类型:str、tuple、dict、set
# str -> list
print(list('abcdefg'))
# print(list(12345))  # 报错

# tuple -> list
print(list((1,2,3,4)))

# dict -> list
print(list({'city':'yulin','area':775}))
# 字典转换成列表,会取键名作为列表的值

# set -> list
print(list({'a','b','c','b'}))
# 集合转换成列表,会先去重,再转换
['a', 'b', 'c', 'd', 'e', 'f', 'g']
[1, 2, 3, 4]
['city', 'area']
['b', 'c', 'a']

深浅拷贝

# 赋值
li = [1, 2, 3, 4]
li2 = li  # 将li直接赋值给li2
print('li',li)
print('li2',li2)
# 给li列表新增元素
li.append(5)
print('新增后的li',li)
print('新增后的li2',li2)
# 赋值:等于完全共享资源,一个值的改变会完全被另一个值共享
li [1, 2, 3, 4]
li2 [1, 2, 3, 4]
新增后的li [1, 2, 3, 4, 5]
新增后的li2 [1, 2, 3, 4, 5]
# 浅拷贝(数据半共享)
# 会创建新的对象,拷贝第一层的数据,嵌套层会指向原来的内存地址
import copy
li = [1,2,3,[4,5,6]]
li2 = copy.copy(li)  # 浅拷贝
print('li',li)
print('li2',li2)
# 查看内存地址 id()
print('li内存地址:',id(li))
print('li2内存地址:',id(li2))
# 内存地址不一样,说明不是同一个对象
li.append(8)
print('li',li)
print('li2',li2)
# 往嵌套列表添加元素
print(li[3])
li[3].append(7)
print('li',li)
print('li2',li2)
print('li[3]内存地址:',id(li[3]))
print('li2[3]内存地址:',id(li2[3]))
# 外层的内存地址不同,但是内层的内存地址相同
# 优点:拷贝速度块,占用空间少,拷贝效率高
li [1, 2, 3, [4, 5, 6]]
li2 [1, 2, 3, [4, 5, 6]]
li内存地址: 2515091931328
li2内存地址: 2515091931520
li [1, 2, 3, [4, 5, 6], 8]
li2 [1, 2, 3, [4, 5, 6]]
[4, 5, 6]
li [1, 2, 3, [4, 5, 6, 7], 8]
li2 [1, 2, 3, [4, 5, 6, 7]]
li[3]内存地址: 2515091931776
li2[3]内存地址: 2515091931776
# 深拷贝(数据完全不共享)
# 外层的对象和内部的元素都拷贝了一遍
import copy  # 导入copy模块
li = [1,2,3,[4,5,6]]
li2 = copy.deepcopy(li)
print('li',li,id(li))
print('li2',li2,id(li2))
li.append(8)
print(li)
print(li2)
# 在嵌套列表添加元素
li[3].append(7)
print(li)
print(li2)
print(id(li[3]))
print(id(li2[3]))
# 深拷贝数据变化只影响自己本身,跟原来的对象没有关联
li [1, 2, 3, [4, 5, 6]] 2548248051456
li2 [1, 2, 3, [4, 5, 6]] 2548248051648
[1, 2, 3, [4, 5, 6], 8]
[1, 2, 3, [4, 5, 6]]
[1, 2, 3, [4, 5, 6, 7], 8]
[1, 2, 3, [4, 5, 6]]
2548248101120
2548247923008

可变对象

# 可变对象
# 变量对应的值可以修改,但是内存地址不会发生改变
# 常见的可变类型:list、dict、set
li = [1, 2, 3, 4]
print("li的原内存地址:",id(li))
li.append(5)
print("li的现内存地址:",id(li))
dic = {'city':'yulin','area':775}
print(dic,id(dic))
dic['city'] = 'guilin'
print(dic,id(dic))
set = {1,2,3,4,5}
print(set,id(set))
set.remove(3)
print(set,id(set))
li的原内存地址: 2487786469120
li的现内存地址: 2487786469120
{'city': 'yulin', 'area': 775} 2487786423808
{'city': 'guilin', 'area': 775} 2487786423808
{1, 2, 3, 4, 5} 2487787076096
{1, 2, 4, 5} 2487787076096

不可变对象

# 不可变对象
# 变量对应的值不能被修改,如果修改就会生成一个新的值从而分配新的内存空间
n = 10
print('原地址:',n,id(n))
n = 15
print('修改后:',n,id(n))
# 内存地址不一样,修改n的值就会生成新的值,重新赋值给变量n
st = 'hello'
print(st,id(st))
st = 'world'
print(st,id(st))
tua = (1,2,3)
print(tua,id(tua))
tua = ('a','b','c')
print(tua,id(tua))
# 注意:前面所说的深浅拷贝只针对可变对象,不可变对象没有拷贝的说法
原地址: 10 2110867374608
修改后: 15 2110867374768
hello 2110872066992
world 2110868703024
(1, 2, 3) 2110872308992
('a', 'b', 'c') 2110872758528

函数

# 函数
# 将独立的代码块组织成一个整体,使其具有特殊功能的代码集,在需要的时候再去调用即可
# 提高代码的重用性,使整体代码看上去更加简练
# 定义函数
# def 函数名():
#   函数体
# 调用函数:函数名()
def login():
    print("这是登录函数")
login()
# 调用几次,函数里面的代码就会运行几次,每次调用的时候,函数都会从开头执行
# 编写一个打招呼的函数并调用它
def say_hello():
    print("hello")
    print("yulin")
    print("0775")
say_hello()
# 调用函数前,必须保证函数已经存在
def buy():
    return "一桶水果茶"
buy()
print(buy())
一桶水果茶
# 返回值的三种情况
# 1. 一个返回值也没有,返回的结果是None
# 2. 一个返回值,就把值返回给调用者
# 3. 多个返回值,已元组的形式返回给调用者
def buy():
    return "一桶水果茶"
print(buy())
('一桶水果茶', 30)
def buy():
    return
print(buy())
None

# return和print区别
# 1. return表示此函数结束,print会一直执行
# 2. return是返回计算值,print是打印结果
def add():
    a =1
    b =2
    return a+b
print(add())
3
def add():
    a =1
    b =2
    print(a+b)
add()
3
# 参数
# def 函数名(形参a,、形参b):
# 行参:定义函数时,小括号里面的变量
# 调用格式:
# 函数名(实参1,实参2)
# 实参:调用函数时,小括号里面的具体的值
def add(a,b):
    return a+b
print(add(1,10))
# 传参 a = 1 , b = 10
11

函数参数

# 函数参数
# 必备参数(位置参数)
# 传递和定义参数的顺序及个数必须一致
# 格式:def func(a,b)
def funa(name,name2,name3):
    print(name)
    print(name2)
    print(name3)
funa('guilin','nanning','yulin')
# 写了几个就必须要传几个,不可以多传,也不可以少传
guilin
nanning
yulin

# 默认参数
# 为参数提供默认值,调用函数时可不传该默认参数的值
# 所有的位置参数必须出现在默认参数前,包括函数定义和调用
# 格式:def func(a=12):
def funb(b,a=8):
    print(b,a)
funb(1)
funb(1,200)
# 设置默认值,没有传值会根据默认值来执行代码,传了值根据传入的值来执行代码
1 8
1 200

# 可变参数
# 传入的值的数量时可以改变的,可以传入多个,也可以不传
# 格式:def func(*args)
def func(*args):  # 可以把args改成其他参数名,但是args符合代码的规范性
    print(args)  # 以元组形式接收
    print(type(args))
func(1)
(1,)
<class 'tuple'>

# 关键字参数
# 格式:def func(**kwargs)
def fund(**kwargs):
    print(kwargs)  # 返回字典
    print(type(kwargs))  # 以字典形式接收
fund()  # 空字典
fund(city='yulin',area=775)  # 传值的时候,需要采用键=值的形式
# 作用:可以扩展函数的功能
{}
<class 'dict'>
{'city': 'yulin', 'area': 775}
<class 'dict'>

函数嵌套

# 函数嵌套
# 嵌套调用
# 在一个函数里面调用另外一个函数
def study():
    print("晚上在学习")
def course():
    study()  # 在course()函数内调用study()
    print("Python基础")
# 调用
course()
晚上在学习
Python基础
# 嵌套定义
# 含义:在一个函数中定义另外一个函数
def study():  # 外函数
    print("晚上在学习")
    def course():  # 内函数
        print("Python基础")  # 不要在内层函数调用外层函数,会陷入死循环,直到超过递归的最大深度
    course()  # 注意:缩进,定义和调用是同级的,调用如果在定义里面则永远调用不到
study()
晚上在学习
Python基础

函数作用域

# 作用域
# 指的是变量生效的范围,分为两种,分部是全局变量和局部变量
# 全局变量
# 函数外部定义的变量,在整个文件中都是有效的
a = 100  # 全局变量
def test1():
    print("这是test1中a的值:",a)
def test2():
    a = 120  # 局部变量
    print("这是test2中a的值:",a)
print("调用函数前a的值:",a)
test1()
test2()
print("调用函数后a的值:",a)
# a的值没有被覆盖是因为函数内部如果要使用变量,会先从函数内部找,有的话就直接使用,没有会到函数外面去找
调用函数前a的值: 100
这是test1中a的值: 100
这是test2中a的值: 120
调用函数后a的值: 100

# 局部变量
# 函数内部定义的变量,从定义位置开始到函数定义结束位置有效
def funa():
    num = 10  # 局部变量
    print("funa中的num:",num)
funa()
# print("num:",num)  # 报错,局部变量只能在被定义的函数中使用,函数外部不能使用
# 作用:在函数体内部,临时保存数据,即当函数调用完成之后,就销毁局部变量
def funb():
    num = 18
    print("funb中的num:",num)
funb()
funa中的num: 10
funb中的num: 18

# 在函数内部修改全局变量的值,可以使用global关键字
# global,将变量声明为全局变量
# 语法格式:global 变量名
a = 100  # 全局变量
def test1():
    print("这是test1中a的值:",a)
def test2():
    global a  # 声明全局变量
    a = 120
    print("这是test2中a的值:",a)
print("调用函数前a的值:",a)
test1()
test2()
print("调用函数后a的值:",a)
调用函数前a的值: 100
这是test1中a的值: 100
这是test2中a的值: 120
调用函数后a的值: 120

def study():
    global name  # 将局部变量name声明为全局变量
    name = "python基础"  # 局部变量
    print(f"我们在学习{name}")
study()
print(name)
def work():
    print(name)
work()
# 总结:global关键字可以对全局变量进行修改,也可以在局部作用域中声明一个全局变量
我们在学习python基础
python基础
python基础

# nonlocal
# 用来声明外层的局部变量,只能在嵌套函数中使用,在外部函数先进行声明,内部函数进行nonlocal声明
a = 10  # 全局变量
def outer():
    a = 5  # 局部变量
    def inner():
        nonlocal a
        a = 20
        print("inner函数中a的值:",a)
    inner()
    print("outer函数中a的值:",a)
outer()
# 总结:nonlocal只能对上一层进行修改
inner函数中a的值: 20
outer函数中a的值: 20

匿名函数

# 匿名函数
# 基本语法
# 函数名 = lambda 形参:返回值(表达式)
# 调用:结果 = 函数名(实参)
# 普通函数
def add(a,b):
    return a+b
print(add(1,10))
# 匿名函数
add = lambda a,b: a+b  # a,b就是匿名函数的形参,a+b是返回值的表达式
# lambda不需要写return返回值,表达式本身就是返回值
print(add(1,10))
11
11

# lambda的参数形式
# 函数名 = lambda 形参:返回值(表达式)
# 无参数
funa = lambda :"一桶水果茶"
print(funa())
# 一个参数
funb = lambda name:name
print(funb('yulin'))
# 默认参数
func = lambda city,area=775:(city,area)
print(func("yulin"))
print(func("guilin",773))
fune = lambda a,b,c=12:a+b+c
print(fune(1,2))
# 默认参数必须写在非默认参数后面
# 关键字参数
fund = lambda **kwargs:kwargs
print(fund(name='yulin',area=775))
一桶水果茶
yulin
('yulin', 775)
('guilin', 773)
15
{'name': 'yulin', 'area': 775}
# lanmda结合if判断
a = 8
b = 5
# 为真结果 if 条件 else 为假结果
print("a比b小") if a < b else print("a大于等于b")
comp = lambda a,b:"a比b小" if a<b else "a大于等于b"
print(comp(5,8))
# lambda只能实现简单的逻辑,如果逻辑复杂且代码量较大,不建议使用lambda,降低代码的可读性,为后期的代码维护增加困难
a大于等于b
a比b小

内置函数

# 内置函数
# 查看所有的内置函数
import builtins
print(dir(builtins))
# 大写字母开头一般是内置常量名,小写字母开头一般是内置函数名
# abs():返回绝对值
print(abs(-10))
# sum():求和
# print(sum(123))  # 报错,整形不是可迭代对象,sum函数内要放可迭代对象
# print(sum('abcd'))  # 报错,字符串不可以进行相加,字典也不可以
print(sum([1,2,3]))
print(sum({1.5,3,4}))  # 运算时,只要有一个为浮点数,那么结果必定是浮点数
10
6
8.5
# min():求最小值
# max():求最大值
print(min(4,1,8))
print(max(4,1,8))
print(min(-8,5,key=abs))  # 传入了求绝对值函数,则参数就会进行先求绝对值再比较

# zip():将可迭代对象作为参数,将对象中对应的元素打包成一个个元组
li = [1,2,3]
li2 = ['a','b','c']
print(zip(li,li2))
# 第一种方式:通过for循环
for i in zip(li,li2):
    print(i,type(i))
# 如果元素个数不一致,就按照长度最短的返回
# 第二种方式:转换成列表打印
print(list(zip(li,li2)))  # 转换成列表打印
# 必须是可迭代对象

1
8
5
<zip object at 0x0000017CC49D0E40>
(1, 'a') <class 'tuple'>
(2, 'b') <class 'tuple'>
(3, 'c') <class 'tuple'>
[(1, 'a'), (2, 'b'), (3, 'c')]
# map():可以对可迭代对象中的每一个元素进行映射,分别去执行
# map(func,iter1):func--自己定义的函数,iter1--要放进去的可迭代对象
# 简单来说就是对象中的每一个元素都会去执行这个函数
li = [1, 2, 3]
def funa(x):
    return x * 5
mp = map(funa, li)  # 只要写函数名,不需要加上小括号
print(mp)
# 第一种方式:for循环取出
for i in mp:
    print(i)
# 第二种:转换成列表打印
print(list(mp))
<map object at 0x000001DD7F58BF10>
5
10
15
[5,10,15]

li = [1, 2, 3]
funa = lambda x:x+5
mp = map(funa, li)  # 只要写函数名,不需要加上小括号
print(list(mp))
[6, 7, 8]
# reduce():先把对象中的两个元素取出,计算出一个值然后保存着,接下来把这个计算值跟第三个元素进行计算
# 需要先导包
from functools import reduce
# reduce(function,sequence)  # function--函数:必须是有两个参数的函数,sequence--序列:可迭代对象
li2 = [1, 2, 3,4]
def add(x,y):
    return x+y
res = reduce(add, li2)
print(res)
10

拆包

# 拆包
# 对于函数中的多个返回数据,去掉元组、列表或者字典直接获取里面数据的过程
tua = (1,2,3,4)
print(tua)
# 方法一
a,b,c,d = tua
print('a=',a,'b=',b,'c=',c,'d=',d)
# 要求元组内的个数与接收的变量个数相同,对象内有多少个数据就需要定义多少个变量接收
# a,b = tua  # 报错,值错误,要拆包的值过多
# 一般再获取元组值的时候使用
# 方法二
a, *b = tua
print(a,b)
# 一般再函数调用时使用
def funa(a,b,*args):
    print(a,b)
    print(args,type(args))
funa(1,2,3,4,5,6,7)
arg = (1,2,3,4,5,6,7)
funa(*arg)
(1, 2, 3, 4)
a= 1 b= 2 c= 3 d= 4
1 [2, 3, 4]
1 2
(3, 4, 5, 6, 7) <class 'tuple'>
1 2
(3, 4, 5, 6, 7) <class 'tuple'>

异常

# 抛出异常 raise
# 1.创建一个exception('xxx')对象,xxx---异常提示信息
# 2.raise抛出这个对象(异常对象)
# raise Exception("程序抛出了一个异常")
# def funa():
#     raise Exception('程序抛出了一个异常')
#     print("哈哈哈!")  # 执行了raise语法,代码不会继续往下运行
# funa()
# 需求:密码长度不足,就报异常
# 分析:用户输入密码,判断输入的长度是否大于等于6,如果输入不足6位,就报错,即抛出自定义异常,并捕获该异常
def login():
    pwd = input("请输入您的密码:")
    if len(pwd) >= 6:
        return "密码输入成功"
    raise Exception("长度不足六位,密码输入失败")
# print(login())
try:
    print(login())
except Exception as e:
    print(e)
print("keep moving")
# 捕获异常是为了监测到异常时代码还能继续往下运行,即程序不会终止

模块

# 模块
# 一个py文件就是一个模块,即导入一个模块本质上就是执行一个py文件
# 内置模块
# 如:random、time、os、logging,直接导入即可使用
# 第三方模块(第三方库)
# 下载:cmd窗口输入pip install 模块名
# 自定义模块
# 即自己在项目中定义的模块
# 注意:命名要遵循标识符规定以及变量的命名规范,并且不要与内置模块起冲突,否则将导致模块功能无法使用

# 导入模块 注意:可以一个import导入多个模块,但是最好是一个模块单独使用一个import
# import 模块名
# 调用功能:模块名.功能名
import pytest
# 调用pytest模块中的city变量
print(pytest.city)
# 调用pytest模块中的funa()
pytest.funa()

# 方式二
# 语法:from ... import ...
# 从模块中指定的部分
# from 模块名 import 功能1,功能2...
# 调用功能
# 直接输入功能即可,不需要添加模块名
from pytest import funa,city
funa()  # 导入函数只需要函数名,不需要加上()
print(city)
# import后面填写需要导入的功能
funb()  # 报错,没有导入

# 方式三:from ... import *
# 语法:from 模块名 import *
from pytest import *  # 把模块中的所有内容全部导入
funa()
funb()
print(city)
# 注意:不建议过多使用from...import...声明,有时候命名冲突会造成一些错误
# as起别名
# as给模块起别名
# 语法:import 模块名 as 别名
# 给模块起别名
import pytest as pt
# 调用模块中的funa()
pt.funa()
# 打印模块中city变量
print(pt.city)

# as给功能起别名
# 语法:from 模块名 import 功能 as 别名
from pytest import funa as a,city,funb as b
a()
print(city)
b()
# 注意:导入多个功能,使用,将功能与功能隔开,后面的功能也可以取别名:功能名 as 别名

# 内置全局变量__name__
# 语法:if __name__ == "__main__"
# 用来py文件在不同的应用场景执行不同的逻辑
# __name__
# 文件在当前程序执行(即自己执行自己):__name__ == "__main__"
# 文件被当作模块被其他文件导入:__name__ == 模块名
import pytest2
pytest2.test()
# 注意:被当作模块导入时,__name__ == "__main__"下面的代码不会被显示出来

# 包
# 含义:就是项目结构中的文件夹/目录
# 与普通文件夹的区别:包是含有__init__.py的文件夹
# 作用:将有联系的模块放到同一个文件夹下,有效避免模块名称冲突问题,让结构更清晰
# 新建包:右键项目名-->new-->python package

# import导入包时,首先执行__init__.py的代码
import pack_01
# 不建议在init文件中编写过多代码,进来保证init文件的内容简单
# __init__.py
print("这是__init__.py")
# 不建议在init文件中写大量代码
# 主要作用:导入这个包内的其他模块
from pack_01 import register
# register.py
def reg():
    print("这是注册函数")
reg()

# __all__:本质上是一个列表,列表里面的元素就代表要导入的模块
# 作用:可以控制要引入的东西。
from pack_01 import *
register.reg()
# 包的本质依然是模块,包又可以包含包
# __init__.py
__all__ = ['register']  # 相当于导入[]里面定义的模块

递归函数

# 递归函数
# 如果一个函数在内部不调用其他的函数,而是调用它本身的话,这个函数就是递归函数。
# 条件
# 1.必须有一个明确的结束条件  --递归出口
# 2.每进行更深一层的递归,问题规模相比上次递归都要有所减少
# 3.相邻两次重复之间有紧密的联系
# 普通函数实现计算1--100累加和
def add():
    s = 0
    for i in range(1,101):
        s += i
    print(s)
add()
# 递归函数
def add2(n):  # 要累加到第n项
    # 如果是1,就返回1  --明确的结束条件
    if n ==1:
        return 1
    # 如果不是1,重复执行累加并返回结果
    return n + add2(n-1)
print(add2(100))

# 斐波那契数列
# 1,1,2,3,5,8,13...
# 规律:从第三项开始,每一项都等于前两项之和,即n=(n-2)+(n-1)
def funa(n):  # n代表第n项
    if n <= 1:
        return n
    return funa(n-2)+funa(n-1)
print(funa(7))
# 优点
# 简洁、逻辑清晰、解题更具有思路
# 缺点
# 使用递归函数的时候,需要反复调用函数,耗内存,运行效率低

闭包

# 闭包
# 条件
# 1.函数嵌套(函数里面再定义函数)
# 2.内存函数使用外层函数的局部变量
# 3.外层函数的返回值是内层函数的函数名
def outer():  # 外层函数
    n = 10  # 外层函数的局部变量
    def inner():  # 内层函数
        print(n)  # 内层函数使用外层函数的局部变量
    # 外层函数的返回值是内存函数的函数名
    return inner
print(outer())  # 返回的是内部函数的内存地址
# 第一种调用写法
outer()()
# 第二种调用写法
ot = outer()  # 调用外函数
ot()  # 调用内函数
<function outer.<locals>.inner at 0x000002A68DD6F1C0>
10
10

def outer(m):  # 外函数,m是形参,也是外函数的局部变量
    n = 10
    def inner(o):  # 内函数
        print("计算结果:",m+n+o)
    return inner  # 返回函数名,而不是inner(),因为inner函数里面参数比较多时或者说受到限制时,写法不太规范
ot = outer(20)  # 调用外函数
ot(20)  # 调用内函数
计算结果: 50
# 函数引用
def funa():
    print(123)
print(funa)  # 函数名里面保存了函数所在位置的引用
# id():判断两个变量是否是同一个值的引用
a = 1  # a只不过是一个变量名,存的是1这个数值所在的地址,就是a里面存聊数值1的引用
print(a)
print(id(a))
a = 2  # 修改a,生成了新的值,重新赋值给变量a
print(id(a))
print(id(2))
# 内存地址发生变化,因为值也发生了变化
def test1():  # test1也只不过是一个函数名,里面存了这个函数所在位置的引用
    print("这是test函数")
test1()
print(test1)
te = test1
te()  # 通过引用调用函数
<function funa at 0x000001C5F3F8F1C0>
1
1949711728880
1949711728912
1949711728912
这是test函数
<function test1 at 0x000001C5F414FF40>
这是test函数
# 每次开启内函数都在使用同一份闭包变量
def outer(m):
    print("outer()函数中的值:",m)
    def inner(n):
        print("inner()函数中的值:",n)
        return m + n  # 在inner函数中返回m+n的值
    return inner
ot = outer(10)  # 调用外函数,给outer()传值
# print(ot)
# 第一次调用内函数,给inner()函数传值
print(ot(20))  # 调用内函数,给inner()传值
# 第二次调用内函数
print(ot(40))
# 第三次调用内函数
print(ot(80))
# 总结:使用闭包的过程中,一旦外函数被调用一次,返回了内函数的引用
# 虽然每次调用内函数,会开启一个函数,执行后消亡
# 但是闭包变量实际上只有一份,每次开启内函数都在使用同一份闭包变量
outer()函数中的值: 10
inner()函数中的值: 20
30
inner()函数中的值: 40
50
inner()函数中的值: 80
90

装饰器

# 装饰器
# 含义:装饰器本质上就是一个闭包函数
# 好处:在不修改原有代码的情况下增加额外的功能
# 条件:
# 1.不修改原程序或函数的代码
# 2.不改变函数或程序的调用方法
def test(fn):  # fn是普通形参
    print("登录")
    print("注册")
    fn()
def test02():
    print("发消息")
test(test02)
登录
注册
发消息

# 标准版装饰器
# 被装饰的函数
def send():
    print("发送信息")
# 闭包的三个条件
# 1.函数嵌套
# 2.内函数要使用外函数的局部变量
# 3.外函数的返回值是内函数的函数名
def outer(fn):  # 外函数,fn是形参,但是往里面传入的是被装饰的函数名:send
    # 既包含原有功能,又包含新功能
    def inner():  # 内函数
        print("登录")
        执行被装饰的函数
        fn()  # send()
    return inner
outer(send)()
# 装饰器的原理就是将原有的函数名重新定义为以原函数为参数的闭包
登录
发送信息

语法糖

# 语法糖
# 格式:@装饰器名称
def outer(fn):
    def inner():
        print("登录...")
        # 执行被装饰的函数
        fn()
    return inner
# 注意:装饰器名称后面不要加上(),前者是引用,后者是调用函数,返回该函数要返回的值
@outer
def send():
    print("发送信息:哈哈哈")
send()
@outer
def send2():
    print("发送消息:呵呵呵")
send2()
登录...
发送信息:哈哈哈
登录...
发送消息:呵呵呵

# 被装饰的函数有参数
def outer(fn):
    def inner(name):  # 内函数,name是内函数的形参
        print(f"{name}是inner函数中的参数")
        fn(name)
    return inner
@outer
def func(name):
    print("这是被装饰的函数")
func('yulin')
yulin是inner函数中的参数
这是被装饰的函数

# 被装饰的函数有可变参数*args,**kwargs
# 被装饰的函数
def func(*args, **kwargs):
    print(args)
    print(kwargs)
# func(city='guilin',area=775)
# 装饰器函数
def outer(fn):
    def inner(*args, **kwargs):
        print("登录...")
        fn(*args, **kwargs)
    return inner
# 函数必须调用才会执行
outer(func)('guagnx',city='guilin',area=775)  # guangxi以元组得形式传递给args,以键等于值得形式传递给kwargs
登录...
('guagnx',)
{'city': 'guilin', 'area': 775}

# 多个装饰器
# 第一个装饰器
def deco1(fn):
    def inner():
        return "哈哈哈"+fn()+"呵呵呵"
    return inner
# 第二个装饰器
def deco2(fn):
    def inner():
        return "奈斯"+fn()+"非常优秀"
    return inner
# 被装饰得函数
@deco1
@deco2
def test1():
    return "晚上在学习python基础"
print(test1())
# 多个装饰器得装饰过程,离函数最近的装饰器先装饰,然后外面的装饰器再进行装饰,由内到外的装饰过程
哈哈哈奈斯晚上在学习python基础非常优秀呵呵呵
最后修改日期: 2025年5月23日

留言

撰写回覆或留言

发布留言必须填写的电子邮件地址不会公开。