Day 26 函数专题1:函数定义与参数

@浙大疏锦行

今日任务:
  1. 函数的定义
  2. 变量作用域:局部变量和全局变量
  3. 函数的参数类型:位置参数、默认参数、不定参数
  4. 传递参数的手段:关键词参数
  5. 传递参数的顺序:同时出现三种参数类型时

函数的定义

在前面提到过,对于一些可重复的代码,可以使用函数的方法将其进行封装,从而实现特定功能。

关于函数的定义,使用的是def关键字,示例如下:

def name_myself():
    print('我是XXX!')

def greet(name): #函数的名称,参数
    """这是一个问候函数""" #注释说明
    #函数主体
    print(f"Hello, {name}!")
    return f"Greeted {name} successfully" #返回值
 
result = greet("Alice") #调用函数
print(result)

从上面的例子中,可以知道,定义函数时,有以下几个部分:

  • 函数的名称:命名规则与变量的命名相同
  • 参数:即‘()’,可以没有参数填入,但必须有括号
  • 注释说明:关于函数的参数含义,以及函数的用途说明,一般使用三引号进行多行注释说明
  • 函数主体:具体编写与之前类似,这里需要注意变量的作用范围(局部变量或全局变量)
  • 返回值:不是所有函数都有返回值,但是遇到return就会立即返回,不会继续执行后续代码。
  • 调用:定义好函数后,要记得调用,否则函数不会执行

下面说明几个重要的部分。

参数

首先,需要知道‘形参’与‘实参’。形参就是在函数定义时的参数,比如上面里的‘name’。而实参就是在函数调用时传递的实际值,如上面的‘Alice’。

关于参数的类型有以下三类:位置参数、默认值参数以及可变数量参数。

位置参数

参数的传递需要按照位置传递,同时要注意对于必选参数,必须提供参数值,否则会报错。

还需要注意的是,在调用函数时输入参数的方式主要有位置参数和关键字参数两种。前者依赖位置顺序传递,而后者依赖关键字传递(与位置无关),显然实际使用中更推荐关键字传递

def student_info(name, grade, score):
    """位置参数必须按顺序传递"""
    print(f"学生: {name}, 年级: {grade}, 分数: {score}")

student_info("张三", "三年级", 95)
# 输出: 学生: 张三, 年级: 三年级, 分数: 95

# student_info("李四")  # 错误!缺少必选参数

默认值参数

对于某些存在默认值的参数来说,,那么在调用的时候,如果不提供这些参数值,就会使用默认值。同时需注意,对于默认参数需要为不可变对象

需要放在必选参数的后面

def register_user(name, age, country="中国", city="北京"):
    """country和city有默认值,可不传递"""
    print(f"姓名: {name}, 年龄: {age}, 国家: {country}, 城市: {city}")

register_user("王五", 20)# 输出: 姓名: 王五, 年龄: 20, 国家: 中国, 城市: 北京

register_user("赵六", 22, "美国")# 输出: 姓名: 赵六, 年龄: 22, 国家: 美国, 城市: 北京

register_user("钱七", 25, "英国", "伦敦")# 输出: 姓名: 钱七, 年龄: 25, 国家: 英国, 城市: 伦敦

可变数量参数

接受不定数量的参数,以元组或字典的形式收集,包括两种类型,args(arguments)和kwargs(keyword arguments):

  • *args:将多余的位置参数收集 为一个元组
  • **kwargs:将多余的关键字参数收集为一个字典

*args(位置参数不够)

若存在多余位置,就放入元组中;若提供的参数数量小于等于明确定义的形参数量,则*args为空元组(没有多余)

def make_pizza(size, *toppings):
    """概述要制作的比萨。
    *toppings 会将所有额外的位置参数收集到一个元组中。
    """
    print(f"\n制作一个 {size} 寸的比萨,配料如下:")
    if toppings: # 只要toppings不为空元组,就会执行
        for topping in toppings:
            print(f"- {topping}")
    else:
        print("- 原味 (无额外配料)")

