Python编程笔记8函数

Python编程学习笔记,第8记:函数

         本章节,将学习:如何编写函数;如何传递实参;如何使用位置实参和关键字实参,以及如何接受任意数量的实参;显示输出的函数和返回值函数;如何将函数同列表、字典、if语句和while循环结合起来使用;如何将函数存储在被称为模块的独立文件中;函数编写指南……

目录

Python编程学习笔记,第8记:函数

# 1.定义函数

# 1.2向函数传递信息

# 1.3实参和形参

# 2传递实参几种方式

# 2.1位置实参

# 2.2关键字实参

# 2.3函数的默认值

# 2.4等效函数调用

# 2.5避免实参错误:实参个数不匹配、实参顺序不正确……

# 3返回值

# 3.1让实参变成可选的

# 3.2返回字典

# 3.3结合使用函数和while循环.

# 4传递列表

# 4.1在函数中修改列表

# 4.2禁止函数修改列表

# 5传递任意数量的实参

# 5.1结合使用位置实参和任意数量实参

# 5.2使用任意数量的关键字实参

# 6将函数存储在模块中

# 6.1导入整个模块

# 6.2导入特定函数

# 6.3使用as给函数指定别名

# 6.4使用as给模块指定别名

# 6.5导入模块中的所有函数

# 7函数编写指南

耐心之树,结黄金之果。


# 1.定义函数

# 1定义函数
# 一个简单的函数
def greet_user():  # 函数定义
    """显示简单的问候语"""   # 文档字符串注释,描述函数是做什么的.
    print("Hello!!!")


greet_user()  # 调用函数

输出结果为:

Hello!!!

  • # 从上面这个示例,可以看出:使用关键字def告诉Python要定义一个函数,并向Python指出了函数名,
  • # 还可能在括号内指出函数为完成其任务需要什么样的信息,最后定义以冒号结尾.
  • # def greet_user():后面所有缩进行构成了函数体.
  • # 文档字符串用三个引号括起,Python使用它们来生成有关程序中函数的文档.
  • # 函数调用让Python执行函数的代码.要调用函数,可依次指定函数名以及用括号括起的必要信息.

# 1.2向函数传递信息

# 1.1向函数传递信息
# 一个简单的函数
def greet_user(user_name):  # 函数定义
    """显示简单的问候语"""   # 文档字符串注释,描述函数是做什么的.
    print("Hello, " + user_name.title() + "!!! ")


greet_user('jesse')  # 调用函数

输出结果为:

Hello, Jesse!!!

# 1.3实参和形参

  • # 形参:在函数定义时,放在括号内的变量.(完成函数工作所需要的信息)

例如:def greet_user(user_name): 函数定义中的user_name即为形参.

  • # 实参:调用函数时,传递给函数的信息.

例如:greet_user('jesse') # 调用函数中的'jesse'即为实参.

  • # 在函数调用时,将要让函数使用的信息放在括号内.将实参传递给函数,其值将存储在形参中.

# 2传递实参几种方式

  • # 向函数传递参数的方式很多:可使用位置实参,要求实参的顺序与形参的顺序相同;
  • # 也可以使用关键字实参,其中每个实参都由变量名和值组成;还可以使用列表和字典.

# 2.1位置实参

  • # 在调用函数时,Python必须将函数调用中的每个实参都关联到函数定义中的一个形参中.
  • # 最简单的关联方式是基于实参的顺序.这种关联方式称为位置实参.
# 2.1位置实参
# 在调用函数时,Python必须将函数调用中的每个实参都关联到函数定义中的一个形参中.
# 最简单的关联方式是基于实参的顺序.这种关联方式称为位置实参.
def describe_pet(animal_type, pet_name):  # 定义函数
    """显示宠物的信息"""   # 文档字符串注释,描述函数是做什么的.
    print("\nI have a " + animal_type + ". ")
    print("My " + animal_type + "'s name is " + pet_name.title() + ". ")


describe_pet('hamster', 'harry')  # 调用函数
describe_pet('dog', 'willie')  # 调用函数

输出结果为:

I have a hamster.
My hamster's name is Harry.

I have a dog.
My dog's name is Willie.

  • # 可根据需要,调用函数多次.
  • # 在函数中,可根据实际需要使用任意数量的实参,Python将按顺序将函数调用中的实参关联到函数定义中的形参.
  • # 位置实参的顺序很重要!!!如果实参的顺序不正确,将导致意想不到的结果.

