python核心语法

B站黑马程序员笔记

数据存储与运算

字面量与变量

字面量——怎么写

程序中,直接书写的固定值(数据),就称为字面量

字面量的种类及其书写格式

  • 数字类型
    • 整数(int)
    • 浮点数/小数(float)
  • 布尔(bool)
    • 表示现实生活中的逻辑,真或假 True False(首字母大写)
    • 本质上是数字类型,在涉及到数字运算时,会自动将True转为1,False转为0
  • 字符串(str)
    • 描述文本的一种数据类型
  • 空值(None Type)
    • 表示空或无值,仅包含一个值None
  • 数据容器
    • 存储多项数据的容器(列表/元组/集合/字典)
变量——怎么存

定义:程序中用来存储单个数据的容器,通常会把经常发生变化的数据存储在变量中。

注意:变量是指存储数据的容器(空间),而不是容器里面存储的数据

定义格式:变量名 = 变量的值

Python是动态类型语言,在程序运行时才进行类型检查,变量的类型可以在程序运行过程中改变(一个变量可以接收不同类型的值),但是在项目开发中,推荐变量只存储一种类型的数据

注意事项:

  • 一个变量只能存储一个值
  • 变量定义的时候必须赋值才可以使用
  • 一条语句可以定义多个变量,也可以连续赋值

标识符

程序员在代码中为变量、函数、类等元素所起的名字

命名规则(规定):(必须遵守的)

  • 只能包含字母,数字,下划线
  • 不能以数字开头
  • 不能使用关键字:True、False、None、and、or、if、else、elif、for、while等
  • 严格区分大小写,比如:age,Age,AGE是三个变量

命名规范(变量)

  • 见名知意
  • 多个部分使用下划线连接
  • 英文字母全小写