make_pizza(12, "蘑菇")
make_pizza(16, "香肠", "青椒", "洋葱") #后面三个参数即对应*toppings里的
make_pizza(9) # toppings 会是空元组

**kwargs(关键词参数不够)

在调用的时候是否所有的关键字参数都能在函数定义中找到对应的明确形参名

def build_profile(first_name, last_name, **user_info):
    """创建一个字典,其中包含我们知道的有关用户的一切。
    **user_info 会将所有额外的关键字参数收集到一个字典中。
    """
    profile = {}
    profile['first_name'] = first_name
    profile['last_name'] = last_name
    for key, value in user_info.items():
        profile[key] = value
    return profile

user_profile = build_profile('爱因斯坦', '阿尔伯特',
                             location='普林斯顿',
                             field='物理学', #多余的关键字
                             hobby='小提琴') #多余的关键字
print(f"\n用户信息: {user_profile}")
# 输出: {'first_name': '爱因斯坦', 'last_name': '阿尔伯特', 'location': '普林斯顿', 'field': '物理学', 'hobby': '小提琴'}

最后,对于上述的参数,定义的时候顺序是:

必选参数 → 默认参数(非必选) → *args → **kwargs

def example(a, b, c=1, d=2, *args, e, f=10, **kwargs):
    """
    完整的参数顺序示例:
    1. a, b - 必选位置参数
    2. c, d - 默认参数
    3. *args - 可变位置参数
    4. e - 必选关键字参数(在*或*args之后)
    5. f - 默认关键字参数
    6. **kwargs - 可变关键字参数
    """
    pass

变量的作用域

根据变量的作用范围不同,可以分为局部变量和全局变量。

  • 局部变量:在函数内部定义,只在函数内部有效。当函数执行完毕后,一般会被销毁。
  • 全局变量:在函数外部定义,在整个程序中有效,可在程序内任何地方被访问。如果在函数内部需要修改全局变量,要使用global关键字。
count = 0 #全局变量
def increment():
    global count  # 声明使用全局变量
    count += 1
    s = 0 #局部变量,函数内部定义
    print('s是一个局部变量')

def increment_error():
    # count += 1  # 错误! 会创建新的局部变量
    pass

print(f"初始值: {count}")  # 0
increment()
print(f"调用后: {count}")  # 1
# print(f"在函数外部,不能看到局部变量: {s}") # 这会产生 NameError,因为 local_var 只在函数内存在

返回值

使用return会返回一个值。如果一个函数中没有return语句或者return后面没有值,那么它会自动返回None。在一个函数中可以有多个return语句(比如多个分支),但最终执行的只有一个return语句,因为程序遇到return就会立即返回,而不会执行后续操作

对于return和print,主要区别为:return用于函数间的数据传递,支持进一步处理和链式调用;尔print主要用于向用户显示信息,不影响程序的逻辑。

既然return在函数中也不是必选的,那么什么时候使用return和print呢?

  • return:结果需要被其他代码使用
  • print:只需要看到结果是什么
  • return + print:即要看到结果又会被后续代码使用

作业

题目1:计算圆的面积

  • 任务: 编写一个名为 calculate_circle_area 的函数,该函数接收圆的半径 radius 作为参数,并返回圆的面积。圆的面积 = π * radius² (可以使用 math.pi 作为 π 的值)
  • 要求:函数接收一个位置参数 radius。计算半径为5、0、-1时候的面积
  • 注意点:可以采取try-except 使函数变得更加稳健,如果传入的半径为负数,函数应该返回 0 (或者可以考虑引发一个ValueError,但为了简单起见,先返回0)。
import math
def calculate_circle_area(radius):
    """计算圆的面积"""
    if radius < 0:
        return 0
    area = math.pi*radius**2
    return area
#调用
result = calculate_circle_area(1)
print(result)