# 2.2关键字实参

  • # 关键字实参是传递给函数的名称-值对.
  • # 关键字实参让你无需考虑函数调用中实参的顺序,还清楚的指出了函数调用中各个值的用途.
# 2.2关键字实参
# 关键字实参是传递给函数的名称-值对.
# 关键字实参让你无需考虑函数调用中实参的顺序,还清楚的指出了函数调用中各个值的用途.
def describe_pet(animal_type, pet_name):  # 定义函数
    """显示宠物的信息"""   # 文档字符串注释,描述函数是做什么的.
    print("\nI have a " + animal_type + ". ")
    print("My " + animal_type + "'s name is " + pet_name.title() + ". ")


describe_pet(animal_type='hamster', pet_name='harry')  # 调用函数

输出结果为:

I have a hamster.
My hamster's name is Harry.

  • # 注意:使用关键字实参时,务必准确地指出函数定义中的形参名.
  • # 注意:使用关键字实参时,务必准确地指出函数定义中的形参名.
  • # 注意:使用关键字实参时,务必准确地指出函数定义中的形参名.

# 2.3函数的默认值

  • # 在编写函数时,可以给每个形参指定默认值.在调用函数中给形参提供了实参时,Python将使用指定的实参值;
  • # 否则,将使用形参的默认值.因此,给定形参默认值后,可在函数调用中省略相应的实参.
  • # 使用默认值可简化函数调用,还可以清楚的指出函数的典型用法.
  •  
  • # 注意:使用默认值时,在形参列表中必须先列出没有默认值的形参,再列出有默认值的形参.这让Python依然能够正确的解读位置实参.
  •  
# 2.3函数的默认值
# 在编写函数时,可以给每个形参指定默认值.在调用函数中给形参提供了实参时,Python将使用指定的实参值;
# 否则,将使用形参的默认值.因此,给定形参默认值后,可在函数调用中省略相应的实参.
# 使用默认值可简化函数调用,还可以清楚的指出函数的典型用法.
# 注意:使用默认值时,在形参列表中必须先列出没有默认值的形参,再列出有默认值的形参.这让Python依然能够正确的解读位置实参.
def describe_pet(pet_name, animal_type='dog'):  # 定义函数
    """显示宠物的信息"""   # 文档字符串注释,描述函数是做什么的.
    print("\nI have a " + animal_type + ". ")
    print("My " + animal_type + "'s name is " + pet_name.title() + ". ")


describe_pet(pet_name='harry')  # 调用函数

输出结果为:

I have a dog.
My dog's name is Harry.

  • # 注意:使用默认值时,在形参列表中必须先列出没有默认值的形参,再列出有默认值的形参.这让Python依然能够正确的解读位置实参.
  • # 注意:使用默认值时,在形参列表中必须先列出没有默认值的形参,再列出有默认值的形参.这让Python依然能够正确的解读位置实参.
  • # 注意:使用默认值时,在形参列表中必须先列出没有默认值的形参,再列出有默认值的形参.这让Python依然能够正确的解读位置实参.

# 2.4等效函数调用

  • # 2.4等效函数调用

  • # 鉴于可混合使用位置实参、关键字实参和默认值,通常有多种等效的函数调用方式.

  • # 无论使用哪种调用方式,只要函数能生成你希望的输出就行.
     

# 2.5避免实参错误:实参个数不匹配、实参顺序不正确……

# 3返回值

  • # 函数返回的值被称为返回值. 在函数中,可使用return语句将值返回到调用函数的代码行.
  • # 调用返回值函数时,需要提供一个变量,用于存储返回的值.
# 3返回值
# 函数返回的值被称为返回值.在函数中,可使用return语句将值返回到调用函数的代码行.
# 调用返回值函数时,需要提供一个变量,用于存储返回的值.
def get_formatted_name(first_name, last_name):
    """返回整洁的姓名"""
    full_name = first_name + ' ' + last_name
    return full_name.title()


musician = get_formatted_name('jimi', 'hendrix')
print(musician)

输出结果为:

Jimi Hendrix

# 3.1让实参变成可选的

  • # 有时候,需要让实参变成可选的,这样使用函数的人就只需要在必要时才提供额外的信息.
  • # 可使用默认值来让实参变成可选的.
