我的理解,程序里的函数和数学里的函数,都是对一类操作的抽象、总结。比如y=x^2,不管你传入的x是多少,运算的实质没有变化。同时,我们还可以看到,使用函数的好处,即一次编写,多次使用。
一、基本概念
1.创建和调用
def my_sqr(x):
return x**2
y = my_sqr(3)
print('3的平方是:', y)
# 输出:3的平方是: 9
2.参数
(1)位置参数,调用时参数的数量和位置必须和定义时一致
def func(name, age):
print('我的名字是:', name)
adult = False
if age >= 18:
adult = True
return adult
# 函数定义时,有两个参数,若调用时数量不一致会报错
func('lily')
# 输出:TypeError: func() missing 1 required positional argument: 'age'
# 参数位置不对应时,可能报错可能结果不可知
# 输出:TypeError: '>=' not supported between instances of 'str' and 'int'
(2)关键字参数,即通过key=value的方式传参,优点是不受顺序制约,且可读性强。
def func(name, age):
print('我的名字是:', name)
adult = False
if age >= 18:
adult = True
return adult
func(age=20, name='lily')
(3)默认参数,为参数设置默认值
def func(name, age, adult=False):
print('我的名字是:', name)
if age >= 18:
adult = True
return adult
如果使用可变对象作为函数默认参数,很容易产生不可知结果,如下例
def func(a, l=[]):
l.append(a)
print(l)
# 执行两次
func(1)
func(1)
一样的函数和参数,输出应该是一样的。实际情况却不是如此,具体参看另一篇博客
Python 避免可变对象作为函数默认参数_Jiangugu的博客-CSDN博客
(4)可变参数
主要考虑的是,事先不知道调用者会传递多少参数。
①*args,参数传递形式:(p1, p2)
def func(name, age, *args):
print('我的名字是:', name)
if age >= 18:
print('我是成年人')
for arg in args:
print('我喜欢的食物有:', arg)
func('lily', 20, '铁锅炖大鹅', '松鼠桂鱼')
# 输出:我的名字是: lily
# 我是成年人
# 我喜欢的食物有: 铁锅炖大鹅
# 我喜欢的食物有: 松鼠桂鱼
②**kwargs,参数传递形式:{p1:value1, p2:value2}
def func(name, age, **kwargs):
print('我的名字是:', name)
if age >= 18:
print('我是成年人')
for key, value in kwargs.items():
print('我喜欢的{}是{}'.format(key, value))
func('lily', 20, food='回锅肉', drink='蜜雪冰城')
# 输出:我的名字是: lily
# 我是成年人
# 我喜欢的food是回锅肉
# 我喜欢的drink是蜜雪冰城
3.返回
(1)函数可以没有返回,调用者此时若强行赋值给变量,将得到None
def func(name):
print('我的名字是:', name)
r = func('lily')
print(r)
# 输出:我的名字是: lily
# None
(2)返回多个变量时,会被包装成一个元组
def func():
return 1, 2, 3
r = func()
print(type(r))
print(r)
# 输出:<class 'tuple'>
# (1, 2, 3)
(3)基于Python中一切皆对象的理念,这里也可以提出一切皆可返回的概念
class Adult:
def say(self):
print('我是成年人')
class Child:
def say(self):
print('我还是个孩子')
def func(age):
if age >= 18:
return Adult()
else:
return Child()
r = func(17)
r.say()
# 输出:我还是个孩子
二、综合使用
1.良好的注释和断言
编写一个好的函数,应该有一个良好的注释,阐明函数的作用、参数的含义。更好地,能对参数进行一些断言(而不是运行中才抛异常)。
def get_day_of_year(year, month, day):
"""
这个函数可以求一个日期属于当年的第几天
:param year: 公元年份,如2021
:param month:
:param day:
:return: 第几天,如365
"""
assert year >= 0 and 1 <= month <= 12 and 1 <= day <= 31,\
"illegal input, need year >= 0 and 1 <= month <= 12 and 1 <= day <= 31"
m = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
if year % 400 == 0 or (year % 100 != 0 and year % 4 == 0):
m[1] = 29
day_of_year = sum(m[:(month-1)])+day
return day_of_year
这里抛砖引玉,更多优秀函数编写可以查看python源码和第三方库。
2.局部变量和全局变量
函数内部定义的变量属于局部变量,作用范围仅限函数内部。全局变量定在在函数外,作用范围包括函数内外部。
def func():
# 如果要修改全局的g,需要Global g,否则,不会影响全局变量
g = -10
print('局部g的值是:', g)
g = 10
func()
print('全局g的值是:', g)
# 输出:局部g的值是: -10
# 全局g的值是: 10
3.递归
函数里调用本函数,如利用递归生成斐波拉楔数列(前两项相加得第三个数)。递归最重要的一点就是要有结束条件。
def func(n):
if n == 1:
return 1
elif n == 2:
return 2
else:
return func(n-1) + func(n-2)
4.函数闭包
闭包,是指内部函数使用了外部函数得变量。当外部函数执行完,普通变量会消失,但该被使用的变量会存在。
def out():
p = '我是一个外部变量'
def inner():
print(p)
return inner
# p是out函数的变量,当执行完out(),p应该随之消失
r = out()
# 通过输出可以看到,变量p仍然存在
r()
# 输出:我是一个外部变量
再看另一个例子,内部函数使用了外部函数的变量i。i虽然在for循环中由0-1-2,但闭包的性质决定了,fs列表中的三个内部函数引用的的i的值是其最后一个时刻的值,即i=2。因此,输出都是2
def out():
fs = []
# 调用my_func时,内部函数func还没有被执行
# 闭包的性质决定了,内部函数会保留外部函数变量的最后一个时刻的值
for i in range(3):
def inner():
return i
# 如果这里改成fs.append(inner()),那就不是闭包问题了
fs.append(inner)
return fs
f1, f2, f3 = out()
print(f1())
print(f2())
print(f3())
# 输出:2
# 2
# 2
5.装饰器
装饰器也是利用了闭包的原理,好处是无需修改基本功能函数,只需对原函数进行一层“装饰”,即可扩展其功能。比如我们有个函数是origin,之前设计时没考虑权限问题,现在要求登陆才可以访问。我们就可以在不修改原函数的情况下,通过语法糖@套一层登陆验证。
def login_require(func):
def wrapper(username, psw):
if username == 'root' and psw == '123456789':
print('通过认证')
print('执行其他功能')
return func()
else:
print('用户名或密码错误')
return
return wrapper
@login_require
def origin():
print('登陆成功才能执行的内容')
origin('root', '123456789')
# 输出:通过认证
# 执行其他功能
# 登陆成功才能执行的内容
6.匿名函数
当你想实现一个简单的函数却又不想为其命名时,就可以使用lambda
f = lambda x: x**3
print('2的3次方是:', f(2))
# 输出:2的3次方是: 8
一般lambda函数都是作为其他函数的参数使用的,比如排序
# 先根据条件1排序,相等时再根据条件2排序
lst = [['lucy', 20], ['lily', 20], ['adam', 18], ['tom', 23], ['tim', 19]]
lst.sort(key=lambda x: (x[1], x[0]))
print(lst)
# 输出:[['adam', 18], ['tim', 19], ['lily', 20], ['lucy', 20], ['tom', 23]]