相信大家看了前几章python教程,能明显发现,这些代码基本上都是从上往下执行的。前面那些案例,我主要是用来模拟演示的,在实际的项目开发过程中,这种写法既低效又繁琐,尤其是在需要重复执行同一套逻辑时,更是显而易见。

举个例子,leader让你开发商品折扣的计算功能,如果反复遇到同样的计算逻辑,你就得把每一个计算公式都写一遍。这么做一方面,效率低下,另一方面,如果你后期想修改折扣规则的时候,容易漏改某一处。但如果你把这套逻辑写成函数,以后遇到同样的业务场景,就可以直接调用函数。那我们今天这一章,要讲解的内容,就是函数的定义和调用,以及参数与返回值的常见写法,下面我们开始吧。

一、为什么要使用函数
先看一段没有使用函数的代码:
price1 = 200 final_price1 = price1 * 0.8 print(final_price1) price2 = 350 final_price2 = price2 * 0.8 print(final_price2)
两次计算使用的是同一套规则,只是商品价格不同。可以把计算过程放进一个函数:
def calculate_discount(price):
final_price = price * 0.8
return final_price
print(calculate_discount(200))
print(calculate_discount(350))这样写以后,折扣规则只保留一份。如果折扣从八折改成九折,只需要修改函数里面的计算。
函数还有一个作用,就是给一段逻辑起名字。看到 calculate_discount(),基本就能知道这段代码在计算折扣,不需要每次都重新阅读里面的细节。
二、定义和调用函数
python 使用 def 定义函数:
def say_hello():
print("hello")
这一段代码由几个部分组成:
def 函数名():
函数体
def表示开始定义一个函数。say_hello是函数名。- 函数名后面要写一对圆括号
()。 - 行尾的冒号
:不能省略。 - 函数体需要缩进。
定义函数并不会立即执行函数体。需要使用函数名加圆括号进行调用:
say_hello()
完整代码是:
def say_hello():
print("hello")
say_hello()
say_hello()函数调用了两次,因此会打印两次 hello。

三、函数的执行过程
程序执行到函数定义时,会记住这个函数,不过不会进入函数体。执行到函数调用时,才会进入函数内部:
def show_message():
print("正在执行函数")
print("调用之前")
show_message()
print("调用之后")输出顺序为:
调用之前 正在执行函数 调用之后
调用结束以后,程序会回到原来的位置,继续执行函数调用后面的代码。
函数通常要先定义,再调用。下面的代码会出现 nameerror:
say_hello()
def say_hello():
print("hello")python 从上到下执行。调用 say_hello() 时,后面的函数定义还没有执行,所以此时找不到这个名字。

四、参数和实参
函数如果只能执行一套固定内容,用途会比较有限。参数可以让调用者把不同的数据传入函数:
def greet(name):
print(f"hello,{name}")
greet("ivan")
greet("tom")定义函数时,圆括号中的 name 叫作形参。调用函数时传入的 "ivan" 和 "tom" 叫作实参。
可以简单理解为,形参是函数内部准备接收数据的变量,实参是调用时真正交给函数的数据:
def greet(name): # name 是形参
print(name)
greet("ivan") # "ivan" 是实参很多时候大家也会把它们都称为参数,只要知道它们分别出现在定义和调用阶段即可。