# 3.1让实参变成可选的
# 有时候,需要让实参变成可选的,这样使用函数的人就只需要在必要时才提供额外的信息.
# 可使用默认值来让实参变成可选的.
def get_formatted_name(first_name, last_name, middle_name=''):
    """返回整洁的姓名"""
    if middle_name:  # 并不是所有人都有中间名.
        full_name = first_name + ' ' + middle_name + ' ' + last_name
    else:
        full_name = first_name + ' ' + last_name
    return full_name.title()


musician1 = get_formatted_name('jimi', 'hendrix')
print(musician1)
musician2 = get_formatted_name('john', 'lee', 'hooker')
print(musician2)

# 注意:使用默认值时,在形参列表中必须先列出没有默认值的形参,再列出有默认值的形参.这让Python依然能够正确的解读位置实参.

输出结果为:

Jimi Hendrix
John Hooker Lee

  • # 注意:使用默认值时,在形参列表中必须先列出没有默认值的形参,再列出有默认值的形参.这让Python依然能够正确的解读位置实参.

# 3.2返回字典

  • # 函数可返回任何类型的值,包括列表和字典等较复杂的数据结构.
# 3.2返回字典
# 函数可返回任何类型的值,包括列表和字典等较复杂的数据结构.
def build_person(first_name, last_name):
    """返回一个字典,其中包含有关一个人的信息"""
    person = {'first':first_name, 'last':last_name}
    return person


musician1 = build_person('jimi', 'hendrix')
print(musician1)

输出结果为:

{'first': 'jimi', 'last': 'hendrix'}

  • # 扩展上面的函数build_person
# 扩展上面的函数build_person
def build_person(first_name, last_name, age=''):
    """返回一个字典,其中包含有关一个人的信息"""
    person = {'first': first_name, 'last': last_name}
    if age:
        person['age'] = age
    return person


musician1 = build_person('jimi', 'hendrix', age=28)
print(musician1)

输出结果为:

{'first': 'jimi', 'last': 'hendrix', 'age': 28}

# 3.3结合使用函数和while循环.

# 3.3结合使用函数和while循环.
def get_formatted_name(first_name, last_name):
    """返回整洁的姓名"""
    full_name = first_name + ' ' + last_name
    return full_name.title()


while True:
    print("\nPlease tell me your name: ")
    print("(enter 'q' at any time to quit) ")

    f_name = input("First name: ")
    if f_name == 'q':
        break
    l_name = input("Last name: ")
    if l_name == 'q':
        break

    formatted_name = get_formatted_name(f_name, l_name)
    print("\nHello, " + formatted_name + "!! ")

输出结果为:

Please tell me your name:
(enter 'q' at any time to quit)
First name: eric
Last name: matthes

Hello, Eric Matthes!!

Please tell me your name:
(enter 'q' at any time to quit)
First name: q

# 4传递列表

  • # 将列表传递给函数后,函数就可以直接访问其中的内容.
# 4传递列表
# 将列表传递给函数后,函数就可以直接访问其中的内容.
def greet_users(names):
    """向列表中的每个用户都发出简单的问候"""
    for name in names:
        msg = "Hello, " + name.title() + "! "
        print(msg)


user_names = ['selene', 'John', 'lee']
greet_users(user_names)

输出结果为:

Hello, Selene!
Hello, John!
Hello, Lee!

# 4.1在函数中修改列表

  • # (a)将列表传递给函数后,函数可以对其进行修改.在函数中对列表所做的修改都是永久性的.
# 4.1在函数中修改列表
# (a)将列表传递给函数后,函数可以对其进行修改.在函数中对列表所做的修改都是永久性的.
# 创建一个列表,其中包含一些要打印的信息
unprinted_designs = ['iphone case', 'robot pendant', 'dodecahedron']
completed_models = []

# 模拟打印每个设计,直到没有未打印的设计为止.
# 打印每个设计后,将其移到列表completed_models中
while unprinted_designs:
    current_design = unprinted_designs.pop()  # 弹出/删除列表末尾的元素

    # 打印设计模型
    print("Printing model: " + current_design)
    completed_models.append(current_design)   # 添加元素到列表的末尾

# 显示打印好的所有设计模型
print("\nTHe following models have been printed: ")
for completed_model in completed_models:
    print(completed_model)

