Python 语言的基础入门

本贴最后更新于 515 天前,其中的信息可能已经时异事殊

通过编写代码指挥计算机工作,但是编程即便是写了代码,计算机也是不认识的,需要翻译程序(解释器)翻译成 0 和 1(二进制)才可以和计算机进行顺畅沟通。

为什么不把中文翻译成二进制呢?中文或者自然语言表达意思太多,不够明确。编程语言语法“死板”。

Python 语言的基础入门

你好 python

**python 环境安装:**安装时勾选最下面哪个勾

第一个 python 程序:

print("Hello World!!!")

这里的引号和括号一定要用英文的

常见问题:

  1. 命令提示符输入 python 出现“不是内部或者外部命令,也不是可运行的程序或批处理文件”。这是因为安装的时候没有勾选“add python 3.10 to PATH 的选项”解决方法卸载重装。
  2. 得先输入 python,才可以写代码
  3. 出现“SyntaxError: invalid character'“(U+201C)”这是因为出现了中文符号

python 解释器

想法>编写 python 代码>python 解释器>翻译成 01101 二进制>提交给计算机执行

python.exe 文件就是解释器,命令提示符输入 python 就是调用解释器

问题来了:我们能否写好多行代码一次运行呢?在命令提示符里面写一行回车后直接就运行了,但是可以运行 python 文件,而不是在内部写代码。解决方法:新建一个 test.py 的文件,并通过记事本程序打开,编写代码。然后在命令提示符里面使用 python 命令(python 后面写上文件路径 python d: \test.py),运行它。

命令提示符里退出内部:exit(),回车。或者管理重开

pyCharm 开发工具的安装和基础使用

程序开发方式

  1. 解释器内部,执行单行代码
  2. 使用解释器执行 python 代码文件
  3. 使用第三方 IDE(集成开发工具),如 pyCharm 软件,开发 python 程序

新建项目

8d379a12e4ae074c7dbb7efbf17af18

运行项目:右键运行

设置调整字体快捷键:设置搜索 increase,右键增加字体添加鼠标快捷键,按住 ctrl 向上滚轮。。搜索 decrease,同上。

插件搜索 chines,第二个就是中文语言包。搜索 translation,翻译软件

常用快捷键

ctrl+alt + s:打开软件设置
ctrl+d:复制当前行代码
shift +alt+ 上下:将当前行代码上移或下移
crtl + shift +f10:运行当前代码文件
shift +f6:重命名文件
ctrl+a:全选
ctrl+ c\x:复制、粘贴、剪切
ctrl+f:搜索
ctrl+/:注释

基础语法

字面量

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

常用的 6 种值(数据)的类型

  1. 数字:分为四个

    1. 整数(int):10、-10

      666 13.14 print(666)
    2. 浮点数(float):1.314、3.25

    3. 复数(complex):4+3j,以 j 结尾表示复数

    4. 布尔(bool):表示真和假,True 是 1,False 是 0

  2. 字符串:又叫文本,描述文本的一种数据类型
    字符串需要用引号包围起来:(或者说凡是被双引号包围起来的都是字符串)

    "及你太美5415112@#¥kl" print("及你太美")
  3. 列表:有序的可变序列

  4. 元组:有序的不可变序列

  5. 集合:无序不重复集合

  6. 字典:无序 Key-Value 集合

注释

作用:在程序代码中进行解释说明的文字。