五、多个参数
函数可以接收多个参数,参数之间使用逗号分隔:
def add(a, b):
result = a + b
print(result)
add(10, 20)调用时传入的实参数量要与形参数量对应。下面少传了一个参数,会出现 typeerror:
add(10)
多传参数同样会报错:
add(10, 20, 30)
除非函数使用了后面要讲的不定长参数,否则参数数量不能随意增加或减少。
六、位置参数
按照参数在函数定义中的位置传值,叫作位置参数:
def introduce(name, age, city):
print(f"姓名:{name}")
print(f"年龄:{age}")
print(f"城市:{city}")
introduce("ivan", 20, "杭州")调用时,第一个实参交给 name,第二个交给 age,第三个交给 city。
位置写错以后,python 不一定会报错,但是数据的含义可能已经变了:
introduce(20, "杭州", "ivan")
这段代码可以运行,不过 name 得到的是 20,age 得到的是 "杭州"。所以使用位置参数时,需要记住参数顺序。
七、关键字参数
调用函数时,也可以明确写出参数名:
introduce(name="ivan", age=20, city="杭州")
这种写法叫作关键字参数。因为参数名称已经写清楚,所以顺序可以调整:
introduce(city="杭州", name="ivan", age=20)
关键字参数更长一些,不过参数较多,或者某个布尔值、数字的含义不明显时,会更容易读懂:
def create_video(width, height, fps, muted):
print(width, height, fps, muted)
create_video(width=1920, height=1080, fps=30, muted=true)如果只写成 create_video(1920, 1080, 30, true),最后的 true 代表什么就不太直观。
位置参数和关键字参数可以一起使用,但是位置参数必须写在关键字参数前面:
introduce("ivan", age=20, city="杭州")
下面的写法会出现语法错误:
introduce(name="ivan", 20, "杭州")

八、默认参数
有些参数在大多数时候使用同一个值,可以在定义函数时设置默认值:
def greet(name, message="hello"):
print(f"{message},{name}")
greet("ivan")
greet("tom", "早上好")第一次调用没有传入 message,所以使用默认值 "hello"。第二次传入了新的内容,默认值会被覆盖。
必填参数要放在默认参数前面:
def greet(name, message="hello"):
print(message, name)
下面的顺序不正确:
def greet(message="hello", name):
print(message, name)
因为调用时无法确定只传入一个位置参数时,它应该交给谁,python 会直接提示语法错误。
默认参数适合处理真正具有常用默认值的设置,例如页码、排序方式和是否显示某个选项:
def search(keyword, page=1, page_size=20):
print(keyword, page, page_size)
search("python")
search("python", page=2)
九、不要使用可变对象作为默认值
默认参数还有一个很容易踩到的问题。下面的写法看起来像是每次调用都创建一个空列表:
def add_item(item, items=[]):
items.append(item)
return items
print(add_item("python"))
print(add_item("java"))实际结果为:
['python'] ['python', 'java']
默认值是在函数定义时创建的,不是每次调用时重新创建。同一个列表会被后续调用继续使用。
通常改成 none,再在函数内部创建新列表:
def add_item(item, items=none):
if items is none:
items = []
items.append(item)
return items字典和集合也是可变对象,不建议直接作为默认参数。字符串、数字、布尔值、none 这类不可变值通常没有这个问题。

十、使用*args接收多个位置参数
如果函数需要接收的参数数量不固定,可以在参数名前加一个星号 *:
def calculate_total(*prices):
total = 0
for price in prices:
total += price
return total
print(calculate_total(20, 35, 18))
print(calculate_total(10, 20, 30, 40, 50))这里的 prices 是一个元组。调用时传入多少个位置参数,它就保存多少个:
def show_args(*args):
print(args)
print(type(args))
show_args(1, 2, 3)输出结果为:
(1, 2, 3) <class 'tuple'>
args 只是常用名称,真正让它接收多个参数的是前面的 *。不过实际编写代码时通常还是沿用 args,这样别人一眼就能看懂。
普通参数可以和 *args 一起出现:
def print_scores(title, *scores):
print(title)
for score in scores:
print(score)
print_scores("第一次考试", 86, 92, 78)第一个实参交给 title,剩余的位置参数都放入 scores。
十一、使用**kwargs接收多个关键字参数
两个星号 ** 可以接收数量不固定的关键字参数:
def create_user(**info):
print(info)
create_user(name="ivan", age=20, city="杭州")这里的 info 是一个字典:
{'name': 'ivan', 'age': 20, 'city': '杭州'}
调用时写下的参数名会成为字典的键,传入的数据会成为对应的值。
kwargs 同样只是常用名称,真正起作用的是 **:
def show_info(**kwargs):
for key, value in kwargs.items():
print(key, value)*args 保存额外的位置参数,得到元组;**kwargs 保存额外的关键字参数,得到字典。

