Day5:Python基础(六)—— 函数

系列文章目录

上一篇:Day4:Python基础(五)—— 常见语句



前言

随着业务层的功能越来越多,代码量也会越来越多,且很多功能是很多业务所通用的,这时候就要考虑将这些常用的功能封装在一个模块中,而函数就是一系列功能的封装,实现了模块的复用性。在本章节中,我们就来学习如何自定义函数,函数调用时的规则,以及python中有哪些内置的函数。


一、自定义函数

1. 形式

def 函数名(arg1, arg2, ...):
	业务逻辑
	return	表达式

def 是函数定义的标志,arg1arg2分别表示形参,各形参间中间用逗号隔开,假如没有return,返回值为None

示例:

def my_sum(x, y):
	return (x+y)
print(my_sum(2, 3))		# 输出5

2. 参数

2.1 参数类型

  1. 位置传参:在实参中,通过与形参位置对应决定参数的传递
    示例:
def greet(name, age):
    print(f"Hello {name}, you are {age} years old.")
greet('Welt', 21)	# 输出Hello Welt, you are 21 years old.
  1. 关键词传参:在实参中,通过对形参的变量名“赋值”进行参数传递(要在位置参数之后,否则会破坏位置关系)
    示例:
def greet(name, age):
    print(f"Hello {name}, you are {age} years old.")