输出结果为:

Printing model: dodecahedron
Printing model: robot pendant
Printing model: iphone case

THe following models have been printed:
dodecahedron
robot pendant
iphone case

# (b)将列表传递给函数后,函数可以对其进行修改.在函数中对列表所做的修改都是永久性的.

# (b)将列表传递给函数后,函数可以对其进行修改.在函数中对列表所做的修改都是永久性的.
# 创建一个列表,其中包含一些要打印的信息
def print_models(unprinted_designs2, completed_models2):
    """
    模拟打印每个设计,直到没有未打印的设计为止.
    打印每个设计后,将其移到列表completed_models中
    """
    while unprinted_designs2:
        current_design2 = unprinted_designs2.pop()  # 弹出/删除列表末尾的元素

        # 打印设计模型
        print("Printing model: " + current_design2)
        completed_models2.append(current_design2)   # 添加元素到列表的末尾


def show_completed_models(completed_models2):
    """
    显示打印好的所有设计模型
    """
    print("\nTHe following models have been printed: ")
    for completed_model in completed_models2:
        print(completed_model)


unprinted_designs2 = ['iphone case', 'robot pendant', 'dodecahedron']
completed_models2 = []

print_models(unprinted_designs2, completed_models2)
show_completed_models(completed_models2)

输出结果为:

Printing model: dodecahedron
Printing model: robot pendant
Printing model: iphone case

THe following models have been printed:
dodecahedron
robot pendant
iphone case

# 对比上面(a)与(b)两个程序, 有什么优缺点:

# 使用函数效率更高,程序组织更为有序,主程序更容易理解,程序更容易扩展和维护.     # 每个函数都应只负责一项工作.

# 4.2禁止函数修改列表

  • # 4.2禁止函数修改列表
    # 有时候,需要在函数中使用列表,但是又不能让函数改变原始列表中的内容.这个时候,可以向函数传递列表的副本而不是原件
    # 要将副本传递给函数,可以这样做:function_name(list_name[:])
    
    # 可以用切片表示法[:]创建列表副本.
    print_models(unprinted_designs2[:], completed_models2)
    
    # 虽然向函数传递列表的副本可保留原始列表的内容,但除非有充分的理由需要传递副本,
    # 否则还是应该将原始列表传递给函数.避免花时间的内存创建副本,从而能提高效率,处理大型列表时尤其如此.

# 5传递任意数量的实参

  • # 有时候,预先不知道函数需要接受多少个实参,好在Python允许函数从调用语句中收集任意数量的实参.
# 5传递任意数量的实参
# 有时候,预先不知道函数需要接受多少个实参,好在Python允许函数从调用语句中收集任意数量的实参.
def make_pizza(*toppings):
    """概述要制作的披萨"""
    print("\nMaking a pizza with the following toppings: ")
    for topping in toppings:
        print("- " + topping)


make_pizza('pepperoni')
make_pizza('mushroom', 'green peppers', 'extra cheeps')

输出结果为:

Making a pizza with the following toppings:
- pepperoni

Making a pizza with the following toppings:
- mushroom
- green peppers
- extra cheeps

# 5.1结合使用位置实参和任意数量实参

  • # 如果要让函数接受不同类型的实参,必须在函数定义中将接纳任意数量实参的形参放在最后面.
# 5.1结合使用位置实参和任意数量实参
# 如果要让函数接受不同类型的实参,必须在函数定义中将接纳任意数量实参的形参放在最后面.
# 文件名:make_pizza.py
def make_pizza(size, *toppings):
    """概述要制作的披萨"""
    print("\nMaking a " + str(size) +
          "-inch pizza with the following toppings: ")
    for topping in toppings:
        print("- " + topping)


make_pizza(16, 'pepperoni')
make_pizza(20, 'mushroom', 'green peppers', 'extra cheeps')

输出结果为:

Making a 16-inch pizza with the following toppings:
- pepperoni

Making a 20-inch pizza with the following toppings:
- mushroom
- green peppers
- extra cheeps

# 5.2使用任意数量的关键字实参

  • # 有时候需要接受任意数量的实参,但预先不知道传递给函数的会是什么样的信息.在这种情况下,
  • # 可将函数编写成能够接受任意数量的键-值对-----调用语句提供了多少就接受多少.