十二、不同参数的排列顺序
普通参数、默认参数、*args 和 **kwargs 可以放在同一个函数中:
def show_data(name, age=18, *args, **kwargs):
print("name:", name)
print("age:", age)
print("args:", args)
print("kwargs:", kwargs)
show_data(
"ivan",
20,
"python",
"java",
city="杭州",
active=true,
)结果为:
name: ivan
age: 20
args: ('python', 'java')
kwargs: {'city': '杭州', 'active': true}基础阶段可以先记住常用顺序:普通参数、带默认值的参数、*args、**kwargs。
参数写得太多以后,调用和维护都会变得麻烦。如果一组数据本身属于同一个对象,例如用户信息,很多时候直接传入一个字典会更合适,没有必要为了展示参数写法把函数设计得特别复杂。
十三、使用return返回结果
前面的部分函数直接使用 print() 输出结果,但是输出和返回并不是一回事:
def add(a, b):
print(a + b)
这个函数会把结果打印到终端,不过调用者拿不到计算结果继续使用。改成 return:
def add(a, b):
return a + b
result = add(10, 20)
print(result)return 会把结果交回函数调用的位置,所以可以把它保存到变量中,也可以参与后续计算:
total = add(10, 20) * 2 print(total)
打印适合把信息显示出来,返回值适合让程序继续使用结果。实际函数中经常需要返回数据,不应该为了看到结果就把所有内容都写成 print()。

十四、return会结束函数
程序执行到 return 以后,会立即离开函数,后面的代码不会再执行:
def check_age(age):
if age < 0:
return "年龄不能小于 0"
return "年龄有效"
print(check_age(-1))
print(check_age(20))这种写法经常用来提前处理不符合要求的数据:
def divide(a, b):
if b == 0:
return none
return a / b当 b 等于 0 时,函数返回 none 并结束,不会继续执行除法。
下面的第二个 print() 永远不会执行:
def test():
return 100
print("这一行不会执行")