greet(age=21, name='Welt')	# 输出Hello Welt, you are 21 years old.
  1. 默认参数值:在形参中,对形参进行赋值,表示在参数传递时,可以不给该形参传递参数,不传参是取等号右方值(要在非默认值参数的后面,否则将会报错:SyntaxError: non-default argument follows default argument
    示例:
def greet(age, name = 'Tesra'):
    print(f"Hello {name}, you are {age} years old.")
greet(21)	# 输出Hello Tesra, you are 21 years old.
  1. 可变位置参数:在形参中,通过在变量前添加*表示不确定的附加参数,作为一个元组传入
    示例:
def add(*numbers):
	print(numbers)
    print(sum(numbers))
add(1, 2, 3, 4)  # 输出(1, 2, 3, 4)
				 #	   10
  1. 可变关键词传参:在形参中,通过在变量前添加**表示不确定的附加参数,作为一个字典传入(在所有参数后面)
    示例:
def my_show(name, age, **arg):
    print(f"My name is {name}, {age} years old.")
    # 下方输出附加信息
    for key, value in arg.items():
        print(f"{key}: {value}")
my_show('Welt', 21, address='Moon',gender='male')
'''
My name is Welt, 21 years old.
address: Moon
gender: male
'''
  1. 解包位置参数:当要传入的数据是列表元组这样的结构化数据时,可以将其解包并依次填充到各位置参数上

示例:

def greet(name, age, country):
    print(f"Name: {name}, Age: {age}, Country: {country}")

# 数据封装在一个元组中
info = ("Alice", 30, "Wonderland")

# 使用 * 解包为位置参数
greet(*info)	# 输出 Name: Alice, Age: 30, Country: Wonderland
  1. 解包关键词参数:当要传入的数据是字典这样的结构化数据时,可以将其解包并将传参给各关键词参数上
    示例:
def show_info(name, age, country):
    print(f"Name: {name}, Age: {age}, Country: {country}")

# 参数封装在字典中
info_dict = {
    "name": "Bob",
    "age": 25,
    "country": "USA"
}

# 使用 ** 解包为关键字参数
show_info(**info_dict)	# 输出Name: Bob, Age: 25, Country: USA

2.2 参数传递

无论参数为可变类型还是不可变类型,参数的传递都是传递 引用(即函数内的形参作为一个临时的引用,指向实参的内存空间或者实参所引用的空间(实参同样是一个引用的情况下)),区别在于对可变类型的引用可以修改该内存空间中的内容,而对不可变类型的引用则无法修改所指向内存空间的内容,只能重新指向其他内存空间

3. lambda表达式

是一个 表达式 ,意味着可以返回值,返回的是是一个函数对象。在逻辑较简单或者调用的次数不多的情况下可以替代函数定义

3.1 形式

lambda arg1, arg2: 表达式

由于lambda返回的是一个函数对象,arg1和arg2表示该函数对象传入的参数,表达式表示的是该函数对象的返回值

3.2 示例

f = lambda x, y: x + y
print(f)                 # 输出<function <lambda> at 0x000001F682A35E40>
print(type(f))           # 输出<class 'function'>
print(f(2, 3))           # 输出5

替换成函数定义形式如下:

def func(x, y):
	return x + y
print(func)				# 输出<function func at 0x000001F68292CA40>
print(type(func))		# 输出<class 'function'>
print(func(2,3))		# 输出5

二、常见内置函数

1. all(iterable)

iterable的元素进行遍历,如果其中全部元素都为True(包括没有元素时),则返回True,发现有任意元素False则返回False

示例:

list1 = []
list2 = [1, 2, 3]
list3 = [1, 2, 0]
list4 = [1, 2, None]
list5 = [1, 2, False]
list6 = [1, 2, []]
print(all(list1))	# 输出True,空列表没有元素为假
print(all(list2))	# 输出True
print(all(list3))	# 输出False,因为元素10为Flase
print(all(list4))	# 输出Fasle,因为元素None为False
print(all(list5))	# 输出False,因为有元素False
print(all(list6))	# 输出False,因为元素[]为False

2. any (iterable)

iterable的元素进行遍历,如果存在任意元素为True,则返回True,当所有元素全为False时返回False(包括没有元素时)

示例:

list1 = []
list2 = [None, 0, []]
list3 = [None, 0, [], 1]
print(any(list1))	# 输出False
print(any(list2))	# 输出False
print(any(list3))	# 输出True

3. sum(iterable, /, start=0)

start作为起始值,对iterable中的元素进行进行遍历求和,start的默认值为0,当内部元素为同类的可迭代对象也可以进行可迭代对象进行拼接,只是要对start赋值使得其能够适用+运算就可以,但是str的拼接是不可行的,会报错:TypeError: sum() can't sum strings [use ''.join(seq) instead] ,因为start 值则不允许为字符串。

示例:

list1 = [1, 3, 4]
list2 = [[1,2,3], [4,5,6]]
list3 = ['hello', 'world']
print(sum(list1))	# 输出8
print(sum(list2, start = ))	# 输出[1, 2, 3, 4, 5, 6]
print(sum(list3))	# 报错TypeError

4. zip(*iterables)

在多个迭代器(来自*iterables)上并行迭代,从每个迭代器返回一个数据项组成元组,而这些元组作为元素构成一个迭代器返回。返回的迭代器中元组的数量取决于iterables元素最少的那一个。

示例:

list1 = [1, 2, 3, 4]
tuple1 = ['a', 'b', 'c']
list2 = [True, False, False]
ziped = zip(list1, tuple1, list2)
print(ziped)	# 输出<zip object at 0x000001F682929AC0>,是一个迭代器
print(list(ziped))	# [(1, 'a', True), (2, 'b', False), (3, 'c', False)],其中有3个元组

三、函数递归调用

在对复杂问题的求解过程中,递归调用是一种非常有效的编程技术。递归是指函数直接或间接 调用自身 的过程,通过将大问题 分解 为相似的子问题来逐步解决,思考的过程相对简单。

1. 经典案例

1.1 斐波那契数列

首先得到问题的定义:
F e b ( n ) = { 0 n = 0 1 n = 1 F e b ( n − 1 ) + F e b ( n − 2 ) n > 1 Feb(n) = \begin{cases} 0 & n = 0 \\ 1 & n = 1 \\ Feb(n-1) + Feb(n-2) & n > 1 \end{cases} Feb(n)= 01Feb(n1)+Feb(n2)n=0n=1n>1
即从第三项(Feb(2))开始,每一项都等于前两项之和

def feb(n):
	if n==0 or n==1:
		return n
	return feb(n-1) + feb(n-2)	# 递归部分

# 调用函数
print(feb(0))		# 输出0
print(feb(1))		# 输出1
print(feb(6))		# 输出8
# print(feb(3000)) 	# 将导致栈溢出,程序无法正常执行

1.2 求n的阶乘:

首先得到问题的定义: n ! = { 1 n = 0 n ⋅ ( n − 1 ) ! n > 0 n! = \begin{cases} 1 & n = 0 \\ n \cdot (n - 1)! & n > 0 \end{cases} n!={1n(n1)!n=0n>0

即从n往前依次相乘,乘到1(0的阶乘)为止

def factorial(n):
    if n == 0:
        return 1
    return n * factorial(n - 1)	# 递归部分

# 函数调用
print(factorial(0))		# 输出1
print(factorial(1))		# 输出1
print(factorial(5))		# 输出120

2. 递归和循环(迭代)的区别

由于Python是图灵完备的语言,故从理论上来说:凡是可以用递归实现的算法,都可以用 循环(迭代) 来实现,反之亦然。然而,两者在实际的实现过程中,性能上和编写上有显著的差异

  1. 递归的特点:
    • 递归算法通常采用"分而治之"的策略,将问题分解为更小的同类子问题(例如阶乘计算:n! = n * (n-1)!)
    • 递归代码通常更简洁直观,更接近数学定义形式(如斐波那契数列:fib(n) = fib(n-1) + fib(n-2))
    • 每次递归调用都会在调用栈中创建新的栈帧,递归次数太多时会导致导致栈溢出
    • 递归可能存在重复计算问题(如简单递归实现的斐波那契数列会有O(2^n)的时间复杂度)
  2. 循环(迭代)的实现特点:
    • 通常需要维护额外的状态变量(如在计算阶乘时需要维护累乘变量)
    • 不会产生额外的函数调用开销,空间复杂度通常是O(1)
    • 代码可能比递归版本更冗长,但执行效率更高

总结

在本章中,我们学习了如何定义函数,以及如何传递参数、如何进行函数的递归调用 。在实际的应用中,应该根据结构化数据(如.json文件)的特点来选择最佳的参数传递方式,根据问题规模选择递归还是迭代,还要在适当的情境下能够想起来有哪些内置函数是可用的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值