题目2:计算矩形的面积

  • 任务: 编写一个名为 calculate_rectangle_area 的函数,该函数接收矩形的长度 length 和宽度 width 作为参数,并返回矩形的面积。
  • 公式: 矩形面积 = length * width
  • 要求:函数接收两个位置参数 length 和 width。
    • 函数返回计算得到的面积。
    • 如果长度或宽度为负数,函数应该返回 0。
def calculate_rectangle_area(length,width):
    """计算矩形的面积"""
    if length < 0 or width < 0:
        return 0
    area = length*width
    return area
#调用
result = calculate_rectangle_area(7,5)
print(result)

题目3:计算任意数量数字的平均值

  • 任务: 编写一个名为 calculate_average 的函数,该函数可以接收任意数量的数字作为参数(引入可变位置参数 (*args)),并返回它们的平均值。
  • 要求:使用 *args 来接收所有传入的数字。
    • 如果没有任何数字传入,函数应该返回 0。
    • 函数返回计算得到的平均值。
def calculate_average(*numbers):
    """
    计算任意数量数字的平均值,
    numbers:接受任意数量的数字的元组
    """
    if len(numbers) == 0:
        return 0
    return sum(numbers)
    
#调用
result = calculate_average(1,2,3)
print(result)

题目4:打印用户信息

  • 任务: 编写一个名为 print_user_info 的函数,该函数接收一个必需的参数 user_id,以及任意数量的额外用户信息(作为关键字参数)。
  • 要求:
    • user_id 是一个必需的位置参数。
    • 使用 **kwargs 来接收额外的用户信息。
    • 函数打印出用户ID,然后逐行打印所有提供的额外信息(键和值)。
    • 函数不需要返回值
def print_user_info(user_id,**extra_info):
    """
    打印用户信息
    extra_info:接受额外用户信息
    """
    print('user_id:{user_id}')
    for key,value in extra_info.items():
        print(f'{key}:{value}')
    
#调用
result = print_user_info(1,gender='female',age=36)
print(result)

题目5:格式化几何图形描述

  • 任务: 编写一个名为 describe_shape 的函数,该函数接收图形的名称 shape_name (必需),一个可选的 color (默认 “black”),以及任意数量的描述该图形尺寸的关键字参数 (例如 radius=5 对于圆,length=10, width=4 对于矩形)。
  • 要求:shape_name 是必需的位置参数。
    • color 是一个可选参数,默认值为 “black”
    • 使用 **kwargs 收集描述尺寸的参数。
    • 函数返回一个描述字符串,格式如下:
    • “A [color] [shape_name] with dimensions: [dim1_name]=[dim1_value], [dim2_name]=[dim2_value], …”如果 **kwargs 为空,则尺寸部分为 “with no specific dimensions.”

第一种方法,但是发现最后元素输出后为逗号,理论上应该是句号。

def describe_shape(shape_name,color='black',**size):
    """
    格式化几何图形描述\n
    shape_name: 图形名称(必选参数)\n
    color: 颜色,默认为黑色\n
    size: 任意数量的描述该图形尺寸的关键字参数
    """
    if not size:
        return f'A {color} {shape_name} with no specific dimensions.'
    based = f'A {color} {shape_name} with dimensions: '
    for key,value in size.items():
        based += f'{key}={value},'
    return based
    
#调用
result = describe_shape('turtle','red',length=5,width=4)
print(result)

使用join()方法修改后:

def describe_shape(shape_name,color='black',**size):
    """
    格式化几何图形描述\n
    shape_name: 图形名称(必选参数)\n
    color: 颜色,默认为黑色\n
    size: 任意数量的描述该图形尺寸的关键字参数
    """
    if not size:
        return f'A {color} {shape_name} with no specific dimensions.'
    based = f'A {color} {shape_name} with dimensions: '
    dimensions = []
    for key,value in size.items():
        dimensions.append(f'{key}={value}')
    based += ','.join(dimensions) + '.' #字符串拼接
    return based
    
#调用
result = describe_shape('turtle','red',length=5,width=4)
print(result) #A red turtle with dimensions: length=5,width=4.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值