十五、函数没有写return会怎样
如果函数没有写 return,或者只写了一个没有返回值的 return,python 会默认返回 none:
def show_message():
print("hello")
result = show_message()
print(result)输出结果为:
hello none
这也是为什么一个只负责打印内容的函数,把调用结果保存到变量以后会得到 none。
下面两种写法的返回结果相同:
def func1():
pass
def func2():
return它们都会返回 none。
十六、返回多个值
一个函数可以在 return 后面写多个值:
def calculate(a, b):
total = a + b
difference = a - b
return total, difference
result = calculate(10, 3)
print(result)输出结果为:
(13, 7)
看起来像是返回了两个独立的值,其实 python 会把它们放进一个元组。
调用时可以直接解包:
total, difference = calculate(10, 3) print(total) print(difference)
左侧变量的数量要与返回值数量对应,否则会出现 valueerror。
如果返回内容比较多,而且每项都有明确含义,使用字典往往更清楚:
def analyze_scores(scores):
return {
"count": len(scores),
"max": max(scores),
"min": min(scores),
"average": sum(scores) / len(scores),
}
十七、参数传递的是对象
调用函数时,形参会引用传入的对象。对于数字、字符串和元组这类不可变对象,函数内部重新赋值不会修改外面的变量:
def change_number(number):
number = 100
value = 10
change_number(value)
print(value)结果仍然是 10。
列表、字典和集合是可变对象。如果函数直接修改对象中的内容,外部也能看到变化:
def add_skill(skills):
skills.append("python")
user_skills = ["java"]
add_skill(user_skills)
print(user_skills)输出结果为:
['java', 'python']
如果不希望修改原列表,可以在函数内部创建副本并返回:
def add_skill(skills):
new_skills = skills.copy()
new_skills.append("python")
return new_skills
这一部分和前面学过的可变对象有关。更完整的对象引用、浅拷贝和深拷贝会在后面的章节中单独讲。
十八、函数文档字符串
函数的第一行可以写一段文档字符串,用来说明函数做什么、接收什么参数以及返回什么内容:
def calculate_discount(price, rate=0.8):
"""根据原价和折扣率计算折后价格。"""
return price * rate
文档字符串使用三引号,通常放在函数体的第一行。可以通过 help() 查看:
help(calculate_discount)
简单函数的说明不需要写得很长,不过函数要提供给别人使用,或者参数含义不够直观时,文档字符串会比较有用。
十九、函数命名
函数名通常使用小写字母和下划线,尽量表达它实际完成的操作:
def calculate_total():
pass
def get_user_info():
pass
def save_file():
pass像 f1()、do_it() 这类名字虽然可以运行,但是很难从调用位置判断函数的用途。
函数应该尽量完成一件相对明确的事情。一个函数里同时读取文件、处理数据、连接数据库、发送消息,后面修改和测试都会比较麻烦。不过也没有必要把每一两行代码都拆成函数,还是要看这段逻辑是否会重复使用,以及单独命名以后是不是更清楚。
二十、综合示例:计算订单价格
下面写一个订单价格函数。它接收商品价格列表、折扣率和运费,返回订单的各项金额:
def calculate_order(prices, discount=1.0, shipping_fee=0):
"""计算订单原价、折扣金额和最终价格。"""
if not prices:
return {
"subtotal": 0,
"discount_amount": 0,
"shipping_fee": shipping_fee,
"final_price": shipping_fee,
}
subtotal = sum(prices)
discounted_price = subtotal * discount
discount_amount = subtotal - discounted_price
final_price = discounted_price + shipping_fee
return {
"subtotal": subtotal,
"discount_amount": discount_amount,
"shipping_fee": shipping_fee,
"final_price": final_price,
}
order = calculate_order(
prices=[99, 128, 56],
discount=0.9,
shipping_fee=10,
)
print("商品原价:", order["subtotal"])
print("优惠金额:", order["discount_amount"])
print("运费:", order["shipping_fee"])
print("最终价格:", order["final_price"])这里把 prices 作为必填参数,因为没有商品价格就无法正常计算。discount 和 shipping_fee 有常用默认值,所以设置成默认参数。
函数返回字典,而不是直接打印结果。这样调用者可以决定如何使用数据,可以打印到终端,也可以显示在页面上或者继续参与其他计算。
空列表会走前面的 return,直接返回一份金额为零的结果,后面的计算不会继续执行。
二十一、常见错误
1. 定义函数以后忘记调用
def say_hello():
print("hello")
这里只完成了定义。如果希望它执行,还要写 say_hello()。
2. 调用函数时忘记圆括号
result = say_hello
这行代码取得的是函数本身,没有调用函数。调用要写成 say_hello()。
3. 参数数量不一致
def add(a, b):
return a + b
add(10)函数需要两个参数,只传入一个会出现 typeerror。
4. 把print()当成返回值
def add(a, b):
print(a + b)
result = add(10, 20)
print(result)函数打印了 30,但是没有返回结果,因此 result 是 none。
5. 在return后面继续写要执行的代码
return 会立即结束函数。确实需要执行的内容,应该放在 return 前面。
6. 使用列表作为默认参数
可变默认值会被多次调用共同使用。需要空列表时,通常把默认值写成 none,再在函数内部创建列表。
二十二、练习
练习一:计算矩形面积
编写一个函数,接收矩形的长度和宽度,返回矩形面积。
练习二:判断奇偶数
编写一个函数,接收一个整数。偶数返回 true,奇数返回 false。
练习三:计算平均分
使用 *args 接收数量不固定的成绩,返回平均分。没有传入成绩时返回 none。
练习四:创建用户信息
使用 **kwargs 接收用户姓名、年龄和城市,返回一个用户信息字典。
练习五:订单价格
在综合示例的基础上增加满减规则,例如订单原价达到 300 时再减 20,并把满减金额放进返回结果。
函数可以把一段会重复使用的逻辑单独保存起来,参数负责把数据传入函数,return 负责把处理结果交回调用位置。下一章我们就来讲变量作用域、匿名函数和高阶函数。
以上就是python 函数、参数与返回值实例详解的详细内容,更多关于python 函数、参数与返回值的资料请关注代码网其它相关文章!
发表评论