# 5.2使用任意数量的关键字实参
# 有时候需要接受任意数量的实参,但预先不知道传递给函数的会是什么样的信息.在这种情况下,
# 可将函数编写成能够接受任意数量的键-值对-----调用语句提供了多少就接受多少.
# 文件名:user_profile.py
def build_profile(first, last, **user_info):  # **user_info形参两个星号让Python创建了一个名为user_info的空字典.
    """创建一个字典,其中包含我们知道的有关用户的一切"""
    profile = {}
    profile['first_name'] = first
    profile['last_name'] = last
    for key, value in user_info.items():
        profile[key] = value
    return profile


user_profile = build_profile('albert', 'einstein',
                             location='princeton',
                             field='physics')
print(user_profile)

输出结果为:

{'first_name': 'albert', 'last_name': 'einstein', 'location': 'princeton', 'field': 'physics'}

  • # 编写函数时,你可以以各种方式混合使用位置实参、关键字实参和任意数量的实参.
  •  

# 6将函数存储在模块中

  • # 函数的优点之一是,使用它们可将代码和主程序分离.通过给函数指点描述性名称,可让主程序容易理解很多.
  • # 还可以将函数存储在被称为模块的独立文件中,再将模块导入到主程序中.
  • # import语句允许在当前运行的程序文件中使用模块中的代码.
  • # 通过将函数存储在独立的文件中,可隐藏程序中代码的细节,将重点放在程序的高层逻辑上;
  • # 还能让你的代码在众多不同的程序中重用函数.

# 6.1导入整个模块

  • # 要让函数是可导入的,要先创建模块.模块是扩展名为.py的文件,包含要导入到程序中的代码.
# 6.1导入整个模块
# 要让函数是可导入的,要先创建模块.模块是扩展名为.py的文件,包含要导入到程序中的代码.
# 创建一个文件名为pizza8_1.py包含一个函数make_pizza()的函数模块.
def make_pizza(size, *toppings):
    """概述要制作的pizza"""
    print("\nMaking a " + str(size) +
          "-inch pizza with the following toppings: ")
    for topping in toppings:
        print("- " + topping)

  • # 再在pizza.py所在的目录中创建另一个名为making_pizzas8_1.py的文件,
  • # 在这个文件中导入刚创建的模块,并调用make_pizza().
# 再在pizza.py所在的目录中创建另一个名为making_pizzas8_1.py的文件,
# 在这个文件中导入刚创建的模块,并调用make_pizza().
# 文件名:making_pizzas8_1.py
import pizza8_1  # 方式一

pizza8_1.make_pizza(16, 'pepperoni')
pizza8_1.make_pizza(24, 'mushroom', 'green peppers', 'extra cheese')


如果你操作成功,运行making_pizzas8_1.py后,输出结果为:

Making a 16-inch pizza with the following toppings:
- pepperoni

Making a 24-inch pizza with the following toppings:
- mushroom
- green peppers
- extra cheese

如下图:两个文件必须在同一个目录下

  • # 要调用被导入的模块中的函数,可指定导入的模块名称和函数名,并用句点分隔它们.
  • # 这就是一种导入方法:只需要编写一条import语句并在其中指定模块名,就可以在程序中使用该模块中的所有函数.
  • # 用下面的语法来使用其中的任何一个函数:
  • module_name.function_name()

# 6.2导入特定函数

  • # 还可以导入模块中的特定的函数,这种语法为:
  • from module_name import function_name
  • # 通过用逗号分隔函数名,可根据需要从模块中导入任意数量的函数:
  • from module_name import function1_name, function2_name, function3_name
  •  
# # 6.2导入特定的函数
# 对于making_pizzas8_1.py的示例,如果只想导入要使用的函数,代码如下:
# 文件名:making_pizzas8_1.py
from pizza8_1 import make_pizza  # 方式二

make_pizza(16, 'pepperoni')
make_pizza(24, 'mushroom', 'green peppers', 'extra cheese')

输出结果为:

Making a 16-inch pizza with the following toppings:
- pepperoni

Making a 24-inch pizza with the following toppings:
- mushroom
- green peppers
- extra cheese

  • # 从输出结果可以看出,两种方式的输出结果是一样的. 方式二中,由于我们在import语句中显式地导入了函数make_pizza(),因此在调用它时只需指定其名称.