单行注释(#和注释内容建议以一个空格隔开),遵守规范更高级

# 我是单行注释

多行注释:一般用来对整个 python 代码文件,类,方法,来解释

""" 我是多行注释 及你太美 """

变量

就是在程序运行的时候,记录数据用的。比如血量的多少

money = 50 print("钱包还有:", money)

print 可以输出多份数据,这里是字符串和变量,只需要用逗号隔开就可以了

这里的”=“,不是指相等是,是把右边的值赋予左边的变量,这个量是变化的

money = 50 money = money - 10 print("买了冰淇淋花费10元,还剩余:", money, "元")

记录数据为了重复使用,如果将下面代码的 money 写成 40,那么就得改四次,如果有 1000 条就太麻烦

print("现在是下午5点,钱包余额剩余:", money) print("现在是下午6点,钱包余额剩余:", money) print("现在是下午7点,钱包余额剩余:", money) print("现在是下午8点,钱包余额剩余:", money)

数据类型

入门阶段,主要接触三类数据类型

  1. string:字符串类型
  2. int:整数
  3. float:浮点

如何验证数据类型?

type(被查看类型的数据)

type()语句的使用方式

  1. 在 print 语句中,直接输出类型信息

    print(type("及你太美"))
  2. 用变量储存 type()的结构(返回值)

    string_type = type("及你太美")
  3. 也可以查看变量中的数据类型

    name = "及你太美" name_type = type(name) print(name_type)

    这个本质上查看的还是数据的类型,因为变量没有类型,但是它存储的数据有类型

数据类型转换

为什么要转换类型

  1. 从文件中读取的数字,默认是字符串,我们需要转换成数字类型
  2. 后续学习的 input()语句,默认结果是字符串,若需要数字也需要转换
  3. 将数字转换成字符串用以写出到外部系统
  4. 等等

常见的转化语句

  1. int(x):将 x 转换为一个整数
  2. float(x):将 x 转换为一个浮点数
  3. str(x):将 x 转换为字符串

这三个语句,都是带有结果的(返回值),所以我们也可以用 print 直接输出,或者用变量存储结果值。

num_str = str(123) print(type(num_str), num_str)

这里类型变化过去了,但是数据没有遭到破坏

任何类型都能转换成字符串,但是不是所以字符串都能转换成数字

整数可以转换成浮点数,浮点数也能转换成整数但是会丢失小鼠部分

标识符

什么是标识符?,用来做内容的标识

  • 变量的名字
  • 方法的名字
  • 类的名字,等等

标识符命名规则

  1. 内容限定:只允许出现英文、中文、数字、下划线这四类元素
    注意事项:不推荐使用中文,数字不可以在开头
  2. 大小写敏感:可以区分开大小写
  3. 不可使用关键字:关键字指特定用途的

标识符命名规范

  • 变量:见名知意,多个单词的组合用下划线,英文字母全小写

运算符

  • 算术在(数字)运算符:做数学运算

    1. +​:两个数相加
    2. -​:相减
    3. *​:相乘
    4. /​:相除
    5. //​:返回商的整数部分
    6. %​:返回除法的余数
    7. **​:指数 a 的 b 次方
    print("1 + 1 = ", 1 + 1)
  • 赋值运算符

    num = 1 num += 1 # num = num + 1 print(num)
    • 标准赋值运算符

      1. =​:把右边的结果赋给左边的变量
    • 复合赋值运算符:将变量本身进行运算,然后将结果赋值给变量本身

      1. +=​:c += a​等效于 c = c + a
      2. -=
      3. *=
      4. /=
      5. %=
      6. **=
      7. //=

字符串扩展

字符串的三种定义方式

  1. 单引号定义法:name = '及你太美'

  2. 双引号定义法:name = "及你太美"

  3. 三引号定义法:name = """及你太美"""
    三引号定义法,和多行注释的写法一样,同样支持换行操作。使用变量接收它,它就是字符串,不使用变量接收,就可以作为多行注释使用。

    name = """ 鸡 你 太美 ”“”

问题:如果我想要定义的字符串本身,是包含:单引号、双引号自身呢? 如何写?

  1. 单引号定义法,可以内含双引号。因为"'不会导致电脑误判

    name = '"及你太美"'
  2. 双引号定义法,可以内含单引号

    name = "'及你太美'"
  3. 可以使用转移字符(\)来将引号解除效用,变成普通字符串

    name = "\"及你太美"\" name = '\'及你太美'\'

字符串的拼接 ​

如果我们有两个字符串(文本)字面量,可以将其拼接成一个字符串,通过 + 号即可完成,如:

print("基尼" + "太美")

不过一般,单纯的 2 个字符串字面量进行拼接显得很呆,一般,字面量和变量或变量和变量之间会使用拼接,如:

name = "及你太美" print("唱跳" + “name” + “两年半”)

注意事项:无法和非字符串(数字)类型进行拼接

字符串格式化

我们会发现,这个拼接字符串不好用

  1. 变量过多,拼接起来实在是太麻烦了
  2. 字符串无法和数字或其它类型完成拼接。

字符串格式化可以解决这个问题

class_num = 57 avg_salary = 16781 message = "坤坤%s, 打篮球%s" % (class_num, avg_salary)

其中,% 表示:我要占位。。。s 表示:将变量变成字符串放入占位的地方

多个变量占位,变量要用括号括起来,并且按照占位的顺序填入

数字类型还要被转成字符串拼接,有没有体面一点的方式,让数字以其原本的面貌拼接进去呢?

最常用的数据类型占位方式

  1. %s:将内容转换成字符串,放入占位位置
  2. %d:将内容转换成整数,放入占位位置
  3. %f:将内容转换成浮点型,放入占位位置
name = "传智播客" setup_year = 2006 stock_price = 19.36 message = "%s,成立于:%d,我今天的股价是:%f" % (name, setup_price, stock_price)

格式化的精准控制

字符串格式化后,浮点数 19.36 会变成 19.360000 输出

我们可以使用辅助符号"m.n"来控制数据的宽度和精度

  • m,控制宽度,要求是数字(很少使用),设置的宽度小于数字自身,不生效
  • n,控制小数点精度,要求是数字,会进行小数的四舍五入

示例:

  • %5d:表示将整数的宽度控制在 5 位,如数字 11,被设置为 5d,就会变成:[空格][空格][空格]11,用三个空格补足宽度
  • %5.2f: 表示将宽度控制为 5,将小数点精度设置为 2
    小数点和小数部分也算入宽度计算。如,对 11.345 设置了 %7.2f 后,结果是:[空格][空格 11.35。2 个空格补足宽度,小数部分限制 2 位精度后,四舍五入为 .35
num1 = 11 num2 = 11.345 print("数字11宽度限制5,结果是:%5d" % num1) print("数字11.345宽度限制7,小鼠精度2,结果是:%7.2f" % num2)

字符串格式化方式 2

前通过 % 符号占位已经很方便了,还能进行精度控制。可是追求效率和优雅的 Python,是否有更加优雅的方式解决问题呢?

通过语法:f"内容{变量}"的格式来快速格式化。f 就是 format(格式化)的首字母。好处是原样输出,不需要做精度控制,适合对精度没有要求的适合快速使用

name = "传智播客" set_up_year = 2006 stock_price = 19.99 print(f"我是{name},我成立于:{set_uo_year}年,我今天的股价是:{stock_price}")

对表达式进行格式化

刚刚的演示,都是基于变量的。
可是,想更加优雅些,少写点代码,直接对“表达式”进行格式化是否可行呢?

表达式:一条具有明确执行结果的代码语句,如 age = 11+11

print("1 * 1的结果是:%d" % (1 * 1)) print(f"1 * 2的结果是:{1 * 2}") print("字符串在python中的类型是:%s" % type("字符串"))

适用于不需要储存数据的时候

数据输入(input 语句)

比如银行取钱,要输入密码。。

  • print:数据输出
  • input:数据输入

使用方法:input()语句可以从键盘获取输入,使用一个变量接收 input 语句获取的键盘输入数据即可

print("请告诉我你是谁") name = input() print("我知道了,你是:%s" % name)

进行到第二行会提示输入。但是这里的第一行是多余的,提示可以直接写到 input 里面

name = input("请告诉我你是谁?") print("我知道了,你是:%s" % name)

注意事项:无论输入什么样的数据,input 语句都会当成字符串。需要其它类型要进行数字类型转化:num = int(num)

判断语句

布尔类型和比较运算

判断是程序最核心最基础的逻辑功能

布尔也属于数字类型,True 表示真,False 表示假

用法:变量名称 = 布尔类型字面量。布尔类型不仅可以自行定义,也可以通过比较运算符进行计算得到布尔类型的结果

比较运算符

运算符 描述
== 判断内容是否相等,满足为 True
!= 判断内容是否不相等
> 判断左侧是否大于右侧
< 判断左侧是否小于右侧
>= 判断左侧是否大于等于右侧
<= 判断左侧是否小于等于右侧

定义变量储存布尔类型的数据

bool_1 = True bool_2 = False print(f"bool_1变量的内容是:{bool_1},类型是:{type(bool_1)}")

比较运算符的使用

一个等号是赋值运算,两个等号才是比较运算

num1 = 10 num2 = 10 print(f"10 == 10的结果是:{num1 == num2}")

if 语句的基本格式

if 要判断的条件:

条件成立时,要做的事情
age = 30 if age >= 18: print("我已经成年了") print("及你太美")

下面的 print 不受 if 的影响

注意:判断条件的结果一定要是布尔类型,不要忘记判断条件后的冒号,归属 if 语句的代码块需要在前方有 4 个空格缩进

if else 语句

if 是满足条件执行,if elae 是不满足执行

print("欢迎来到游乐园,儿童免费,成人收费") age = int(input("请输入你的年龄")) if age >= 18: print("已经成年,10元") elseprint("未成年,免费") print("玩得愉快")

if elif else 语句

有些场景,判断条件不止一个,可能有多个

print("欢迎来到动物园") height = int(input("输入身高")) vip_level = int(input("输入VIP等级")) if height < 120: print("免费") elif vip_level > 3: print("免费") elseprint"10元"print("游玩愉快")

注意:判断互斥,满足 1 就不会理会 2 和 3,都不满足进入 else

也可以将 input 输入语句直接写入判断条件中,节省代码量

if int(input("输入vip")) > 3:

判断语句的嵌套

有很多场景,不仅仅是多个并列条件,还会有满足前置条件才会二次判断的多层判断需求

第二个 if 属于第一个 if 内,只有第一个 if 满足条件,才会执行第二个 if

if int(input(("身高是多少")) > 120: print("超出限制") print("但是vip大于3可以免费") if int(input("vip是多少")) > 3: print(可以免费) else: print("10元") else: print(免费游玩)

循环语句

循环广告牌,批量修图,音乐轮播,动态壁纸,大喇叭喊话

while 循环语句的基础应用

只要条件满足就会无限循环执行

while 条件(布尔类型):

循环满足条件,做的事情 ......
i = 0 while i < 100: print("小美我喜欢你") i += 1

注意:

  1. while 的条件需得到布尔类型
  2. 需要设置循环终止的条件
  3. 空格缩进

while 循环的嵌套应用

i = 1 while i <= 100: print(f"今天是第{i}天,准备表白") j = 1 whlie j <= 10: print(f"送给小美第{j}只玫瑰花") j += 1 print("小美,我喜欢你") i += 1 print(f"坚持到第{i - 1}天,表白成功")

注意:避免出现无限循环

for 循环的基础语法

基础语法

  • while 循环的循环条件是自定义的,自行控制循环条件
  • for 循环是一种”轮询”机制,是对一批内容进行”逐个处理

for 临时变量 in 待处理数据集

循环满足条件执行的代码

待处理数据集,严格来说是序列类型:其内容可以一个个依次取出的一种类型,包括,字符串、列表、元组、等。

name = "itheima" for x in name: print(x)

结果是将字符串的内容依次取出,所以 for 循环也被称为:遍历循环

理论上,for 循环无法构建无限循环

range 语句

  1. range(num)
    获取一个从 0 开始,到 num 结束的数字序列(不含 num 本身)
    range(5)​取得的数据是:[0,1,2,3,4]
  2. range(num1, num2)
    获得一个从 num1 开始,到 num2 结束的数字序列(不含 num2 本身)
    range(5, 10)​取得的数据是:[5,6,7,8,9]
  3. range(num1, num2, step)
    获得一个从 num1 开始,到 num2 结束的数字序列(不含 num2 本身),数字之间的步长,以 step 为准(step 默认为 1)
    range(5, 10, 2)​取得的成绩是:[5,7,9]
for x in range(10): print(x)

变量作用域

for i in range(5): print(i) print(i)

上述这种写法,最后的 print 实际上是可以访问到的,但是在编程规范上不允许不建议的

解决方法:在循环之前,先把变量定义好

i = 0 for i in range(5): print(i) print(i)

for 循环的嵌套应用

i = 0 for i in range(1, 101): print(f"今天是向小美表白的第{i}天") for j in range(1, 11): print(f"给小美送的第{j}朵玫瑰花") print("小美我喜欢你") print(f"第{i}天,表白成功")

循环中断:break 和 continue

无论是 while 循环或是 for 循环,都是重复性的执行特定操作。在这个重复的过程中,如果要暂时跳过某次循环,直接进行下一次。或者提前退出循环,不在继续

continue 和 break 用以对循环进行临时跳过和直接结束

continue 关键字用于:终端本次循环,直接进入下一次循环。可用于 for 循环和 while 循环,效果一致

for i in range(1, 6): print("语句1") continue print("语句2")

上述代码运行后只输出了语句 1,continue 以下的不执行

continue 关键字只可以控制:它所在的循环临时中断

for i in range(1, 6): print("语句1") for j in range(1, 6): print("语句2") continue print("语句3") print("语句4")

break 关键字用于:直接结束循环。for 循环和 while 循环效果一致

for i in range(1, 101): print("语句1") break print("语句2") print("语句3")

上述代码运行后只输出了一次语句 1 和语句 3

break 关键字同样只可以控制:它所在的循环临时中断

for i in range(1, 6): print("语句1") for i in range(1, 6): print("语句2") break print("语句3") print("语句4")

上述代码,语句 1,语句 2,语句 4 都输出了 5 次

函数

函数的初体验

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

有的函数是编程语言内置的

# 需求,统计字符串的长度,不适用内置的函数len() str1 = "itheima" str2 = "itcast" str3 = "python" # 定义一个计数变量 count = 0 for i in str1: count += 1 print(f"字符串{str1}的长度是:{count}") count = 0 for i in str2: count += 1 print(f"字符串{str2}的长度是:{count}") count = 0 for i in str3: count += 1 print(f"字符串{str3}的长度是:{count}")

这样的代码重复性太大,低效

可以使用函数优化这个过程

def my_len(data): count = 0 for i in data: count += 1 print(f"字符串{data}的长度是{count}") # 然后就可以调用这个函数了、 my_len(str1) my_len(str2) my_len(str3)

把重复的劳动封装起来

函数的基础定义语法

函数的定义:

def 函数名(传入参数):

函数体 return 返回值
def say_hi(): print("Hi,我是及你太美") # 函数写好后要调用才会生效 say_hi()

注意事项:
参数如不需要,可以省略
返回值如不需要,可以省略
函数必须先定义后使用

函数的传入参数

传入参数,可以接收外部(调用时)提供的数据

def add(x, y): result = x + y print(f"{x} + {y}的结果是:{result}")

上述代码定义了 x 和 y 两个形式参数,表示函数声明将要使用两个参数,在函数调用中,提供的两个数值叫做实际参数

函数的返回值

def add(a, b): result = a+ b return result r = add(1, 2) print(r) # 输出结果:3

注意事项:一旦与导师 return​返回值,那么函数后面的代码将不再执行

None 类型

如果函数没有使用 return 返回值。那么函数也有返回值

有一个特殊的字面量:None,其类型是<class 'NoneType'>,没有返回值的函数,实际上就是返回了 None 这个字面量

None 表示:空的,无实际意义的意思

用在 if 判断上

  • 在 if 判断中,None 等同于 False
  • 一般用于在函数中主动返回 None,配合 if 判断做相关处理
def check_age(age): if age > 18: return "cuccess" else: return None result = check_age(16) if not result: print("未成年不可加入")

也可以用于声明无内容的变量上

定义变量,但暂时不需要变量有具体值,后续再处理,可以用 None 来代替

name = None

函数的说明文档

如果是纯代码语言,要一行一行理解,效率低。所以可以给函数添加说明文档,辅助理解函数的作用

用多行注释,内容写在函数体之前

def add(x, y): """ add函数可以进行两数相加 :param x:表示相加的其中一个数字 :param y:另一个数字 :return:相加的结果 """ result = x + y print(f"2数相加的结果是:{result}") return result

注意事项:调用函数的时候,鼠标悬停在函数上就会弹出说明文档

函数的嵌套调用

一个函数里面又调用了另外一个函数

def func_b(): print("222") def func_a(): print("111") func_b() print("333") fun_a

变量在函数中的作用域

变量作用域指的是变量的作用范围(变量在哪里可用,在哪里不可用)主要分为两类:局部变量和全局变量

  • 局部变量是定义在函数体内部的变量,即只在函数体内部生效

    def testA(): num = 100 print(num) testA() print(num)

    出了函数体,局部变量就无法使用了

  • 全局变量是在函数体内、外都能生效的变量

    num = 100 def testA(): print(num) def testB(): print(num) testA() testB() print(num)
  • global 关键字

    num = 100 def testA(): print(num) def testB(): num = 500 # 局部变量 print(num) testA() testB() print(num) # 输出 结果还是100

    但是可以用 global 声明一下全局变量

    num = 100 def testA(): print(num) def testB(): global num # global关键字声明为全局变量 num = 500 print(num) testA() testB() print(num) # 输出结果为500

数据容器

数据容器入门

思考问题:如果要在程序中,记录 5 名学生的信息,如姓名。如何做呢?

name1 = '威威' name1 = '单独' name1 = '让他' name1 = '煽风点火' name1 = '风格和'

定义一堆变量,人数一多就不现实

解决方法:数据容器,一种可以容纳多份数据的数据类型,容纳的每一份数据称之为 1 个元素每一个元素,可以是任意类型的数据,如字符串、数字、布尔等

数据类型根据特点不同,如:

  • 是否支持重复元素
  • 是否可以修改
  • 是否有序,等

分为五类,分别是:列表(list)、元组(tuple)、字符串(str)、集合(set)、字典(dict)

list(列表)的定义语法

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

  • []​作为标识
  • 列表内每一个元素之间,用逗号隔开
# 定义一个列表list my_list = ["itheima", 666, True] print(my_list) print(type(my_list)) # 定义一个嵌套列表 my_list = [[1, 2, 3], [a, b, c]] print(my_list)

注意事项:列表可以一次存储多个数据,而且可以是不同的数据类型,也支持嵌套

列表的下标索引

如何从列表中取出特定位置的数据呢?可以用下表索引

列表中的每一个元素,都有其位置下标索引,从前向后的方向,从 0 开始,依次递增我们只需要按照下标索引,即可取得对应位置的元素。或者,可以反向索引,也就是从后向前: 从-1 开始,依次递减 (-1、-2、-3......)。同样也符合嵌套,列表中有列表

my_list = ["a", "s", "d"] print(my_list[1]) # 输出:s print(my_list[-2]) # 输出:s print(my_list[4]) # 报错 # 取出嵌套列表的元素 my_list = [[1, 2, 3], [a, b, c]] print(my_list[1][2]) # 输出:c

列表的常用操作

列表除了可以:

  • 定义
  • 使用下表索引获取值

以外,列表也提供了一系列功能:

  • 插入元素
  • 删除元素
  • 清空元素
  • 修改元素
  • 统计元素个数

等等功能,都称之为:列表的方法

函数是一个封装的代码单元,可以提供特定功能在 Python 中,如果将函数定义为 class (类)的成员,那么函数会称之为: 方法

  • 函数:

    def add(x, y): return x + y
  • 方法

    class Student: def add(self, x, y): return x + y

函数和方法功能一样,有传入参数和返回值,只是方法的使用格式不同:

  • 函数的使用:num = add(1, 2)
  • 方法的使用:student = Student()
    num = student.add(1, 2)
编号 使用方式 作用
1 列表 . append(元素) 向列表中追加一个元素
2 列表 . extend(容器) 将数据容器的内容依次取出,追加到列表尾部
3 列表 . insert(下标,元素) 在指定下标处,插入指定的元素
4 del 列表[下标] 删除列表指定下标元素
5 列表 . pop(下标) 删除列表指定下标元素
6 列表 . remove(元素) 从前向后,删除此元素第一个匹配项
7 列表 . clear() 清空列表
8 列表 . count(元素) 统计此元素在列表中出现的次数
9 列表 . index(元素) 查找指定元素在列表的下标找不到报错 ValueError
10 len(列表) 统计容器内有多少元素

查找某元素的下标:
语法:列表 . index(元素)
index 就是列表对象(变量)内置的方法(函数)

mylist = ["lalala", "kunkun", "python"] index = mylist.index("kunkun") print(f"kunkun在列表的下表索引值是:{index}") # 1.2 如果被查找的元素不存在,会报错

修改特定位置(索引)的元素值:
语法:列表[下标] = 值

mylist[0] = "wujiayu"

插入元素:
语法:列表 . insert(下标,元素),在指定的下标位置插入指定的元素

mylist.insert(1, "lllll")

追加元素:
语法:列表​ . append(元素),将指定元素追加到列表尾部

mylist.append("及你太美")

追加元素方法 2:
语法:列表 . extend(其它数据容器),将其它数据容器的内容取出,一次追加到列表尾部

mylist.extend([1, 2, 3]) # 或者定义一个列表 mylist2 = [1, 2, 3] mylist.extend(mylist2)

删除元素:
语法 1:del 列表[下标]
语法 2:列表 . pop(下标)

# 方法1 del mylist[2] # 方法2 mylist.pop(2) # 还可以用变量接收删除的元素 element = mylist.pop(2)

删除某元素在列表中的第一个匹配项
语法:列表 . remove(元素)

mylist = [1, 2, 3, 2, 5] mulist.remove(2) print(mylist) # 结果:[1, 3, 2, 5]

清空列表内容:
语法:列表 . clear()

mylist = [1, 2, 3] mylist.clear() print(mylist) # 结果:[]

统计某元素在列表的数量:
语法:列表​ . count(元素)

mulist = [1, 1, 1, 2, 3] print(mylist.count(1)) # 结果:3

统计列表内,有多少元素
语法:len(列表)

mylist = [1, 2, 3, 4, 5] print(len(mylist)) # 结果:5

总结出列表有如下特点:

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

list(列表)的循环遍历

既然数据容器可以存储多个元素,那么,就会有需求从容器内依次取出元素进行操作将容器内的元素依次取出进行处理的行为,称之为: 遍历、迭代。

index = 0 while index < len(列表): 元素 = 列表[insex] 对元素进行处理 index += 1

while 循环的遍历

def list_while_func(): mylist = [1, 2, 3, 4] index = 0 while index < len(mylist): element = mylist[index] print(f"列表的元素:{element}") index += 1

for 循环的遍历

对比 while,for 循环更加适合对列表等数据容器进行遍历

def list_for_func(): mylist = [1, 2, 3, 4, 5] for element in mylist print(element)

注意事项:while 循环适用于任何想要循环的场景
for 循环适用于,遍历数据容器的场景或简单的固定次数循环场景,是使用场景最多的循环

tuple(元组)的定义和操作

列表是可以修改的,如果想要传递的信息,不被篡改,列表就不合适了

所以,当我们需要在程序内封装数据,又不希望数据被篡改,那么元组就非常合适了

元组的定义格式

t1 = (1, "hello", True) t2 = () # 空元组 t3 = tuple() # 空元组

定义单个元素的元组

t4 = ("hello, ") # 必须要协商空的逗号才能是元组,要不然数据类型是str

元组也支持嵌套

t5 = ((1, 2, 3), (4, 5, 6))

下标索引取出内容

num = t5[1][2]
编号 方法 作用
1 .index() 查找某个数据,如果数据存在返回对应的下标,否则报错
2 .count() 统计某个数据在当前元组出现的次数
3 .len(元组) 统计元组内的元素个数
# index()查找某个数据的下标 t6 = (1 ,2 ,3 ,4) index = t6.index(1) # count()统计某个数据在当前元组出现的次数 t7 = (1, 1, 3, 4, 1, 1, 7, 8, 9) num = t7.count(1) # len(元组)统计元组内的元素个数 t8 = (1, 1, 3, 4, 1, 1, 7, 8, 9) num = len(t8)

元组的遍历

# 元组的遍历:while t8 = (1, 1, 3, 4, 1, 1, 7, 8, 9) index = 0 while index < len(t8): print(f"元组的元素有:{t8[index]}") index += 1 # 元组的遍历:for for element in t8: print(f"元组的元素有:{element}")

注意事项:不可修改元组的内容,否则会直接报错
但是可以修改元组内 list 的内容(修改元素,增加,删除,反转等)

t9 = (1, 2, [8, 9, 7]) t9[2][0] = "及你太美" print(f"t9的内容是:{t9}") 结果:t9的内容是:(1, 2, ['及你太美', 9, 7])

str(字符串)的定义和操作

尽管字符串看起来并不像:列表、元组那样,一看就是存放了许多数据的容器但不可否认的是,字符串同样也是数据容器的一员。
字符串是字符的容器,一个字符串可以存放任意数量的字符
如,字符串:“itheima"。拆开就是 i, t, h, e, i, m, a。

my_str = "kunkun and amagi" # 通过下标索引取值 value = my_str[2]
编号 操作 说明
1 字符串[下标] 根据下标索引取出特定位置字符
2 字符串.index(字符串) 查找给定字符的第一个匹配项的下标
3 字符串.replace(字符串 1,字符串 2) 将字符串内的全部字符串 1,替换为字符串 2 不会修改原字符串,而是得到一个新的
4 字符串.split(字符串) 按照给定字符串,对字符串进行分隔不会修改原字符串,而是得到一个新的列表
5 字符串.strip()
字符串.strip(字符串)
移除首尾的空格和换行符或指定字符串
6 字符串.count(字符串) 统计字符串内某字符串的出现次数
7 len(字符串) 统计字符串的字符个数

同元组一样,字符串是一个:无法修改的数据容器

查找特定字符串的下标索引值
语法:字符串​ . index(字符串)

my_str = "kunkun and amagi" value = my_str.index("and")

字符串的替换
语法:字符串 . replace(字符串 1,字符串 2)
功能:将字符串内的全部内容:字符串 1,替换成字符串 2.
注意:不是修改字符串本身,而是得到了一个新的字符串

my_str = "kunkun and amagi" my_str. replace("and", "和")

字符串的分割:
语法:字符串​ . split(分隔符字符串)
功能:按照指定的分隔符字符串,将字符串划分为多个字符串,并存入列表对象中
注意:字符串本身不变,而是得到了一个列表对象

my_str = "kunkun and amagi" my_str_list = my_str.split(" ")

字符串的规整操作(去前后空格)
语法:字符串​ . strip()

my_str = " kunkun and amagi " new_my_str = my_str.strip() # 不传入参数就是去除首尾空格

字符串的规整操作(去前后指定字符串)
语法:字符串 . strip(字符串)

my_str = "1kunkun 2and 3amagi" new_my_str = my_str.strip("12") # 传入的是"12",实际上是分开的"1", "2"

统计字符串中某字符串的出现次数

my_str = "kunkun and amagi" count = my_str.count("kun")

统计字符串的长度

num = len(my_str)

字符串和列表,元组一样,也支持 while 循环和 for 循环进行遍历

  • while 循环

    my_str = "及你太美" index = 0 while index < len(my_str): print(my_str[index]) index += 1
  • for 循环

    my_str = "及你太美" for i in my_str: print(i)

字符串的特点

  • 只可以存储字符串
  • 长度任意 (取决于内存大小)
  • 支持下标索引
  • 允许重复字符串存在
  • 不可以修改 (增加或删除元素等)
  • 支持 for 循环

数据容器(序列)的切片

序列是指:内容连续、有序,可使用下标索引的一类数据容器

列表、元组、字符串,均可以可以视为序列。

序列的切片操作
序列支持切片,即:列表、元组、字符串,均支持进行切片操作
切片: 从一个序列中,取出一个子序列
语法: 序列[起始下标:结束下标:步长]
表示从席列中,从指定位置开始,依次取出元素,到指定位置结束,得到一个新序列:

  • 起始下标表示从何处开始,可以留空,留空视作从头开始

  • 结束下标(不含)表示何处结束,可以留空,留空视作截取到结尾

  • 步长表示,依次取元素的间隔

    • 步长 1 表示,一个个取元素
    • 步长 2 表示,每次跳过 1 个元素取
    • 步长 N 表示,每次跳过 N-1 个元素取
    • 步长为负数表示,反向取(注意,起始下标和结束下标也要反向标记)

注意:此操作不会影响序列本身,而是会得到一个新的序列(列表、元组、字符串)

my_list = [0, 1, 2, 3, 4, 5, 6] result1 = my_list[1:4]

set(集合)的定义和操作

集合不是序列

我们目前接触到了列表、元组、字符串三个数据容器了。基本满足大多数的使用场景。
为何又需要学习新的集合类型呢?

通过特性分析:

  • 列表可修改、支持重复元素且有序
  • 元组、字符串不可修改、支持重复元素且有序

局限就在于:它们都支持重复元素。如果场景需要对内容做去重处理,就不方便了

集合最主要的特点就是不支持元素的重复(自带去重功能),并且内容无序

集合的定义

# 定义集合字面量 {元素,元素,.....,元素} # 定义集合变量 变量名称 = {元素,元素,.....,元素} # 定义空集合 变量名称 = set()

和列表,元组,字符串等定义基本相同:

数据容器 定义方法
列表 []
元组 ()
字符串 ""
集合 {}
myset = {及, 你, 太, 美, 及} # 结果:及, 你, 太, 美。会自动去重

首先,因为集合是无序的,所以集合不支持:下标索引访问。(因为没有顺序)
但是集合和列表一样,是允许修改的,所以我们来看看集合的修改方法

编号 操作 说明
1 集合 . add(元素) 集合内添加一个元素
2 集合 . remove(元素) 移除集合内指定的元素
3 集合 . pop() 从集合中随机取出一个元素
4 集合 . clear() 将集合清空
5 集合 1 . difference(集合 2) 得到一个新集合,内含 2 个集合的差集原有的 2 个集合内容不变
6 集合 1 . difference_update(集合 2) 在集合 1 中,删除集合 2 中存在的元素集合 1 被修改,集合 2 不变
7 集合 1 . union(集合 2) 得到 1 个新集合,内含 2 个集合的全部元素原有的 2 个集合内容不变
8 len(集合 得到一个整数,记录了集合的元素数量
  • 添加新元素
    语法:集合 . add(元素)。将指定元素添加到集合内
    结果:集合本身被修改,添加了新元素

    my_set = {"hello", "world"} my_set.add("itheima")
  • 移除元素
    语法:集合 . remove(元素),将指定元素从集合内移除
    结果:集合本身被修改,移除了元素

    my_set = {"hello", "world", "kunkun"} my_set.remove("hello")
  • 从集合中随机取出元素
    语法:集合 . pop(),从集合中随机取出一个元素
    结果:会得到一个元素的结果。同时集合本身被修改,元素被移除

    my_set = {"hello", "world", "kunkun"} element = my_set.pop() print(my_set) # 结果:{"world", "kunkun"} print(element) # 结果:'hello'
  • 清空集合
    语法:集合 . clear()
    结果:集合本身被清空

    my_set = {"hello", "world", "kunkun"} my_set.clear() print(my_set) # 结果:set() 空集合
  • 取出 2 个集合的差集
    语法:集合 1 . difference(集合 2),取出集合 1 和集合 2 的差集(集合 1 有而集合 2 没有的)
    结果:得到一个新集合,集合 1 和集合 2 不变

    set1 = {1, 2, 3} set2 = {1, 5, 6} set3 = set1.difference(set2) print(set3) # 结果:{2, 3} 得到新的集合 print(set1) # 结果:{1, 2, 3} 不变 print(set2) # 结果:{1, 5, 6} 不变
  • 消除 2 个集合的差集
    语法:集合 1.difference_update(集合 2)
    功能:对比集合 1 和集合 2,在集合 1 内,删除和集合 2 相同的元素。
    结果:集合 1 被修改,集合 2 不变

    set1 = {1, 2, 3} set2 = {1, 5, 6} set1.difference_update(set2) print(set1) # 结果:{2, 3} print(set2) # 结果:{1, 5, 6}
  • 2 个集合合并
    语法:集合 1.union(集合 2)
    功能:将集合 1 和集合 2 组合成新集合
    结果:得到新集合,集合 1 和集合 2 不变

    set1 = {1, 2, 3} set2 = {1, 5, 6} set3 = set1.union(set2) print(set3) # 结果:{1, 2, 3, 5, 6} 新集合 print(set1) # 结果:{1, 2, 3} 不变 print(set2) # 结果:{1, 5, 6} 不变
  • 统计集合元素数量
    语法:len(集合)

    set1 = {1, 2, 3} num = len(set1)

集合的遍历

集合不支持下标索引,所以不能用 while 循环
但是可以用 for 循环

set1 = {1, 2, 3} for element in set1: print(f"集合的元素有:{element}")

集合的特点:

  • 可以容纳多个数据
  • 可以容纳不同类型的数据 (混装 )
  • 数据是无序存储的 (不支持下标索引)
  • 不允许重复数据存在
  • 可以修改 (增加或删除元素等)
  • 支持 for 循环

dict(字典、映射)的定义

生活中的字典 Python 中的字典
【字】:【含义】 Key:Value

通过 Key(学生姓名),取到对应的 Value(考试成绩)

Key Value
"王力宏" 99
"周杰伦" 88
"林俊杰" 77

字典的定义

同样使用 {},不过存储的元素是一个个的:键值对,如下语法

# 定义字典字面量 {key: value, key: value, ......, key: value} # 定义字典变量 my_dict = {key: value, key: value, ......, key: value} # 定义空字典 my_dict = {} # 空字典定义方式1 my_dict = dict() # 空字典定义方式2
my_dict = {"王力宏": 99, "周杰伦": 88, "林俊杰", 77}

字典中的 Key​是不可以重复的
如果出现重复,那么后面的会把前面的替换掉

字典同集合一样,不可以使用下标索引
但是字典可以通过 Key​值来取得对应的 Value

my_dict = {"王力宏": 99, "周杰伦": 88, "林俊杰", 77} print(my_dict["王力宏"]) # 结果:99

字典的嵌套
字典的 Key​和 Value​可以是任意数据类型(Key 不可为字典)
那么,就表明,字典是可以嵌套的
需求如下:记录学生各科的考试信息

stu_score_dict = { "王力宏": { "语文": 77, "数学": 66, "英语": 33 }, "周杰伦": { "语文": 66, "数学": 86, "英语": 53 }, "林俊杰": { "语文": 97, "数学": 56, "英语": 93 } } # 获取信息 stu_score_dict["周杰伦"]["语文"]

字典的常用操作

编号 操作 说明
1 字典[Key] 获取指定 Key 对应的 Value 值
2 字典[Key]= Value 添加或更新键值对
3 字典.pop(Key) 取出 Key 对应的 Value 并在字典内删除此 Key 的键值对
4 字典.clear() 清空字典
5 字典.keys() 获取字典的全部 Key,可用于 for 循环遍历字典
6 len(字典) 计算字典内的元素数量
  • 新增元素
    语法:字典 [Key] = Value
    结果:字典被修改,新增了元素

    my_dict = {"王力宏": 99, "周杰伦": 88, "林俊杰", 77} my_dict["张信哲"] = 66
  • 更新元素
    语法:字典 [Key] = Value
    结果:字典被修改,元素被更新
    注意:字典 Key 不可以重复,所以对已存在的 Key 执行上述操作,就是更新 Value 值

    my_dict = {"王力宏": 99, "周杰伦": 88, "林俊杰", 77} my_dict["周杰伦"] = 66
  • 删除元素
    语法:字典 . pop(Key)
    结果:获得指定 Key 的 Value,同时字典被修改,指定 Key 的数据被删除

    my_dict = {"王力宏": 99, "周杰伦": 88, "林俊杰", 77} score = my_dict.pop("周杰伦")
  • 清空字典
    语法:字典 . clear()​,结果:字典被修改,元素被清空

    my_dict.clear()
  • 获取全部的 Key
    语法:字典.keys()
    结果:得到字典中的全部 Key

    keys = my_dict.keys() # 可以帮助做字典的遍历
  • 统计字典内的元素数量
    语法:len(字典)

    num = len(my_dict)

字典的遍历

  • 方法 1:通过获取到全部 key 来完成遍历

    my_dict = {"王力宏": 99, "周杰伦": 88, "林俊杰", 77} keys = my_dict.keys() for key in keys: print(f"字典的key是:{key}") print(f"字典的value是:{my_dict[key]}")
  • 方法 2:直接对字典进行 for 循环,每一次循环都是直接得到 key

    for key in my_dict: print(f"2字典的key是:{key}") print(f"2字典的value是:{my_dict[key]}")

字典的特点

  • 可以容纳多个数据
  • 可以容纳不同类型的数据
  • 每一份数据是 KeyValue 键值对
  • 可以通过 Key 获取到 Value,Key 不可重复 (重复会覆盖)
  • 不支持下标索引
  • 可以修改(增加或删除更新元素等)
  • 支持 for 循环,不支持 while 循环

5 类数据容器对比总结

列表 元组 字符串 集合 字典
元素数量 支持多个 支持多个 支持多个 支持多个 支持多个
元素类型 任意 任意 仅字符 任意 Key: Value
Key:除字典外任意类型
Value:任意类型
下标索引 支持 支持 支持 不支持 不支持
重复元素 支持 支持 支持 不支持 不支持
可修改性 支持 不支持 不支持 支持 支持
数据有序
使用场景 可修改、可重复的一批数据记录场景 不可修改、可重复的一批数据记
录场景
一串字符的记录场景 不可重复的数据记录场景 以 Key 检索 Value 的数据记录场景

数据容器可以从以下视角进行简单的分类:

  • 是否支持下标索引

    • 支持:列表、元组、字符串-序列类型
    • 不支持:集合、字典-非序列类型
  • 是否支持重复元素

    • 支持:列表、元组、字符串-序列类型
    • 不支持:集合、字典-非序列类型
  • 是否可以修改

    • 支持:列表、集合、字典
    • 不支持:元组、字符串

各类数据容器的应用场景:

  • 列表:一批数据,可修改、可重复的存储场景
  • 元组:一批数据,不可修改、可重复的存储场景
  • 字符串:一串字符串的存储场景
  • 集合:一批数据,去重存储场景
  • 字典:一批数据,可用 Key 检索 Value 的存储场景

数据容器的通用操作

功能 描述
通用 for 循环 遍历容器 (字典是遍历 key)
max 容器内最大元素
min() 容器内最小元素
len() 容器元素个数
list() 转换为列表
tuple() 转换为元组
str() 转换为字符串
set() 转换为集合
sorted(序列,[reverse=True]) 排序,reverse=True 表示降序
得到一个排好序的列表

数据容器的通用操作 - 遍历

数据容器尽管各自有各自的特点,但是它们也有通用的一些操作

首先,在遍历上:

  • 5 类数据容器都支持 for 循环遍历
  • 列表、元组、字符串支持 while 循环,集合、字典不支持 (无法下标索引)

数据容器通用统计功能

  • len(容器)
    统计容器的元素个数

    my_list = [1, 2, 3] my_tuple = (1, 2, 3, 4, 5) my_str = "itheima" print(len(my_list)) # 结果:3 print(len(my_tuple)) # 结果:5 print(len(my_str)) # 结果:7
  • max(容器)
    统计容器的最大元素

    my_list = [1, 2, 3] my_tuple = (1, 2, 3, 4, 5) my_str = "itheima" print(max(my_list)) # 结果:3 print(max(my_tuple)) # 结果:5 print(max(my_str)) # 结果:t
  • min(容器)
    统计容器的最小元素

    my_list = [1, 2, 3] my_tuple = (1, 2, 3, 4, 5) my_str = "itheima" print(min(my_list)) # 结果:1 print(min(my_tuple)) # 结果:1 print(min(my_str)) # 结果:a

容器的通用转换功能

  • list(容器)
    将给定容器转换为列表

    list(my_...)
  • str(容器)
    将给定容器转换为字符串

    str(my_...)
  • tuple(容器)
    将给定容器转换为元组

    tuple(my_...)
  • set(容器)
    将给定容器转换为集合

    set(my_...)

容器通用排序功能

sorted(容器, [reverse = True])
将给定容器进行排序

sorted(my) sorted(my, [reverse = True]) # 排序反转

字符串大小比较的方式

ASCII 码表

在程序中,字符串所用的所有字符如:

  • 大小写英文单词
  • 数字
  • 特殊符号(!、\、|、@、#、空格等)

都有其对应的 ASCII 码表值
每一个字符都能对应上一个:数字的码值
字符串进行比较就是基于数字的码值大小进行比较的

字符串比较
字符串是按位比较,也就是一位位进行对比,只要有一位大,那么整体就大。

函数进阶

函数的多返回值

如果一个函数写了两个 return,程序如何执行?

只执行了一个 return,原因是因为 return 可以推出当前函数,导致 return 下方的代码不执行

解决方法

def test_return(): return 1, 2 x, y = test_return() print(x) # 结果:1 print(y) # 结果:2 # 按照返回值的顺序,写对应顺序的多个变量接收即可变量之间用逗号隔开 # 支持不同类型的数据return

函数的多种传参方式

使用方式上的不同,函数有 4 中常见参数使用方式:

  • 位置参数:调用函数时根据函数定义的参数位置来传递参数

    def user_info(name, age, gender): print(f"你的名字是{name}, 年龄是{age}, 性别是{gender}") user_info("TOM", 20, "男")

    注意:传递的参数和定义的参数的顺序及个数必须一致

  • 关键字参数:函数调用时通过“键 = 值”形式参数
    作用:可以让函数更加清晰,容易使用,同时也清除了参数的顺序需求

    def user_info(name, age, gender): print(f"你的名字是{name}, 年龄是{age}, 性别是{gender}") user_info(name = "TOM", age = 20, gender = "男")

    注意:函数调用时,如果有位置参数时,位置参数必须在关键字参数的前面,但关键字参数之间不存在先后顺序

  • 缺省参数:缺省参数也叫默认参数,用于定义函数,为参数提供默认值,调用函数时可不传该默认参数的值(注意:所有位置参数必须出现在默认参数前,包括函数定义和调用)
    作用:当调用函数时没有传递参数,就会使用默认是用缺省参数对应的值

    def user_info(name, age, gender = '男'): print(f"你的名字是{name}, 年龄是{age}, 性别是{gender}") user_info("TOM", 20, "女") # 结果:女 user_info("TOM", 20) # 结果:男

    注意:函数调用时,如果为缺省参数传值则修改默认参数值,否则使用这个默认值

  • 不定长参数:不定长参数也叫可变参数,用于不确定调用的时候会传递多少个参数(不传参也可以)的场景
    作用:当调用函数时不确定参数个数时,可以使用不定长参数

    不定长参数的类型:

    1. 位置传递

      def user_info(*args): # args是规范要求 print(args) user_info('TOM') user_info('TOM', 18)

      注意:传进的所有参数都会被 args 变量收集,它会根据传进参数的位置合并为一个元组(tuple),args 是元组类型,这就是位置传递

    2. 关键字传递

      def user_info(**kwargs): print(kwargs) user_info(name = 'TOM', age = 18, id = 110)

      注意:参数是“键 = 值”形式的情况下,索引的“键 = 值”都会被 kwargs 接受,同时会根据“键 = 值”组成字典

函数作为参数传递

在前面的函数学习中,我们一直使用的函数,都是接受数据作为参数传入:

  • 数字
  • 字符串
  • 字典、列表、元组等

其实,我们学习的函数本身,也可以作为参数传入另一个函数内。

def test_func(compute): result = compute(1, 2) print(result) def compute(x, y): return x + y test_func(compute) # 结果:3

这是一种,计算逻辑的传递,而非数据的传递

lambda 匿名函数

在函数的定义中

  • def 关键字,可以定义带有名称的函数
  • lambda 关键字,可以定义匿名函数(无名称)

有名称的函数,可以基于名称重复使用

无名称的匿名函数,只可临时使用一次

匿名函数定义语法:

lambda 传入参数: 函数体(一行代码)

  • lambda 是关键字,表示定义匿名函数

  • 传入参数表示匿名函数的形式参数,如:xy 表示接收 2 个形式参数

  • 函数体,就是函数的执行逻辑,要注意:只能写一行,无法写多行代码

  • def

    def test_func(compute): result = compute(1, 2) print(result) def compute(x, y): return x + y test_func(compute) # 结果:3
  • lambda

    def test_func(compute): result = compute(1, 2) print(result) test_func(lambda x, y: x + y) # 结果:3

文件操作

文件编码概念

思考:计算机只能识别:0 和 1,那么我们丰富的文本文件是如何被计算机识别,并存储在硬盘中呢?

答案:使用编码技术 (密码本)将内容翻译成 0 和 1 存入。

编码技术:翻译的规则,记录了如何将内容翻译成二进制,以及如何将二进制翻译回可识别内容。

计算机中有很多可用编码

  • UTF-8
  • G8K
  • Big5

不同的编码,将内容翻译成二进制也是不同的。

所以说以说明编码写入就以说明编码读取

查看文件编码

我们可以使用 Windows 系统自带的记事本,打开文件后,即可看出文件的编码是什么
UTF-8 是目前全球通用的编码格式(百分之 99.999 都是它)
除非有特殊需求,否则,一律以 UTF-8 格式进行文件编码即可

文件的读取操作

什么是文件

内存中存放的数据在计算机关机后就会消失。要长久保存数据,就要使用硬盘、光盘、I 盘等设备。为了便于数据的管理和检索,引入了“文件”的概念。
一篇文章、一段视频、一个可执行程序,都可以被保存为一个文件,并赋予一个文件名。操作系统以文件为单位管理磁盘中的数据。一般来说,文件可分为文本文件、视频文件、音频文件、图像文件、可执行文件等多种类别。

文件操作主要包括:打开、关闭、读、写等操作

open()打开函数

在 Python,使用 open 函数,可以打开一个已经存在的文件,或者创建一个新文件,语法如下

open(name, mode, encoding)

  • name:是要打开的目标文件名的字符串(可以包含文件所在的具体路径)。

  • mode:设置打开文件的模式(访问模式): 只读、写入、追加等

  • encoding:编码格式(推荐使用 UTF-8)

  • 示例代码

    f = open('D:/python.txt', 'r', encoding = "UTF-8") # encoding的顺序不是第三位,所以不能用位置参数,用关键字参数直接指定

    注意:此时的“是”open 函数的文件对象,对象是 Python 中一种特殊的数据类型,拥有属性和方法,可以使用对象.属性或对象.方法对其进行访问,后续面向对象课程会给大家进行详细的介绍。
    指定文件名默认是在 python 文件一个层级,否则要指定路径和文件名

模式 描述
r 以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。
w 打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,原有内容会被删除.
如果该文件不存在,创建新文件。
a 打开一个文件用于追加。如果该文件已存在,新的内容将会被写入到已有内容之后。
如果该文件不存在,创建新文件进行写入。

读操作相关方法

操作 功能
文件对象 = open(file, mode, encoding) 打开文件获得文件对象
文件对象 . read(num) 读取指定长度字节不指定 num 读取文件全部
文件对象 . readline() 读取一行
文件对象 . readlines() 读取全部行,得到列表
for line in 文件对象 for 循环文件行,一次循环得到一行数据
文件对象 . close() 关闭文件对象
with open() as f 通过 with open 语法打开文件,可以自动关闭
  • read()方法:
    文件对象.read(num)
    num 表示要从文件中读取的数据的长度(单位是字节),如果没有传入 num,那么就表示读取文件中所有的数据

    f.read(10) f.read() # 第二次调用是从第一次调用后的结尾开始读取
  • readlines0 方法:
    readlines 可以按照行的方式把整个文件中的内容进行一次性读取,并且返回的是一个列表,其中每一行的数据为一个
    元素。

    lines = f.readlines() # 读取文件的全部行,封装到列表中 # \n,是换行符
  • readline()方法:一次读取一行内容

    lines = f.readline()
  • for 循环读取文件行

    for line in open("D:/python.txt", "r"): print(line)
  • close()关闭文件对象

    f.close() # 解除文件的占用 time.sleep(500000) # 此时文件在占用无法删除或者修改文件
  • with open 语法

    with open('D:/python.txt', 'r', encoding = "UTF-8") as f: for line in f: print(f"每一行数据是:{line}") # 文件会自动关闭,避免遗忘

文件的写出操作

# 打开文件 f = open("D:/python.txt", "w") # 文件写入 f.write("hello world") # 内容刷新 f.flush() # close方法,内置了flush功能 f.close()

注意:

  • 直接调用 write,内容并未真正写入文件,而是会积攒在程序的内存中,称之为缓冲区
  • 当调用 flush 的时候,内容会真正写入文件
  • 这样做是避免频繁的操作硬盘,导致效率下降(攒一堆,一次性写磁盘)
  • w 模式:当文件不存在的时候会创建文件然后写入内容
    w 模式:当文件存在,会先清空内容,然后写入新的内容
    close()方法,带有 flush()方法的功能

文件的追加写入操作

# 打开文件,通过a模式打开即可 f = open('python.txt', 'a') # 文件写入 f.write('hello world') # 内容刷新 f.flush()

注意:

  • a 模式,文件不存在会创建文件
  • a 模式,文件存在会在最后,追加写入文件
    要换行写入:\n

异常、模块与包

了解异常

当检测到一个错误时,Python 解释器就无法继续执行了,反而出现了一些错误的提示,这就是所谓的“异常”,也就是
我们常说的 BUG

bug 单词的诞生
早期计算机采用大量继电器工作,马克二型计算机就是这样的。
1945 年 9 月 9 日,下午三点,马克二型计算机无法正常工作了,技术人员试了很多办法,最后定位到第 70 号继电器出错。负责人哈珀观察这个出错的继电器,发现一只飞娥躺在中间,已经被电器打死。她小心地用摄子将饿子夹出来,用透明胶布帖到“事件记录本”中,并注明“第一个发现虫子的实例。"自此之后,引发软件失效的缺陷,便被称为 Bug。

异常的捕获

为什么要捕获异常

世界上没有完美的程序,任何程序在运行的过程中,都有可能出现:异常,也就是出现 buo 导致程序无法完美运行下去。

我们要做的,不是力求程序完美运行而是在力所能及的范围内,对可能出现的 bug,进行提前准备、提前处理。

这种行为我们称之为:异常处理 (捕获异常)

基本语法

try: 可能发生错误的代码 except: 如果出现异常执行的代码

异常捕获示例

# 读取一个不存在的文件 f = open('D:/python.txt', 'r', encoding = "UTF-8") # 捕获方法 try: f = open('D:/python.txt', 'r', encoding = "UTF-8") except: print("出现异常了,因为文件不存在,将open的模式,改为w模式去打开") f = open('D:/python.txt', 'w', encoding = "UTF-8")

捕获指定异常

try: print(name) except NameError as e: print("出现了变量未定义的异常") print(e) # e是异常的对象 # 只捕获了NameError的异常,其它异常不会捕获

捕获多个异常

try: 1 / 0 except (NameError, ZeroDivisionError) as e: print("出现了变量未定义 或者 除以0的异常错误")

捕获全部异常

try: 1 / 0 except Exception as e: print("出现异常了") # 或者用基础语法也可以捕获全部异常

异常 eles

eles 表示的是如果没有异常要执行的代码

try: print("hello") except Exception as e: print("出现异常了") else: print("好高兴,没有异常")

异常的 finally

finally 表示的是无论是否异常都要执行的代码,例如关闭文件

try: f = open('D:/python.txt', 'r', encoding = "UTF-8") except Exception as e: print("出现异常了") f = open('D:/python.txt', 'w', encoding = "UTF-8") else: print("好高兴,没有异常") finally: f.close()

异常的传递性

def func1(): print("func1开始执行") num = 1 / 0 print("func1结束执行") def func2(): print("func2开始执行") func1() print("func2结束执行") def main(): func2() main()

捕获异常传递

def func1(): print("func1开始执行") num = 1 / 0 print("func1结束执行") def func2(): print("func2开始执行") func1() print("func2结束执行") def main(): try: func2() except Exception as e: print(f"出现异常了,异常的信息是{e}") main()

不需要在真正出现问题的代码进行捕获,因为会传递

模块的概念和导入

Python 模块(Module),是一个 Python 文件,以 py 结尾,模块能定义函数,类和变量,模块里也能包含可执行的代码

作用:python 中有很多各种不同的模块,每一个模块都可以帮助我模块的作用:们快速的实现一些功能,比如实现和时间相关的功能就可以使用 time 模块我们可以认为一个模块就是一个工具包,每一个工具包中都有各种不同的工具供我们使用进而实现各种不同的功能

模块的导入方式

模块在使用前需要先导入 导入的语法如下:

[from 模块名] import [模块 | 类 | 变量 | 函数 | *] [as 别名]

常用的组合形式:

  • import 模块名
  • from 模块名 import 类、变量、方法等
  • from 模块名 import*
  • import 模块名 as 别名
  • from 模块名 import 功能名 as 别名

import 模块名

import time # 导入time的内置模块,按住ctrl左击就能查看代码 print("你好") time.sleep(5) #调用time模块里的sleep函数,代码执行到这里就暂停5秒 print("我好")

from 模块名 import 功能名

from time import sleep print("你好") sleep(5) print("我好")

from 模块名 import*

from time import * # *表示全部的意思 print("你好") sleep(5) print("我好")

as 定义别名

import time as t # 给模块改名 print("你好") t.sleep(5) print("我好") from time import sleep as sl print("你好") sl(5) print("我好")

自定义模块并导入

Python 中已经帮我们实现了很多的模块,不过有时候我们需要一些个性化的模块,这里就可以通过自定义模块实现,也就是自己制作一个模块

每个 Python 文件都可以作为一个模块,模块的名字就是文件的名字,也就是说自定义模块名必须要符合标识符命名规则

def test(a, b): print(a + b)
import my_module1 my_module1.test(1, 2)

如果是两个模块的名字相同的函数会如何
下面的会覆盖上面的

# 模块1 def test(a, b): print(a + b) # 模块2 def test(a, b): print(a - b) from my_module1 import test from my_module2 import test

测试模块

此时,无论是当前文件,还是其他已经导入了该模块的文件,在运行的时候都会自动执行 test 函数的调用

# 模块1 def test(a, b): print(a + b) if __name__ == '__main__': # __name__是内置变量,当右键运行的时候这里判断为True # 当以模块导入的时候,就判断为False test(1, 2) from my_module1 import test

alla

当模块有 all 变量的时候*就只有 all 的列表,没有 all 变量就是全部都有

# 模块 __all__ = ['testa'] def testa(a, b): print(a + b) def testb(a, b): print(a - b) from my_module1 import *

自定义 Python 包

基于 Python 模块,我们可以在编写代码的时候,导入许多外部代码来丰富功能。
但是,如果 Python 的模块太多了,就可能造成一定的混乱,那么如何管理呢?

python 包

从物理上看,包就是一个文件夹,在该文件夹下包含了一个 __nit__.py ​​文件,该文件夹可用于包含多个模块文件从逻辑上看,包的本质依然是模块

快速入门:

步骤如下:

  1. 新建包 'my_package'
  2. 新建包内模块:'my_module1' 和 'my_module2'
  3. 模块内写入代码

pycharm 中的基本步骤:
new > python package > 输入包名 > ok > 新建功能模块(有联系的模块)
注意:新建包后,内部会自动创建 __nit__.py​文件,这个文件控制包的导入行为。

导入包

  • 方式 1:
    import 包名 . 模块名
    包名 . 模块名 . 目标

    import my_package.my_module1 import my_package.my_module2 my_package.my_module2.info_print()
    from my_package import my_module1 from my_package import my_module2 my_module2.info_print() from my_package.my_module1 import info_print info_print()
  • 方式 2
    from 包名 import*
    模块名 . 目标
    注意:必须在 '__init__.py'​文件中添加 '__all__ = []'​,控制允许导入的模块列表

    # `__init__.py`文件下 __all__ = ['my_module1'] # 只包含模块1的函数 from my_module1 import *

安装第三方包

在 Python 程序的生态中,有许多非常多的第三方包(非 Python 官方),可以极大的帮助我们提高开发效率,如:

  • 科学计算中常用的:numpy 包
  • 数据分析中常用的:pandas 包
  • 大数据计算中常用的:pyspark、apache-flink 包
  • 图形可视化常用的: matplotlib、pyecharts
  • 人工智能常用的: tensorflow

安装第三方包 - pip

第三方包的安装非常简单,我们只需要使用 Python 内置的 pip 程序即可

打开我们许久未见的: 命令提示符程序,在里面输入:pip install 包名称 即可通过网络快速安装第三方包

pip 的网络优化

由于 pip 是连接的国外的网站进行包的下载,所以有的时候会速度很慢。

我们可以通过如下命令,让其连接国内的网站进行包的安装:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple 包名称

安装第三方包 - pycharm

打开右下角解释器设置,点击 + 号搜索想要的包

Python 语言的高阶加强

面向对象

初识对象

使用变量记录数据太乱了。

如果程序中也和生活中一样

  • 可以设计表格
  • 可以将设计的表格打印出来
  • 可以将打印好的表格供人填写内容

使用对象组织数据

在程序中是可以做到和生活中那样设计表格,生产表格,填写表格的组织形式的

  1. 在程序中设计表格,我们称之为:设计类(class)

    class Student: name = None # 记录学生姓名 gender = None # 记录学生性别 nationality = None # 记录学生国籍 native_place = None # 记录学生籍贯 age = None # 记录学生年龄
  2. 在程序中打印生产表格,我们称之为:创建对象

    stu1 = Student()
  3. 在程序中填写表格,我们称之为:对象属性赋值

    stu1.name = "林俊杰" stu1.gender = "男"

类的成员方法

  • class 是关键字,表示要定义类了
  • 类的属性,即定义在类中的变量(成员变量)
  • 类的行为,即定义在类中的函数(成员方法)

成员变量和成员方法

在类中定义成员方法和定义函数基本一致,但仍有细微区别:

def 方法名(se1f,形参1,......,形参N):

可以看到,在方法定义的参数列表中,有一个:self 关键字
self 关键字是成员方法定义的时候,必须填写的。

  • 它用来表示类对象自身的意思
  • 当我们使用类对象调用方法的是,self 会自动被 python 传入
  • 在方法内部,想要访问类的成员变量,必须使用 self

注意事项:self 关键字,尽管在参数列表中,但是传参的时候可以忽略它

class Student: name = None def say_hi(self): print(f"大家好,我是{self.name}") stu = Student() stu.name = "周杰伦" stu.say_hi()

类和对象

现实世界的事物和类

    • 打扫卫生

      • 属性:何时何地、谁
      • 行为:扫地、拖地、擦桌子
      • 属性:身高、体重、品种
      • 行为:汪汪叫、吃东西、睡觉

使用程序中的类,可以完美的描述现实世界的事物

类和对象

基于类创建对象的语法: 对象名 = 类名称()

为什么非要创建对象才能使用呢?

类只是一种程序内的“设计图纸”,需要基于图纸生产实体(对象 ),才能正常工作
这种套路,称之为:面向对象编程

使用类和对象描述现实事物

设计图纸

  • 闹钟

    • 属性:序列号、价格

      class clock: id = None price = None
    • 行为:响铃

      def ring(self): import winsound winsound.Beep(2000, 3000)

生产实体

  • 编号 1

    clock1 = clock() clock1.id = "003032" clock1.price = 19.99 clock1.ring()
  • 编号 2

    clock2 = clock()

这就是面向对象编程:设计类,基于类创建对象,由对象做具体工作

构造方法

属性(成员变量)的赋值

思考:stu = student()

这个括号,能否像函数 (方法)那样,通过传参的形式对属性赋值呢?

可以,需要使用构造方法:__init__()

  • 在创建类对象(构造类)的时候,会自动执行
  • 在创建类对象(构造类)的时候,将传入参数自动传递给 __init__ ​​方法使用
class Student: name = None age = None tel = None def __init__(self, bame, age, tel): self.name = name self.age = age self.tel = tel stu = Student("周杰伦", 32, "18012056523")

注意事项

  • 千万不要忘记 init 前后都有 2 个下划线

  • 构造方法也是成员方法,不要忘记在参数列表中提供: self

    • 在构造方法内定义成员变量,需要使用 self 关键字

      def __init__(self, bame, age, tel): self.name = name self.age = age self.tel = tel

      这是因为:变量是定义在构造方法内部,如果要成为成员变量,需要用 self 来表示。

其它内置方法

魔术方法

上文学习的 __init__​构造方法,是 Python 类内置的方法之一。

这些内置的类方法,各自有各自特殊的功能,这些内置方法我们称之为:魔术方法

  • __init__​​构造方法

    class Student: name = None age = None tel = None def __init__(self, bame, age, tel): self.name = name self.age = age self.tel = tel stu = Student("周杰伦", 32, "18012056523")
  • __str__​​字符串方法
    当类对象需要被打印转换成字符串之时,会输出 <__main_.student object at 0x000002200CFD7040>​​,叫做内存地址
    但是内存地址没什么用

    class Student: def __init__(self, bame, age, tel): self.name = name self.age = age self.tel = tel stu = Student("周杰伦", 32, "18012056523") print(stu) # 结果:<__main_.student object at 0x000002200CFD7040> print(str(stu)) # 结果:<__main_.student object at 0x000002200CFD7040> # __str__魔术方法 class Student: def __init__(self, bame, age, tel): self.name = name self.age = age self.tel = tel def __str__(self): return f"Student类对象,name:{self.name}" stu = Student("周杰伦", 32, "18012056523") print(stu)
  • __lt__​​小于、大于符号比较

    class Student: def __init__(self, bame, age): self.name = name self.age = age stu1 = Student("周杰伦", 11) stu2 = Student("林俊杰", 13) print(stu1 < stu2) # 结果:报错 class Student: def __init__(self, bame, age): self.name = name self.age = age def __lt__(self, other): return self.age < other.age stu1 = Student("周杰伦", 11) stu2 = Student("林俊杰", 13) print(stu1 < stu2) # 结果:True
  • __le__​​小于等于、大于等于符号比较

    class Student: def __init__(self, bame, age): self.name = name self.age = age def __le__(self, other): return self.age <= other.age stu1 = Student("周杰伦", 11) stu2 = Student("林俊杰", 13) print(stu1 <= stu2) # 结果:True
  • __eq__​​==符号比较

    class Student: def __init__(self, bame, age): self.name = name self.age = age def __eq__(self, other): return self.age == other.age stu1 = Student("周杰伦", 11) stu2 = Student("林俊杰", 11) print(stu1 == stu2) # 结果:True

封装

面向对象编程,是许多编程语言都支持的一种编程思想。

简单理解是:基于模板(类)去创建实体(对象),使用对象完成功能开发。

面向对象包含 3 大主要特性:

  • 封装
  • 继承
  • 多态

封装

封装表示将现实世界事物的:属性、行为。封装到类中,描述为:成员变量、成员方法

对用户隐藏的属性和行为

现实世界中的事物,有属性和行为

但是不代表这些属性和行为都是开放给用户使用的。

  • 对用户开放的属性和行为

    • 序列号
    • 品牌、型号
    • 上网
    • 通话
  • 对用户隐藏的属性和行为

    • 运行电压
    • 驱动信息
    • 程序调度
    • 内存管理

比如苹果越狱,安卓 root,也是为了突破权限使用这些对用户隐藏的属性和行为

私有成员

既然现实事物有不公开的属性和行为,那么作为现实事物在程序中映射的类,也应该支持。

类中提供了私有成员的形式来支持。

  • 私有成员变量
  • 私有成员方法

定义私有成员的方式非常简单,只需要:

  • 私有成员变量:变量名以__开头 (2 个下划线)

    class phone: IMEI = None producer = None __current_voltage = None # 私有成员变量
  • 私有成员方法:方法名以__开头 (2 个下划线)

    class phone: IMEI = None producer = None __current_voltage = None # 私有成员变量 def call_by_sg(self): print("5g通话已经开启") def __keep_single_core(self): print("让CPU以单核模式运行以节省电量") # 私有成员方法 phone = phone() # 创建对象 phone.__current_voltage = 33 # 私有变量赋值 不报错但无效 print(phone.__current_voltage) # 获取私有变量值 报错 phone.__keep_single_core() # 获取私有方法 报错

使用私有成员

私有成员无法被类对象使用,但是可以被其它的成员使用

class phone: __current_voltage = 0.5 #当前手机运行电压 def __keep_single_core(self): print("让CPU以单核模式运行以节省电量") def call_by_sg(self): if self.__current_voltage >= 1: print("5g通话已经开启") else: self.__keep_single_core() print("电量不足,无法使用5g通话,并已设置为单核运行进行省电")

继承

如果你是设计师,你会如何选择?

  1. 每一代新款手机,都从零开始出设计图
  2. 基于老款的设计图,修修改改

单继承

class 类名(父类名):

class phone: IMEI = None producer = None def call_by_4g(self): print("4g通话")
class phone2023(phone): face_id = Tree # 面部识别 def call_by_5g(self): print("2023最新5g通话")

多继承

单继承是一个子类继承一个父类,多继承是一个子类继承多个父类

class 类名(父类1,父类2,父类3,......,父类N):

如果继承了多个父类,但是子类没有东西要写,可以换行加个 pass 让语法不产生错误

class MyPhone(Phone, NFCReader, RemoteControl): pass

注意事项:
多个父类中,如果有同名的成员,那么默认以继承顺序(从左到右)为优先级即:先继承的保留,后继承的被覆盖

复写和使用父类成员

子类继承父类的成员属性和成员方法后,如果对其“不满意”那么可以进行复写。即:在子类中重新定义同名的属性或方法即可

class phone: IMEI = None # 序列号 producer = "ITCAST" # 厂商 def call_by_5g(self): print("父类的5g通话") class Myphone(phone): producer = "ITHEIMA" # 复写父类属性 def call_by_5g(self): # 复写父类方法 print("子类的5g通话")

调用父类的同名成员

一旦复写父类成员,那么类对象调用成员的时候,就会调用复写后的新成员如果需要使用被复写的父类的成员,需要特殊的调用方式:

  • 方式 1:直接调用父类成员
    使用成员变量:父类名.成员变量
    使用成员方法:父类名.成员方法(self)
  • 方式 2:使用 super()​调用父类成员
    使用成员变量:super().成员变量
    使用成员方法:super().成员方法()

变量的类型注解

为什么内置模块 random 的方法可以提示类型,自己定义的就不可以?

因为 PyCharm 无法通过代码确定应传入什么类型

我们需要使用类型注解

类型注解

Python 在 3.5 版本的时候引入了类型注解,以方便静态类型检查工具,IDE 等第三方工具类型注解:在代码中涉及数据交互的地方,提供数据类型的注解(显式的说明)

主要功能:

  • 帮助第三方 IDE 工具(如 PyCharm)对代码进行类型推断,协助做代码提示
  • 帮助开发者自身对变量进行类型注释

支持:

  • 变量的类型注解
  • 函数(方法)形参列表和返回值的类型注解

类型注解的语法

  • 为变量设置类型注解
    语法:变量:类型

    • 基础数据类型注解

      var_1: int = 10 var_2: float = 3.14 var_3: bool = True var_4: str = "itheima"
    • 类对象类型注解

      class student: pass stu: student = student()
    • 基础容器类型注解

      my_list: list = [1, 2, 3] my_tuple: tuple = (1, 2, 3) my_set: set = {1, 2, 3} my_dict: dict = {"itheima": 666} my_ str = "itheima"
    • 容器类型详细注解

      my_list: list[int] = [1, 2, 3] my_tuple: tuple[str, int, bool] = ("itheima", 666, True) my_set: set[int] = {1, 2, 3} my_dict: dict[str, int] = {"itheima": 666}

      注意:

      • 元组类型设置类型详细注解,需要将每一个元素都标记出来
      • 字典类型设置类型详细注解,需要 2 个类型,第一个是 key 第二个是 value
    • 在注释中进行类型注解
      语法:# type: 类型

      class student: pass var_1 = random.randint(1, 10) # type: int var_2 = json.loads(date) # type: dict[str, int] var_3 = func() # type: student
  • 函数和方法类型注解
    语法:def 函数方法名(形参名:类型,形参名:类型......):

    • 行参类型注解

      def add(x: int, y: int): return x + y def func(data: list): pass
    • 返回值注解
      语法:def 函数方法名(形参名:类型,形参名:类型......) -> 返回值类型:

      def func(data: list) -> list: return data
  • Union 联合类型注解

    # 先导包 from typing import Union my_list: list[Union[int, str]] = [1, 2, "itheima"] def func(data: Union[int, str]) -> Union[int, str]: pass

一般,无法直接看出变量类型的时候会添加变量的类型注解
而且类型注解只是起到提示作用,不会改变类型

多态

多态,指的是:多种状态,即完成某个行为时,使用不同的对象会得到不同的状态如何理解?

class Animal: def speak(self): pass class Dog(Animal): def speak(self): print("汪汪汪") class Cat(Animal): def speak(self): print("喵喵喵") def make_noise(animal: Animal): animal.speak() dog = Dog() cat = Cat() make_noise(dog) # 结果:汪汪汪 make_noise(cat) # 结果:喵喵喵

抽象类(接口)

父类 Animal 的 speak 方法,是空实现

class Animal: def speak(self): pass class Dog(Animal): def speak(self): print("汪汪汪") class Cat(Animal): def speak(self): print("喵喵喵")

这种设计的含义是:

  • 父类用来确定有哪些方法
  • 具体的方法实现,由子类自行决定

这种写法,就叫做抽象类(也可以称之为接口)

抽象类:含有抽象方法的类称之为抽象类
抽象方法:方法体是空实现的 (pass)称之为抽象方法

  • Python

    Python 是一种面向对象、直译式电脑编程语言,具有近二十年的发展历史,成熟且稳定。它包含了一组完善而且容易理解的标准库,能够轻松完成很多常见的任务。它的语法简捷和清晰,尽量使用无异义的英语单词,与其它大多数程序设计语言使用大括号不一样,它使用缩进来定义语句块。

    556 引用 • 674 回帖
1 操作
ZanMei 在 2023-10-31 22:41:17 更新了该帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...