PEP8:PEP是Python社区的核心技术文档和标准化机制,而PEP8是Python的代码风格指南(https://peps.python.org/pep-0008

常见数据类型

基础数据类型

整数(int),浮点数(float),字符串(str),布尔(bool),空值(NoneType)

判定数据类型
  1. 通过type()数据类得到数据的类型,type(要查看类型的数据)
    说明:变量本身是没有类型的,type(变量)输出的类型是变量中存储的数据的类型

  2. 通过isinstance()检查数据是否属于指定的类型,返回的是一个bool值,isinstance(数据,类型)

字符串
  1. 定义方式:双引号定义,单引号定义(不能直接换行),三引号定义(双(官方推荐),多行字符串)

  2. 字符:文本世界的基本单位,一个字母、一个数字、一个标点符号、一个汉字等都是一个字符

  3. 常见的转义字符:(字符串内的引号不能正确表示时)
    \’ ,单引号
    \" ,双引号
    \n ,换行符
    \t ,制表符,缩进一个制表符(tab)的大小

  4. 字符串拼接:
    直接使用(+)进行拼接(多个字符串字面量直接写)(拼接繁琐,破坏字符串完整性,类型转换)

注意:+号可以用来拼接两个字符串,但是无法将非字符串与字符串进行拼接(非字符串需要转换为字符串类型)

str(int数字):将int类型的数字转为字符串

  1. 字符串格式化:(优化加号拼接)

5.1. 通过%占位符的形式完成字符串和变量的快速拼接(其中%表示我要占位,s表示将变量转为字符串放入占位的位置)

print("大家好,我是%s" % s1)
print("大家好,我是%s,%s" % (s1,s2))

注意:前面有多少个占位符(%s),后面就需要有多少个变量(或数据),前后数量需要一致,且顺序决定占位顺序

5.2.也可以通过 f"内容{变量/表达式}" 的形式来完成快速格式化(推荐)

print(f"大家好,我是{name}")
print(f"大家好,我是{s1}{s2}")

输入与输出

  • input语句(函数)的功能就是获取键盘输入的数据,具体的用法为:s = input(提示信息)

  • print语句(函数)的功能就是将数据输出到控制台,具体语法为:print(数据…)

注意:无论键盘输入什么类型的数据,获得到的数据类型永远都是字符串类型

int(…) 字符串转为int类型,类似有str(),float(),bool()

运算符

算术运算符

用于执行基本的数学运算的符号,作用于一个或多个操作数上,并产生一个计算结果

包括:+加法 -减法 *乘法 /除法(运算结果是一个小数) //整除 %取余/求模 **幂指数

优先级:括号,幂指数,乘法除法整除求模,加减

涉及到浮点数的算术运算,可能会损失精度:由于计算机底层是基于二进制进行数据的存储和处理的,而二进制是无法精确表示所有的小数的

赋值运算符

编程语言中用于将值(或表达式的结果)保存到变量中的运算符(把右边的值,赋给左边的变量)

包括:
= 赋值运算符
+=加法赋值运算符(中间不要加空格,是一个整体的符号) num +=2 等效于 num = num + 2
-=,*=,/=,%=,//=,**=同理

浮点值涉及计算,结果就是一个浮点数

比较运算符

也称关系运算符,用于比较两个值之间的关系。会计算运算符两边的表达式,然后返回一个布尔值作为结果

==等于,!=不等于,>大于,>=大于等于,<小于,<=小于等于

逻辑运算符

用于连接多个条件(布尔)表达式(其值为“真”或“假”),并返回一个最终布尔结果的运算符

and 逻辑与(并且),or 逻辑或(或者),not 逻辑非(取反)

数据的逻辑处理

条件判断

if条件判断

场景:只有满足指定条件,才会执行对应的代码逻辑

基本格式:
if 要判断的条件1:(判断语句必须为布尔类型,不要忘记冒号)
         ~~~~~~~~         条件成立时,要执行对应的操作1(必须缩进)
elif 要判断的条件2:
         ~~~~~~~~         条件成立时,执行对应的操作2(elif可以有多个)
else:
         ~~~~~~~~         条件不成立时,执行操作3(可有可无,若有必须放在最后)

注意:Python中是通过缩进来描述代码的归属的,归属于if代码块的语句,需要在前方缩进4个空格(按Tab键Pycharm会自动转为空格)。多个条件依次判断,接收即中止

pass空语句,什么都不做,保持代码的完整性,起到语法占位的作用

模式匹配match…case

在if语句中涉及大量判断,重复且繁琐

结构模式匹配就是用一个清晰的模板去精准的匹配数据的结构和内容,匹配成功则执行相应的操作

在这里插入图片描述

执行流程:

  1. 首先计算match指定的表达式的值
  2. 从上到下依次和case后i按的值进行匹配,匹配正确,就执行对应语法
  3. 如果前面所有的case都没有匹配上,就会执行默认的case_

| 表示或的关系,匹配多种模式中的任意一个

在这里插入图片描述

循环

while

语法结构:

while 条件表达式:
        ~~~~~~~        循环体语句1
        ~~~~~~~        循环体语句2
else:
        ~~~~~~~        条件为False,循环正常结束时执行

注意:while循环是通过条件表达式来控制是否要进行下一次循环

for

while循环是通过条件表达式来控制是否要进行下一次循环的。用于在某个条件满足时一直循环,循环的次数通常是未知的,只知道循环开始/结束的条件。(关注的是循环的条件)

而for循环,本质上是一种轮询遍历机制,对一批内容进行逐个处理。用于对一个已知的数据集进行遍历或已知次数的循环。(关注的是遍历每一个元素)

语法结构:

for 元素 in 待处理数据集:
        ~~~~~~~        循环体代码(对元素进行处理)
else:
        ~~~~~~~        循环结束时,执行的代码

range语句:

作用:生成指定规则的数字序列

用法1:range(end)获取一个从0开始,到end结束的数字序列(不含end本身)

用法2:range(start,end)获取一个从start开始,到end结束的数字序列(不含end本身)

用法3:range(start,end,step)获取一个从start开始,到end结束的数字序列,step步长(不含end本身)

嵌套循环

语法结构:
for 元素 in 待处理数据集1:
        ~~~~~~~        循环体的代码1
        ~~~~~~~        循环体的代码2
        ~~~~~~~        
        ~~~~~~~        for 元素 in 待处理数据集2:
               ~~~~~~~~~~~~~~               循环体的代码1
               ~~~~~~~~~~~~~~               循环体的代码2
               ~~~~~~~~~~~~~~               
        ~~~~~~~        

print自带换行效果print(“*”,end=" "),end表示每次输出以什么结束,默认换行

print()换行

关键字:

break 只能出现在循环中,表示结束,跳出循环的含义(break跳出循环时,while后面的else中的代码不会执行)

continue 只能出现在循环中,表示中断本次循环,直接进入下一次循环

随机数生成:
import random
random_number = random.randint(1,100)

数据存储容器

一种可以容纳多份数据的数据类型(容器),容纳的每一份数据称之为1个元素,每个元素都可以是任意类型的数据,如字符串,数字,布尔等

根据是否支持重复元素,是否可以修改,是否有序分为:

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

列表

  1. 列表是数据容器中的一类,是一次性可以存储多个数据(元素)的
  2. 定义:列表名称 = [元素1,元素2,元素3,元素4,元素5…]
  3. 特点:
    • 可以存储不同类型的元素
    • 元素有序,可以重复,元素可以修改

注意:从前向后(正向索引),下标从0开始s[1];从后往前(反向索引),下标从-1开始s[-6]

序列类型:容器中的元素按特定顺序排列的,可通过索引访问的容器类型称之为序列

修改:s[5] = 赋值即可

删除:del s[6]

切片:对操作的数据截取其中一部分的操作。列表,字符串,元组都支持切片操作

  • 语法:序列数据[开始索引:结束索引:步长]
    • 不包含结束索引位置对应的元素(开始索引未指定默认为0,结束索引未指定默认为列表长度(直到列表末尾);步长未指定默认为1)
    • 索引采用正向反向都可以
    • 步长是选取间隔,默认是1

print(s[0:5:1])或s[:5]或s[:5:]或s[:5:1]

常用方法(物品具有的能力或功能(列表内置的常见功能),方法归属于对象):

(1) append()在列表的尾部追加元素——a.append()

(2) insert()在指定索引之前,插入元素——a.insert(0,92)

(3) remove()移除列表中第一个匹配到的值——a.remove(75)

(4) pop()删除列表中指定索引位置的元素(如果未指定索引,默认删除最后一个)——a.pop()/a.pop()可接收

(5) sort()对列表进行排序(列表元素的数据类型一致,才可以进行排序)——a.sort(),如果事先没有转为数,排序规则为字典序,而不是比较数值大小

(6) reverse()反转列表元素——a.reverse()

sum()求和,对列表中的元素累加

len()获取元素的个数(列表的长度)

判断是否在列表中 num not in num_list,返回布尔值

合并代码:直接+可以合并;(解包:把列表这一类容器解开成一个一个独立的元素)定义新列表num_list=[*num_list1,*num_list2];把其中一个列表循环取出元素。添加到另一个列表中

对应有(组包:将多个值合并到一个容器)

列表推导式:就是按照一定规则快速生成一个列表的方法。[要插入的值 for i in 序列/列表 (if 条件)]

num_list = [i**2 for i in range(1,21)]

字符串str

介绍:字符的容器,一个字符串中可以存放任意数量的字符

特点:不可变性(无法修改,不能赋值修改),有序性(字符串中的每一个字符元素都有对应的下标,通过元素对应的索引,可以获得相应的元素,同列表),可迭代性(遍历用in)

基本操作: 切片同列表

常用方法:

  1. find() 在字符串中查找子串,返回第一次出现的索引位置,找不到返回1,a.find(“Python”)
  2. count() 统计子串在字符串中出现的次数,返回次数,a.count(‘H’)
  3. upper() 将字符串中的所有字母转换为大写,赋值给新字符串,a.upper()
  4. lower() 将字符串中的所有字母转换为小写,a.lower()
  5. split() 将字符串按指定分隔符分割成列表,a.split(’ ')
  6. strip() 去除字符串两端的空格字符或指定字符,a.strip( )
  7. replace() 将字符串中的指定子串替换为新子串,返回新字符串,a.replace(‘H’,‘C’)
  8. startswith() 检查字符串是否以指定子串开头,返回布尔值,a.startswith(‘p’)
  9. endswith() 检查字符串是否以指定子串结尾,返回布尔值,

注意:原始字符串不变

案例tip:

in 用于判断子串是否存在字符串中,返回bool值

元组tuple

元组与列表最大的不同点:元组一旦定义完成,不可修改

介绍:不可变序列,类似于列表,但创建后不能修改

特点:可以存储不同类型的元素;元素可以重复,有序,不可以修改(支持索引访问,切片)

定义:元组名称 = (元素1,元素2,…)

定义空元组:元组名称=();元组名称 = tuple()

方法:

  • count():统计某元素在元组中出现的次数
  • index():查找某个元素在元组中的索引位置(第一次出现的位置)

定义单个元素的元组:t1 = (100,)必须加逗号

组包与解包:

组包(Packing):将多个值合并到一个容器(元组,列表)中
解包(Unpacking):将容器(元组,列表)解开成独立的元素,分别赋值给多个变量

注意:在元组解包时,*表示收集剩余所有元素,允许我们处理不确定数量的元素(生成列表,以便于可以进行进一步的处理)

在这里插入图片描述

基础解包,数量要一致,不然报错

a,b =b,a # 交换两个值,同时进行组包和解包

{avg:.1f}保留1位小数

shift+Tab往前走

# 根据提供的学生成绩单,完成如下需求:
students = (("S001","王林",85,92,78),
            ("S002","李慕婉",92,45,78),
            ("S003","十三",85,96,78),
            ("S004","曾牛",85,92,68),
            ("S005","周铁",78,92,78),
            ("S006","王卓",85,67,78),
            ("S007","红蝶",77,92,56),
            ("S008","徐立国",78,92,78),
            ("S009","许木",47,98,59),
            ("S010","通天",85,45,78)
)
# 1.计算每个学生的总分,各科平均分,然后一并输出出来
print("学号\t\t姓名\t\t语文\t\t数学\t\t英语\t\t总分\t\t平均分")
# 方法一
#
# for s in students:
#     total = s[2]+s[3]+s[4]
#     avg = total/3
#     print(f"{s[0]} \t {s[1]} \t {s[2]} \t {s[3]} \t {s[4]} \t {total} \t {avg:.1f}")

# 方法二
for id,name,chinese,math,english in students:
    total = chinese+math+english
    avg = total/3
    print(f"{id} \t {name} \t {chinese} \t {math} \t {english} \t {total} \t {avg:.1f}")
print()
# 2.统计各科成绩的最低分,最高分,平均分,并输出
# 2.1获取各科成绩列表
chinese_scores = [s[2] for s in students]
math_scores = [s[3] for s in students]
english_scores = [s[4] for s in students]

# 2.2统计
print(f"语文的最低分 {min(chinese_scores)},最高分 {max(chinese_scores)},平均分 {sum(chinese_scores)/len(chinese_scores)}")
print(f"数学的最低分 {min(math_scores)},最高分 {max(math_scores)},平均分 {sum(math_scores)/len(math_scores)}")
print(f"英语的最低分 {min(english_scores)},最高分 {max(english_scores)},平均分 {sum(english_scores)/len(english_scores)}")
# 3.查找成绩优秀(平均分大于90)的学生,并输出
print()
print("优秀学生名单如下")
# for s in students:
#     total = s[2]+s[3]+s[4]
#     avg = total/3
#     if avg > 90:
#         print(f"学号:{s[0]},姓名:{s[1]},平均分:{avg:.1f}")
for id,name,chinese,math,english in students:
    total = chinese+math+english
    avg = total/3
    if avg > 90:
        print(f"学号:{id},姓名:{name},平均分:{avg:.1f}")

集合set

无序,不可重复,可修改的数据容器

定义:{},逗号分割元素,空集合 set()

注意:空集合的定义不可以使用{},{}表示的是空字典,由于集合是无序的,因此是不支持下标索引访问的

常见方法:

  1. 添加元素到集合中s.add()
  2. 移除集合中的指定元素(指定元素不存在将报错)s.remove()
  3. 随机删除集合中的元素并返回s.pop()
  4. 清空集合s.clear()
  5. 求取两个集合的差集(包含在第一个集合但不包含在第二个集合的元素)s.difference(),或用-号,或集合推导式(快速构建集合,语法:{要往集合中添加的数据 for s in set1 if 条件})
  6. 求取两个集合的并集s.union(),或用|,可连续取并
  7. 求取两个集合的交集s.intersection(),或用&

案例分析:

统计每个学生选修的课程数量

all_set = football_set | basketball_set | french_set | art_set
all_list = [*football_set,*basketball_set,*french_set,*art_set]
for s in all_set:
		print(f"{s}选修了{all_list.count(s)}课程")

字典dict/哈希表hash/映射map

存储的是键值对(key:value)类型的数据,每一个键都对应一个值,可以根据键(key)找到对应的值(value)

特点:键值对(key:value)存储,键(key)不能重复,不可修改(str,int,float,tuple)

注意:字典(dict)中的value可以是任何类型的数据,而key不能位可变类型(如:不能为列表list,集合set,字典dict)

定义:字典名称 = {key:value,key:value,key:value …}

定义空字典:字典名称 = {},字典名称 = dict()

根据key获取value:值 = 字典名称[key]

常用方法:

  1. 添加:往指定字典中添加key-value键值对          ~~~~~~~~         字典名称[key]=value

  2. 删除:
             ~~~~~~~~         删除字典中指定的key,并返回该key对应的value          ~~~~~~~~          字典名称.pop(key)
             ~~~~~~~~         删除字典中指定的键值对          ~~~~~~~~         del 字典名称[key]

  3. 修改:修改字典中指定的key对应的值          ~~~~~~~~         字典名称[key]=value

  4. 查询:
             ~~~~~~~~         根据key获取value                       ~~~~~~~~~~~~~~~~~~~~~                       字典名称.get(key)
             ~~~~~~~~         根据key获取value                       ~~~~~~~~~~~~~~~~~~~~~                       字典名称[key]
             ~~~~~~~~         获取所有的key                            ~~~~~~~~~~~~~~~~~~~~~~~~~~                            字典名称.keys()
             ~~~~~~~~         获取所有的value                         ~~~~~~~~~~~~~~~~~~~~~~~                         字典名称.values()
             ~~~~~~~~         获取所有的key-value键值对         ~~~~~~~         字典名称.items()

  5. 遍历:

for k in dict1.keys(): # 遍历键
		print(f"{k} : {dict1[k]}")
for item in dict1.items(): # 遍历键值对
		print(f"{intem[0]} : {item[1]}")
for k,v in dict.items():   # 解包
		print(f"{k} : {v}")

Alt+Shift 列操作

# 案例:
# 开发一个购物车管理系统,实现商品信息的添加、修改、删除、查询和统计功能。系统使用嵌套字典结构存储商品数据,通过控制台菜单与用户交互。
# 具体功能如下:
# 1,添加购物车:用户根据提示录入商品名称、以及该商品的价格、数量,保存该商品信息到购物车。
# 2,修改购物车:要求用户输入要修改的购物车商品名称,然后再提示输入该商品的价格、数量,输入完成后修改该商品信息。
# 3,删除购物车:要求用户输入要删除的购物车名称,根据名称删除购物车中的商品。
# 4,查询购物车:将购物车中的商品信息展示出来,格式为:“商品名称:xX×,商品价格:xXX,商品数量:xxx”。
# 5.退出购物车
# 结构:shopping_cart ={"Meta80": {"price": 6999, "num": 2},“鼠标”:{..}}

shopping_cart = {}
menu = """
########### 购物车系统 ###########
#         1.添加购物车           #
#         2.修改购物车           #
#         3.删除购物车           #
#         4.查询购物车           #
#         5.退出购物车           #
################################
"""

# 1.制作菜单
print("欢迎使用购物车管理系统")
while True:
    print(menu)

    # 2.要执行的操作
    choice = input("请选择要执行的操作(1-5):")

    match choice:
        case "1":  # 添加购物车
            goods_name = input("请输入商品名称:")
            goods_price = float(input("请输入商品价格:"))
            goods_num = int(input("请输入商品数量:"))

            # 如果商品存在,则不执行添加
            if goods_name in shopping_cart:
                print("该商品已存在,请重新选择")
            else:
                shopping_cart[goods_name] = {"price": goods_price, "num": goods_num}
                print("商品添加完毕")
        case "2":  # 修改购物车
            goods_name = input("请输入要修改的商品名称:")
            goods_price = float(input("请输入商品最新的价格:"))
            goods_num = int(input("请输入商品最新的数量"))
            if goods_name not in shopping_cart:
                print("该商品不存在,请重新选择")
                continue #没有的时候就退出不再执行后续的
            shopping_cart[goods_name] = {"price": goods_price, "num": goods_num}
            print("商品修改完毕")
        case "3":  # 删除购物车
            goods_name = input("请输入要删除的商品名称:")
            if goods_name not in shopping_cart:
                print("该商品不存在,请重新选择")
            else:
                del shopping_cart[goods_name]
                print("商品删除完毕")
        case "4":  # 查询购物车
            for goods_name in shopping_cart.keys():
                goods_info = shopping_cart[goods_name]
                print(f"{goods_name},商品价格:{goods_info['price']},商品数量:{goods_info['num']}")
        case "5":  # 退出购物车
            print("拜拜")
            break
        case _:  # 匹配其他所有情况
            print("非法操作,不支持")

总结

在这里插入图片描述

函数

函数基础

函数定义

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

例子:input(),print(),max(),min(),len(),sum():Python的内置函数

语法:

def 函数名(参数列表):
		函数体
		……
		return 返回值
# 调用函数
函数名(参数)

注意:函数定义时的参数列表与返回值语句是可有可无的(由需求确定),函数必须先定义后调用执行(函数定义时,内部代码并未执行,在调用时才执行),函数中通过缩进来描述归属关系

函数的参数和返回值:

# 计算圆的面积
def circle_area(r): # 函数定义时如果有多个参数,多个参数之间使用逗号分隔
				    # 形参(形式参数):函数定义时括号里的参数,只能在函数内使用(局部变量) 
		area = 3.14 * r * r
		# round(2*3.14*r,1)保留一位小数
		return area # 返回多个逗号分隔,多个返回值会封装到元组中
		            #return语句必须放在最后,不然,后面的语法不会执行
					# return语句只有返回功能,而没有输出打印的功能,如果要输出,需要结合print()函数来实现

# 调用函数
c_area = cirle_area(10) # 实参(实际参数):函数在实际调用时传入的参数,要与形参顺序一致
print(c_area)
# 计算圆的面积和周长
def circle_area_len(r): # 形参的具体含义不清,不友好,难理解,难维护-----函数说明文档
		"""
        根据圆的半径,计算圆的面积和周长
		:param r:半径
		:return:圆的面积
		"""
		return round(3.14 * r * r,1),round(2 * 3.14 * r,1)

al = circle_area_len(10) # 元组
print(al)

area,len = circle_area_len(10) # 解包
print(area)
print(len)

函数说明文档

函数说明文档(Docstring)是写在函数开头,用三个引号包裹的字符串,用于解释函数的功能,参数,返回值等信息,方便调用者清楚函数的具体作用及细节

查看函数的说明文档:使用help函数,比如:help(cirlcle_area_len),或鼠标悬浮在函数上自动展示(推荐)—好的文档,能让你的代码更容易理解、使用和维护

在这里插入图片描述

函数的嵌套调用

在一个函数中,又调用了另外一个函数

函数调用遵循栈结构,最后被调用的函数最先返回LIFO(Last In First Out,后进先出)

案例
# 1.定义一个函数:根据传入的底和高计算三角形面积的函数
def triangle_area(b,h):
    """
    根据传入的低和高计算三角形面积
    :param b: 底
    :param h: 高
    :return: 三角形面积
    """
    return (b*h)/2

print("底为3,高为5的三角形面积:",triangle_area(3,5))

# 2.定义一个函数,计算传入字符串中元音字母的个数
def count_aeiou(s):
    """
    统计字符串中元音字母的个数
    :param s: 字符串
    :return: 元音字母的个数
    """
    num = 0
    for w in s:
        if w in "aeiouAEIOU":
            num += 1
    return num

print(count_aeiou("hello"))

# 3.定义一个函数:计算传入的班级学员高考成绩列表中成绩的最高分,最低分,平均分(保留一位小数),并返回

def calc_score(score_list):
    """
    计算传入的班级学员高考成绩列表中成绩的最高分,最低分,平均分
    :param score_list: 分数列表
    :return: 最高分,最低分,平均分
    """
    max_s = max(score_list)
    min_s = min(score_list)
    avg_s = round(sum(score_list)/len(score_list),1)
    return max_s, min_s, avg_s

s_list = [12,34,95,85,67,84,37,92,56,76,35]

max_s, min_s, avg_s = calc_score(s_list)
print("最高分:",max_s)
print("最低分:",min_s)
print("平均分:",avg_s)

函数进阶

函数变量的作用域

变量的作用范围(标记这个变量在哪里可以使用,在哪里不可以使用)

全局变量:在函数之外定义的变量,在整个文件中(包括函数内)都可以使用(通常定义在文件的顶部)

局部变量:在函数内部定义的变量,只能在该函数内部使用,外部无法访问(函数执行完毕后,会自动销毁其内部局部变量)

# 全局变量
num = 100
def circle_area(radius):
    pi = 3.14159
    area = pi * radius * radius
    # global num 先定义后使用,注意:加上这个会改变全局变量为1000
    num = 1000
    print(num) # 1000
    return area

c_area = circle_area(10)
print(c_area)
print(num) # 100

global关键字

用于明确gaosPython解释器,在函数中要使用全局变量,使得可以在函数内部修改全局变量的值(一定要先声明,再使用)

注意事项:

  1. 尽量避免在函数中使用全局变量,因为会使代码难以维护和调试
  2. 考虑使用函数参数和返回值来传递数据,而不是依赖全局变量
  3. global主要用在程序的状态、配置和计数器等场景中
函数参数详解
传参方式

在调用函数时,传递实参的方式(两种传参方式可混合使用,关键字参数必须在位置参数之后)

  1. 位置参数:调用函数时根据函数定义时的位置来传递参数(调用函数时参数顺序与定义函数时参数顺序一致)简洁,但可读性差,易出错,维护难—场景:参数少(不超过3个),且顺序自然
  2. 关键字参数:调用函数时以函数定义时形参名称作为关键字,以“键=值”的形式来传递参数(不要求顺序)可读性强,易维护和扩展,但代码繁琐—场景:参数较多,或易混淆的场景

黄金法则:半年后回头看你今天写的代码,能否一眼看出每个参数的含义,如果不能,就应该使用关键字参数

默认参数

也叫缺省参数,用于在定义函数时,为参数提供默认值,调用函数时,可以不传递有默认值的参数

默认参数必须放在没有默认值的参数列表的后面,一个函数在定义时是可以设置多个默认参数的

函数调用时,如果为默认参数传递了值,则会修改默认的参数值;如果没有传递该参数,则直接使用默认值

def reg_stu(name,age,gender="男",city="北京"):
    print(f"注册成功,姓名:{name},年龄:{age},性别:{gender},城市:{city}")
    return {"name":name,"age":age,"gender":gender,"city":city}
stu = reg_stu("王林",20)
print(stu)

stu = reg_stu("李三",18,"女")
print(stu)

stu = reg_stu("韩立",22,city = "上海")# 不能使用位置参数,不然会顺序赋值
print(stu)

不定长参数

也叫可变参数,用于函数定义及调用时参数个数不确定(0个或多个)的场景

类型:

  • 位置传递(*args)—处理数据不确定的选项
    • 传递的所有匹配的位置参数都会被args变量收集,这些参数会合并封装为一个元组,args是元组类型(注意并不会封装关键字参数)
    • args只是约定俗成的变量名,并不是关键字,这里可以使用任何合法的变量名(如*data)
  • 关键字传递(**kwargs)—处理数量不确定的选项(函数的配置参数,用来定制函数的行为)
    • 参数是以“键=值”形式传递的关键字参数,这些参数“键=值”都会被kwargs接受, 并合并封装为一个字典类型
    • kwargs只能是约定速成的变量名,并不是关键字,这里可以使用任何合法的变量名(如**options)
def calc_data(*args,**kwargs): # 先写位置参数,再写关键字参数
    """
    根据传入的这批数据,计算这批数据的最小值,最大值,平均值
    :param args: 不定长位置参数,需要计算的这批数据
    :param kwargs: 不定长关键字参数
        round:保留的小数位小数
        print:是否打印输出
    :return:
    """
    min_data = min(args)
    max_data = max(args)
    avg_data = sum(args) / len(args)

    if kwargs.get('round') is not None:
        avg_data = round(avg_data, kwargs.get('round'))
    if kwargs.get('print') :
        print(f"计算出来的最小值:{min_data},最大值:{max_data},平均值:{avg_data}")

    return min_data, max_data, avg_data

data = calc_data(1,2,3,4,5,6,7,8,9,10,round = 3,print=True)
print(data)
data = calc_data(1,3,5,7,9)
print(data)
参数类型

普通参数:数字,布尔,字符串,列表,元组,集合,字典等—传递的是实际要计算的数据

特殊参数:函数—传递的是函数中封装的逻辑

def add(x,y):
    return x+y
def calc(x,y,oper):
    return oper(x,y)

print(calc(3,4,add))


匿名函数

没有名称的函数,需要通过lambda表达式来声明函数,可以简化简单函数的编写(单行表达式)只适用于简单函数的编写,多行不适用

lambda 参数列表:函数体

前面定义的,命名函数:函数逻辑复杂,需要多步操作,需要多个地方重复使用或需要加文档说明的场景

代码的可读性和可维护性比简洁性更重要

注意:函数逻辑比较简单(单行表达式)且只在一个地方使用时,可以考虑使用匿名函数,简化书写(通常作为高阶函数的参数使用)。匿名函数中可以返回结果,也可以不返回结果,返回结果时,不需要写return,表达式的运行结果就是要返回的结果

# def add(a,b):
#     return a+b

add = lambda x,y: x+y # 自动返回,这里的add相当于函数
print(add(11,22))

data_list = ["c++","c","python","jack","go","javascript","rust"]
print(data_list)
data_list.sort() # 按字母的先后顺序排序
data_list.sort(key=lambda item:len(item)) # 按字符个数排序,匿名函数典型运用场景
# key作为函数,会获得每一个元素;reverse=False默认升序排序
print(data_list)
案例
N的阶乘
# 递归调用:在函数中自己调用自己,一定要有终结点
def jc(n):
    if n == 1:
        return 1
    else: 
        return n * jc(n - 1)

result = jc(5)
print(result) #ctrl D快速复制一行
# 执行中def会进栈多次,每次的n递减,依次计算顺序出栈
电商订单计算器
# 定义一个函数,用于根据传入的(批商品信息)(商品名、价格、数量)、优惠(优惠券、积分抵扣)、运费信息计算订单的总金额。
# 具体规则如下:
# 优惠券需要商品金额满5000才可以使用,且优惠券金额不能超过商品总价。
# 积分抵扣需要商品总金额满5000才可以使用,100积分抵扣1元(且抵扣金额不能超过商品总价,积分只能整百抵扣)

def calc_order_cost(*args,coupon=0,score=0,express=0.0):
    """
    根据传入的(批商品信息)(商品名、价格、数量)、优惠(优惠券、积分抵扣)、运费信息计算订单的总金额。
    :param args: 商品名、价格、数量,元组嵌套
    :param coupon: 优惠券
    :param score: 积分抵扣
    :param express: 运费信息
    :return: 订单总金额
    """
    # 订单总金额 = 商品总金额 - 优惠券 - 积分抵扣 + 运费
    # 1. 计算商品总金额
    total_price = [goods[1]*goods[2] for goods in args]
    total_cost = sum(total_price)
    # 2. 扣减优惠券
    if total_cost>=5000 and coupon <= total_cost:
        total_cost -= coupon
    # 3. 扣减积分抵扣
    if total_cost>=5000 and score//100 <= total_cost:
        total_cost -= score//100
    # 4. 添加运费
    total_cost += express
    return total_cost

total = calc_order_cost(("鼠标",188,2),("键盘",388,1),("手机",6999,1),coupon=10,score=4000,express=9.9)#必须用键值对传入
print(total)

类型注解

python中的一种语法特性,用于明确标识变量,函数参数和返回值的数据类型,从而使代码更清晰,更安全,更易维护

a: int = 695
score: float = 98.5
hobby: str = "Python"
flag: bool = False
pic: None = None
names: list[str | int] = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"] #列表中是字符串或整数
phones: set[str] = {"4567890","1234567"}
opthons: dict[str, int] = {"count":2,"total":10}
goods: tuple[str,int,int] = ("手机",6999,1)

类型推断:Python解释器自动推断出变量,表达式或函数返回值的数据类型的能力,而无需开发者显式声明—在对变量进行直接赋值,或者涉及到变量的运算,容器的推导等场景时,解释器会自动推导出变量的类型

类型注解只是起到语法提示作用,并不会影响程序运行的结果

类型注解优点:代码结构更清晰,代码逻辑更安全,易维护;更准确的代码自动提示;提前发现代码潜在错误

如果对变量直接赋值,变量运算等场景,python会自动进行类型推断

python是动态类型语言,添加的类型注解只是提示,并不是强制约束

函数-类型注释

为函数添加类型注解,主要就是为函数的参数和返回值添加类型注释

def calc1(score:list[int]) -> float: #给默认会推断,不默认是Any
    return sum(score)/len(score)

def calc2(score:list[int]) -> tuple[int,int,float]:
    max_value = max(score)
    min_value = min(score)
    avg = sum(score)/len(score)
    return max_value, min_value, avg
 # 对于*args:tuple[str,float,int]本身就是元组,只用说明里面的元素

不加类型注解,就是代码编写时看不出错,运行后才报错

模块

Python模块(module):一个.py文件就是一个模块,模块是python程序的基本组织单位。在模块中可以定义变量,函数,类,以及可执行的代码

自定义模块,math(数学计算),random(随机数),os(操作系统),sys(系统参数),datetime(日期时间),time(时间访问),re(正则),csv(csv文件操作)

优点:提高代码复用性,降低开发门槛,避免命名重复

导入模块

在使用模块中提供的功能之前,必须先导入,后使用

导入形式 代码样例 调用方式 调用方式
import 模块名 import random,os 模块名.功能名 random.randint(10,100)
import 模块名 as 别名 import random as rd 别名.功能名 rd.randint(10,100)
from 模块名 import 功能名 from random import randint,choice 功能名 randint(10,100)
from 模块名 import 功能名 as 别名 from random import randint as rint 别名 rint(10,100)
from 模块名 import * from random import * 功能名 randint(10,100)

导入模块语句会放在代码的最开始

自定义模块

当开发一些复杂的项目,为了让项目结构更清晰,更便于项目的维护管理及代码的复用,可能会把一个项目拆分为若干个模块

每一个python文件都可以作为一个模块,模块名字就是文件的名字(建议使用python标识符定义,规范命名)

# __all__是一个模块级别的特殊变量,用于指定from 模块名 import *时会导入哪些功能(*通配了哪些功能)
# __all__控制的是from 模块名 import *时,要导入的功能,并不会影响直接导入具体的功能(如from 模块名 import 功能)
# __all__ = ["log_separator1","log_separator3","PI"] 

# 常量(不会发生变化的数据,常量的名称为全部大写)
PI = 3.14159
name = "mushroom"

# 函数
def log_separator1():
    print("- " * 30) # "- "重复输出30次

def log_separator2():
    print("+ " * 30)

def log_separator3():
    print("# " * 30)

def log_separator4():
    print("* " * 30)

# 测试
# __name__:python中内置函数,表示的当前模块名字(直接运行当前模块,__name__的值为"__main__",当该模块被导入时,__name__的值为模块名)
# 测试只在该文件中运行,在调用后的文件中不执行
if __name__ == '__main__': # 快捷main
    log_separator1()
# crtl+y快速删除一行
import my_fun

my_fun.log_separator1()

print(my_fun.PI)

软件包(package)

包:本质就是一个文件夹,该文件夹(utils工具)中可以包含若干python模块(.py文件),文件夹下还包含一个__init__.py(描述当前包的信息的)

作用:模块文件较多时,用来管理多个模块(包的本质也是一个模块)

包的导入方式:

导入形式 代码样例 调用方式 调用方式
import 包名.模块名 import utils.my_fun 包名.模块名.功能名 utils.my_fun.log_separator1()
from 包名 import 模块名 from utils import my_fun 模块名.功能名 my_fun.log_separator1()
from 包名 import * from utils import * 模块名.功能名 my_fun.log_separator1()
from 包名.模块名 import 功能名 from utils.my_fun import log_separator1 功能名 log_separator1()
from 包名.模块名 import * ifrom utils.my_fun import * 功能名 log_separator1()

注意:在通过‘from 包名 import *’,导入全部模块的时候,需要在文件‘init.py’中添加’all.py’,控制允许导入的模块列表

# 在__init__.py中可以不写也可以
# 描述包信息
__version__ = "1.0.0"
__author__ = "cc"

__all__ = ["my_fun","my_var"] # 在from 包名 import *
# 导入模块中的功能
# 相对路径:从当前文件所在目录开始查找
from util.my_fun import log_separator1,log_separator3
# 绝对路径:从项目的根目录下开始查找
from 第二章.util.my_fun import log_separator1,log_separator3

面向对象基础

面向过程编程

核心思想:把一个需求分解成一系列要执行的步骤,然后按照步骤依次执行这些任务(关注的是流程,步骤)

适用场景:面向过程编程非常直接,适合简单,线性的任务(前面的编程都是面向过程)

面向对象编程

对象可以理解为现实中具体的人/物在程序中的数字化身(万物皆对象)

它把一个人/物的特征(属性)和功能(方法)打包到一起,是面向对象编程的基本单元(关注的是谁来帮我做这件事)

类与对象

类:描述的是一组具有相同属性(特征)和方法(功能/行为)的模板

月饼模板工具:属性(颜色,形状,大小,厚度)功能(提供能量,文化传承)

对象:对象是类的实例,是基于类创建出来的(实例对象)

提示:对象是由类创建出来的,创建对象的过程,也称为对象的实例化,一个类可以创建无数个对象

# 定义类
class 类名:
		pass

# 创建对象
对象名 = 类名()
对象名.属性名1 = 属性值1
对象名.属性名2 = 属性值2
class Car: # 类名的命名规范,遵循大驼峰命名法,每个单词的首字母都是大写,单词之间没有分隔符,比如:UserInfo,UesrAccount
    pass
# 创建对象
c1 = Car()
# 动态地为对象添加属性(不推荐),会降低代码的可读性可维护性
c1.color = "red"
c1.brand = "Mercedes"
c1.name = "as"
c1.price = 10

print(c1) # <__main__.Car object at 0x0000020466EA5400>内存地址,每次运行程序都会输出不同的地址
print(c1.__dict__) # __dict__是Python中用户自定义类实例的一个特殊属性,用于以字典形式存储对象的属性
# {'color': 'red', 'brand': 'Mercedes', 'name': 'as', 'price': 10}
print(c1.color) # red
# 定义类
class 类名:
		def __init__(self,参数列表)# 定义在类外面的称之为函数,定义在类中的函数称之为方法
		# __init__ :初始化方法,对象创建后自动调用,主要用于设置对象的初始状态(设置对象属性)
		# self: 方法的第一个参数,表示当前创建的实例对象
				self.属性名 = 参数值
				self.属性名 = 参数值
# 创建对象
对象名 = 类名(参数列表)
class Car:
    def __init__(self, brand, model, year):
        self.brand = brand
        self.model = model
        self.year = year
        print("初始化完成")

c1 = Car("bc","10",10)
print(c1.__dict__)

实例方法

在类中定义实例方法时,定义语法与之前学习的函数定义的方式是一致的

# 定义类
class 类名:
	def __init__(self,形参列表):
		self.属性名 = 参数值
		self.属性名 = 参数值
	def 方法名(self.形参列表):
		...
# 创建对象
对象名 = 类名(参数列表)
对象名.方法名(实参)
class  Car:
    def __init__(self, c_color,c_brand,c_name,c_price):
        self.c_color = c_color
        self.c_brand = c_brand
        self.c_name = c_name
        self.c_price = c_price
        print("Car类型的对象初始化完毕,对象属性已经添加完毕!")

    # 定义实例方法
    def running(self):
        print(f"{self.c_brand}{self.c_name}正在高速行驶中....")
    def total_cost(self,discount,rate=0.1):
        """
        计算提车的总费用,包括两个部分:车的价格,税费
        :param discount: 折扣
        :param rate: 税率
        :return: 提车的总费用
        """
        total_cost = discount * self.c_price + rate * self.c_price
        return total_cost

# 测试
c1 = Car("红色","BMW","X7",80000)

# 调用对象中的方法
c1.running()
total = c1.total_cost(0.9,0.1)
print("提车的总费用",total)

total2 = c1.total_cost(0.9)
print("提车的总费用",total2)

魔法方法

Python中提供的以下划线开头和结尾的特殊方法,用于定义类的特殊行为,比如:init

魔法方法不需要我们手动调用,python会在合适的时机自动调用

魔法方法 描述
init 初始化方法
str 字符串表示的方法
eq 此时两个对象是否相等(equal)
lt,le,gt,ge 支持比较两个对象的大小,小于,小于等于,大于,大于等于
class  Car:
    def __init__(self, c_color,c_brand,c_name,c_price):
        self.c_color = c_color
        self.c_brand = c_brand
        self.c_name = c_name
        self.c_price = c_price
        print("Car类型的对象初始化完毕,对象属性已经添加完毕!")

    # 定义实例方法
    def running(self):
        print(f"{self.c_brand}{self.c_name}正在高速行驶中....")
    def total_cost(self,discount,rate=0.1):
        total_cost = discount * self.c_price + rate * self.c_price
        return total_cost

    # 定义魔法方法
    def __str__(self):
        return f"{self.c_color} {self.c_brand} {self.c_name} {self.c_price}"
    def __eq__(self,other):
        return self.c_color == other.c_color and self.c_brand == other.c_brand
    def __lt__(self,other):
        return self.c_color < other.c_color and self.c_brand < other.c_brand

# 测试
c1 = Car("红色","BMW","X7",80000)
print(c1)

c2 = Car("红色","BMW","X7",80000)
print(c2)

print(c1 == c2)
print(c1 < c2)

实例属性和类属性

实例属性:属于每个具体对象的属性,每个对象都是独立的(各个对象特有的数据)(通过实例对象.属性的方式操作)

类属性:属于类本身的属性,所有实例共享的(所有对象共享的数据或配置)(通过类名.属性的方式操作)

说明:通过实例查找属性时,会先查找实例属性,实例属性不存在时,再查找类属性

class  Car:
    # 类属性
    wheel = 4 # 轮胎数量
    tax_rate = 0.1 # 购置税率
    def __init__(self, c_color,c_brand,c_name,c_price):
        # 实例属性
        self.c_color = c_color
        self.c_brand = c_brand
        self.c_name = c_name
        self.c_price = c_price
        self.wheel = 2

    # 定义实例方法
    def running(self):
        print(f"{self.c_brand}{self.c_name}正在高速行驶中....")
    def total_cost(self,discount,rate=0.1):
        total_cost = discount * self.c_price + rate * self.c_price
        return total_cost

# 测试
c1 = Car("红色","BMW","X7",80000)
print(c1.c_brand)
print(c1.wheel) # 通过实例对象查找属性时,先查找实例属性
# 通过类名访问
print(Car.wheel)

案例

# 采用面向对象的编程思想,完成教务管理系统的开发。教务管理系统可以管理在校学生的成绩信息,通过
# 控制台菜单与用户交互,具体的功能如下:
# 1. 添加学生成绩:根据输入的学生姓名、语文成绩、数学成绩、英语成绩,记录在系统中
# 2. 修改学生成绩:根据输入的学生姓名,修改对应的学生成绩
# 3. 删除学生成绩:根据输入的学生姓名,删除对应的学生成绩
# 4. 查询指定学生成绩:根据输入的学生姓名,查找对应的学生成绩,并输出
# 5. 展示全部学生成绩:展示出系统中所有学生的成绩

class Student:
    def __init__(self, name, chinese,math,english):
        self.name = name
        self.chinese = chinese
        self.math = math
        self.english = english
    def __str__(self):
        return f"姓名:{self.name}|语文: {self.chinese} |数学:{self.math} |英语:{self.english}| 总分:{self.chinese+self.math+self.english}"

    # 修改学生成绩
    def update_score(self,chinese = None,math = None,english = None): # 设置默认值,这样可以只传需要修改的
        if chinese is not None:
            self.chinese = chinese
        if math is not None:
            self.math = math
        if english is not None:
            self.english = english

# # 测试
# if __name__ == '__main__':
#     s1 = Student("王林",90,80,70)
#     print(s1)
#     s1.update_score(english = 90)
#     print(s1)

class EduManager:
    system_version = "1.0"
    system_name = "教务管理系统"
    def __init__(self):
        self.student_list = [] # 列表,记录在校学生的成绩

    # 添加学生的成绩
    def add_student(self):
        name = input("请输入学生姓名:")

        # 判断学生姓名是否存在,存在则不可添加
        for s in self.student_list:
            if s.name == name:
                print("该学生已经存在,添加失败")
                return

        chinese = int(input("请输入学生语文成绩:"))
        math = int(input("请输入学生数学成绩:"))
        english = int(input("请输入学生英语成绩:"))

        # 判断分数是否再0-100之间
        if 0<= chinese <= 100 and 0<= math <= 100 and 0<= english <= 100:
            stu = Student(name, chinese, math, english)
            self.student_list.append(stu)
            print("添加成功")
        else:
            print("不合法,各科成绩必须在0-100之间")

    # 修改学生的成绩
    def update_student(self):
        name = input("请输入学生姓名:")

        for s in self.student_list:
            if s.name == name:
                print(f"当前成绩:{s}")
                chinese = int(input("请输入修改后语文成绩:"))
                math = int(input("请输入修改后数学成绩:"))
                english = int(input("请输入修改后英语成绩:"))

                if 0 <= chinese <= 100 and 0 <= math <= 100 and 0 <= english <= 100:
                    s.update_score(chinese,math,english)
                    print("修改成功")
                    print(f"修改后的成绩:{s}")
                    return
                else:
                    print("不合法,各科成绩必须在0-100之间")
                    return
        print("未找到该学生,修改失败")

    # 删除学生的成绩
    def delete_student(self):
        name = input("请输入学生姓名:")

        for s in self.student_list:
            if s.name == name:
                self.student_list.remove(s)
                print("删除成功")
                return
        print("未找到该学生,删除失败")

    # 查询指定学生的成绩
    def query_student(self):
        name = input("请输入学生姓名:")
        for s in self.student_list:
            if s.name == name:
                print(f"学生信息:{s}")
                return
        print("未找到该学生,查询失败")
    # 显示全部学生成绩
    def list_student(self):
        for s in self.student_list:
            print(s)

    # 运行系统
    def run(self):
        print(f"欢迎使用教务管理系统 V{EduManager.system_version}")
        while True:
            print()
            print("# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #")
            print("# 1.添加学生  2.修改学生  3.删除学生  4.查询指定学生  5.查询所有学生  6.退出系统 #")
            print("# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #")

            choice = input("\n请选择要执行的操作,输入1-6:")
            try:
                match choice:
                    case "1":
                        self.add_student()
                    case "2":
                        self.update_student()
                    case "3":
                        self.delete_student()
                    case "4":
                        self.query_student()
                    case "5":
                        self.list_student()
                    case "6":
                        print("再见")
                        break
                    case _:
                        print("输入错误")
            except Exception as e:
                print("出错了",e)

# 测试
if __name__ == "__main__":
    edu_manager = EduManager()
    edu_manager.run()

异常

bug,程序运行过程中出现的错误,它会中断程序的正常执行流程

作用:

保证数据,逻辑的正确性,避免程序执行混乱

在开发阶段,尽量发现更多的问题,尽早解决问题,保障程序正常执行

NameError TypeError IndexError KeyError ValueError(异常不是坏东西,而是编写健壮程序的重要工具)

程序运行过程中出现异常,有两种处理方案:

  1. 不做处理:整个程序因为一个Bug,中断执行(之前编程的程序)
  2. 捕获异常:按照我们自己的处理方式,处理好异常,程序继续执行,而不是中止程序运行(编写程序时,做好预案,出现异常,按预案处理)
try:
		可能出现异常的业务代码1
		可能出现异常的业务代码2
		...
except [异常类型 as 变量名]: # Exception捕获所有异常(父类),省略也可但不要省略
		出现异常时的预案
finally
		不管是否出现错误,都会执行的代码 # 通常资源释放

# ---------------------------------------------------------------
try:
    print("================")
    # print(my_name)
    # print(1 / 0)
    print("aer",hello)
    print("================")
except NameError as e:
    print("名字不存在,请检查变量或函数名",e)
except ZeroDivisionError as e:
    print(e)
except Exception as e:
    print(e)
finally: # 可有可无
    print("资源释放")

异常传递

就是异常在函数调用中层层上报的过程,直到有人处理它,或者程序崩溃

def fun1():
    print("fun1 running")
    fun2()

def fun2():
    print("fun2 running")
    fun3()

def fun3():
    print("fun3 running")
    print(my_color)

if __name__ == "__main__": # 在程序入口捕获异常
    try:
        fun1()
    except Exception as e:
        print(e)

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