# 6.3使用as给函数指定别名

  • # 如果要导入的函数名称可能与程序中现有的名称冲突,或者函数名称太长,
  • # 可指定简短而独一无二的别名-----函数的另一个名称,类似于外号.
  • # 要给函数指定这种特殊的外号,需要在导入函数时就这样做.
  • # 可使用关键字as给函数取别名.
  • # 给函数指定别名的通用语法如下:
  • from module_name import function_name as fn
# 6.3使用as给函数指定别名
# 如果要导入的函数名称可能与程序中现有的名称冲突,或者函数名称太长,
# 可指定简短而独一无二的别名-----函数的另一个名称,类似于外号.
# 要给函数指定这种特殊的外号,需要在导入函数时就这样做.
# 可使用关键字as给函数取别名.
from pizza8_1 import make_pizza as mp  # 方式三

mp(16, 'pepperoni')
mp(24, 'mushroom', 'green peppers', 'extra cheese')

# # 指定别名的通用语法如下:
# from module_name import function_name as fn

输出结果为:

Making a 16-inch pizza with the following toppings:
- pepperoni

Making a 24-inch pizza with the following toppings:
- mushroom
- green peppers
- extra cheese

# 6.4使用as给模块指定别名

  • # 还可以通过关键字as给模块指定别名
  • # 给模块指定别名的通用语法如下:
  • from module_name as mn
  •  
# # 6.4使用按时给模块指定别名
# # 还可以通过关键字as给模块指定别名.
# # 给模块指定别名的通用语法如下:
# from module_name as mn
import pizza8_1 as p

p.make_pizza(16, 'pepperoni')
p.make_pizza(24, 'mushroom', 'green peppers', 'extra cheese')

输出结果为:

Making a 16-inch pizza with the following toppings:
- pepperoni

Making a 24-inch pizza with the following toppings:
- mushroom
- green peppers
- extra cheese

# 6.5导入模块中的所有函数

  • # 使用星号*运算符可让Python导入模块中的所有函数.
# 6.5导入模块中的所有函数
# 使用星号*运算符可让Python导入模块中的所有函数.
from pizza8_1 import *

make_pizza(16, 'pepperoni')
make_pizza(24, 'mushroom', 'green peppers', 'extra cheese')

# 由于导入了模块中的每个函数,因此可通过函数名直接调用每个函数,而无需使用句点表示法.
# 然而,使用并非自己编写的大型模块时,最好不要采用这种方法:
# 如果模块中有函数/变量的名称与你的项目中使用的名称相同,进而会被覆盖,会导致意想不到的结果.
# 最佳做法是,要么只导入你需要使用的函数,要么导入整个模块并使用句点表示法来调用你需要使用的函数.

输出结果为:

Making a 16-inch pizza with the following toppings:
- pepperoni

Making a 24-inch pizza with the following toppings:
- mushroom
- green peppers
- extra cheese

  • # 由于导入了模块中的每个函数,因此可通过函数名直接调用每个函数,而无需使用句点表示法.
    # 然而,使用并非自己编写的大型模块时,最好不要采用这种方法:
    # 如果模块中有函数/变量的名称与你的项目中使用的名称相同,进而会被覆盖,会导致意想不到的结果.
    
  • # 最佳做法是,要么只导入你需要使用的函数,要么导入整个模块并使用句点表示法来调用你需要使用的函数.

# 7函数编写指南

# 编写函数时,应牢记几个细节:

  • # 1.给函数指定描述性名称,且只使用小写字母和下划线命名函数.
  • # 2.每个函数定义后面,都要有采用文档字符串格式阐述其功能的注释.
  • # 3.建议代码行的长度不要超过79个字符.如果形参太多,可在函数定义中输入左括号后按回车,
  •         然后按两次Tab键,从而将形参列表和只缩进一层的函数体区分开来.
  • # 4.如果程序/模块包含多个函数,可使用两个空行将相邻的函数分开.
  •       这样更容易知道前一个函数在什么地方结束,下一个函数在什么地方开始.
  • # 5.所有的import语句都应放在文件的开头.唯一例外的情形是,在文件开头使用了注释来描述整个程序.
  • 耐心之树,结黄金之果。

猜你喜欢

转载自blog.csdn.net/yishuihanq/article/details/106179159
今日推荐