我们学习编程的时候,一学到函数就感觉很难,其实函数很简单,听我给你细细道来,在我们之前的学习中,我们最常用的一个操作,打印输出print()
,其实这就是我们最先接触的函数,只不过这是由Python源码中编写好的函数,那我们来看下print()函数到底是怎么写的?
从中我们可以看到用到def关键字,然后接一个print还有一个括号并且里边有内容,这样我们就可以使用print打印输出这个功能了,下边我们详细介绍函数
函数是组织好的,可重复使用的,用来实现单一、或相关联功能的代码段。这句话怎么理解,比如我们要比较2和3的大小,我们可以直接写
num1 = 2
num2 = 3
if num1 < num2:
print("2小于3")
else:
print("2大于3")
那如何我们下次又要比较5和6的大小呢,把代码可以在写一遍,如果下次再比较10和11的大小呢,这时候就可以用到函数
我们一般使用 def
关键词作为声明,后面紧接着是函数标识符名称与圆括号 ()
,最后接一个冒号 :
def 函数名(参数列表):
# 函数体
return [返回值]
函数体
必须有缩进,在函数体我们编写要实现的功能逻辑
熟悉招式后,我们就可以练习,在我们上边还有一个问题,比较两个数的大小,可能有很多两个数,我们用函数实现
# 定义比较两个数字的函数
def compare_numbers(num1, num2):
# 如果num1小于num2
if num1 < num2:
# 打印num1小于num2
print("{}小于{}".format(num1, num2))
# 否则
else:
# 打印num1大于num2
print("{}大于{}".format(num1, num2))
这样我们就写好了这个函数
调用函数是什么意思,我们不是把函数都写好了吗,我们来执行上边的代码,比较两个数大小的函数
发现执行结果为空
现在懂了吧,函数相当于我们的工具,可以是扳手,也可以是螺丝刀,如果没有手使用,就会毫无效果
所以我们得调用函数,才能使用函数的功能,来看下调用:
语法格式如下所示:
函数名(参数) # 第一种调用方式,如果有参数就填写对应参数,无参数可以不写
返回值 = 函数名(参数) # 第二种调用方式,如果函数有返回值,得变量来接收该值
括号里传的参数需要额外注意,定义函数的时候有几个参数,调用的时候就传几个参数,并且要保持顺序
在我们小栗子中,我们来调用一下
compare_numbers(2, 3)
compare_numbers(6, 5)
compare_numbers(10, 11)
# 执行结果
2小于3
6大于5
10小于11
看,这样以后我们不管在比较多少次两个数的大小,就不需要在编写重复的代码了
在我们实际编程工作中,如果不是单一功能的代码,也可以不用使用函数,这个要看情况使用
函数中有形参和实参,我们拿一个具体例子,传入一个数字,返回这个数字的2倍数字
def toal(num):
return num * 2
在我们定义的函数中,num就是我们的形参,相当于放个位置,不做实际使用
我们调用上边的函数
toal(5)
我们调用函数,括号中实际传入5,这就是我们的实参,函数实际会拿实参来进行逻辑处理
好,我们理解了形参和实参后,就来看下我们的值传递和引用传递:
那值传递和引用传递有什么区别啊?
值传递,形参的值发生改变,不影响实参的值 引用传递,改变形参的值,实参的值也会一同改变
我们来做个例子参考下:
# 定义了一个函数
def change(a):
a += 100
print("函数内:", a)
a = 100
change(a)
print("函数外:", a)
猜猜看,函数内的值是多少,函数外的值是多少
执行结果:
函数内: 200
函数外: 100
大家猜的准吗
这个就属于值传递,虽然形参里的改变了,但是实际我们定义的实际参数值虽然在函数中进行了改变,但是实际的值是不会改变的
当然这种适用于数字,字符串,元祖等不可变类型
那引用传递是什么呢?
# 函数的引用传递
def f(a):
a[0] = 100
print('函数内:', a)
a = [1, 2, 3, 4]
f(a)
print('函数外:', a)
执行结果:
函数内: [100, 2, 3, 4]
函数外: [100, 2, 3, 4]
这下大家应该都猜对了吧,引用传递,传递给函数参数是实际引用地址,修改形参中的值后,引用地址就会改变,所以传递给实参的值也会进行改变
位置参数,也叫必传参数,顾名思义,参数是必须要传入的,并且还要按照位置顺序传入,如果没有按照上边要求,会报错或者得到结果不一致
直接看例子
def hello(name, message):
print(f"{name}! {message}")
传入1个参数
hello("小华")
# 执行结果
TypeError: hello() missing 1 required positional argument: 'message'
传入3个参数
hello("小华", '你好', '哈哈')
# 执行结果
TypeError: hello() takes 2 positional arguments but 3 were given
传入2个参数
hello("小华", '你好')
hello("你好", '小华')
# 执行结果
小华! 你好
你好! 小华
所以,在调用函数时,一定要确定好位置
默认参数,函数定义时,如果给某个参数提供一个默认值,这个参数就变成了默认参数
直接看代码:
def hello(name='小华', message):
pass
def hello(message,name='小华'):
pass
上述两个代码有问题吗,在编辑器中发现第一个函数报错了
原因就在默认参数必须在位置参数后面!
那默认值参数有什么用呢?
默认值参数表示这个参数就算不给传参,也有一个默认值,不会报错
def test1(a, b=1):
print(a, b)
test1(0)
test1(0, 2)
执行结果
0 1
0 2
代码是没问题的
Python编程题
# 默认参数传空列表
def my_function(a=[]):
a.append('A')
print(a)
my_function()
my_function()
my_function()
不实际运行代码,答案是什么?
那不很简单吗
['A']
['A']
['A']
如果是这个答案,肯定错误
真正的答案是:
['A']
['A', 'A']
['A', 'A', 'A']
默认参数传入空列表,在我们函数的引用传递中我们知道,如果参数传入的是列表,表示传入的参数的引用地址,而后边列表改变了,默认参数表示这个默认值也对应改变了,所以调用一次函数后续再次调用这个函数的参数的默认值就会改变
可变参数也叫动态参数,为什么有可变参数呢
比如我们有求2个数的求和函数
def add(a, b):
return a + b
之后又需要3个数的求和函数
def add(a, b,c):
return a + b + c
那之后,我们要求100个数的和,应该怎么做呢
这时候就用到了我们的动态参数
Python的动态参数有两种,分别是*args
和**kwargs
,这里面的关键是一个和两个星号的区别
至于叫*a或者*as是没有区别的,只不过Python官方默认让我们使用这个*args
和**kwargs
定义中使用星号 *
来表示。它允许函数接受任意数量的位置参数,并将它们作为一个元组传递给函数
# 函数可变参数
def my_func(*args):
for arg in args:
print(arg)
my_func('hello', 'world', 123)
打印结果
hello
world
123
如果传入的是一个列表呢
def my_func(*args):
for arg in args:
print(arg)
my_func([1, 2, 3, 4])
猜猜打印的结果是什么
正确答案:[1, 2, 3, 4]
为什么不是1,2,3,4分别打印出来呢,因为当传入的参数为列表,是作为一个整体传入的,那接受会用一个元祖接受就是args = ([1, 2, 3, 4],)
,遍历元祖整个列表表示为1个元素会打印输出
那如果我们想要1,2,3,4作为单独的元素一个个传入给参数呢,我们可以用解包操作符 *
来将列表的元素作为独立的参数传递给函数
my_func(*[1, 2, 3, 4])
再来看看打印结果
1
2
3
4
如果可变参数和位置参数和默认参数联合使用呢,看下边这个例子:
def myfun(a, *b, c=None):
print(a)
print(b)
print(c)
myfun(1, 2, 3, 4)
猜猜a,b,c各自的值,执行结果:
1
(2, 3, 4)
None
a拿到了1,b作为可变参数,把后边的值都接受了,所以c没有拿到值
所以,我们如果想要给c赋值,我们就要指定参数值,这就用到了我们的关键字参数
我们在调用函数时,可以以 “参数名 = 参数值” 的形式传递参数,这种我们可以认为强行赋值,不需要传递,所以不受位置影响,还是上边的例子,我们要给c赋值,这时候我们就用关键字参数
def myfun(a, *b, c=None):
print(a)
print(b)
print(c)
myfun(1, 2, 3, c=4)
# 执行结果
1
(2, 3)
4
那如果我们把给c赋值放在最前面可以吗?
myfun(c=4, 2, 3)
# 执行结果
myfun(c=4, 2, 3)
^
SyntaxError: positional argument follows keyword argument
代码报错了,因为如果有关键字参数,调用的时候顺序必须放在最后面
myfun(2, 3,c=4)
定义中使用星号 **
来表示,它允许函数接受任意数量的键值对也就是关键字参数,并将它们作为一个字典传递给函数
# 函数可变参数
def my_func(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
my_func(name="小华", age=25, city="beijing")
打印结果
name: 小华
age: 25
city: beijing
这是第一种传递方式:key=value 的方式传参
如果我们传入整个字典,是否可以?
# 函数可变参数
def my_func(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
my_dict = {
"name": "小华",
"age": 25,
"city": "beijing"
}
my_func(my_dict)
执行结果:报错
这是因为,我们传入整个字典,属于位置参数传递方式,但是我们的函数没有任何位置参数,所以类型就会报错,如果我们想要以整个字典的方式传入,我们可以使用字典解包操作符 **
,这样就可以将字典中的键值对分别作为关键字参数传递给函数。
一个函数中包含多种参数的组合,必须遵守这样的顺序:位置参数(必传参数),默认参数,单星号参数,双星号参数
定义参数时,位置参数都必须在关键字参数之前
def my_func(a=1, b):
print(a, b)
# 这种写法就会报错,关键字参数必须放在位置参数后边
可变参数中,单星号要在双星号之前
def my_func(a, b, c=0, *args, **kwargs):
print(a, b, c, args, kwargs)
my_func(1, 2, 3, 4, 5, x=9, y=10)
my_func(1, 2, x=9, y=10)
my_func(1, 2, 3, (4, 5), a=9, b=10)
my_func(1, 2, 3, x=9, y=10, z=11)
my_func(1, 2, 3, x=9, y=10, z=11, a=12)
不运行代码,猜猜结果
还有一个小细节,单星号后面的都是关键字参数,无论是否带=;
def my_func(a, b, c=0, *args, d , **kwargs):
print(a, b, c, args,d, kwargs)
上述中的d在*args后面,因此属于关键字参数,虽然没有带=
Python函数,可以用 return 语句指定应该返回的值,该返回值可以是任意类型 语法格式:return [返回值]
def add(a, b):
return a + b
print(a + b)
add(3, 4)
上述一个求和的代码,我们执行后会返回什么,试一试,发现返回的是啥也没有,为什么呢?我们的代码里不是有打印a+b吗,是的,但是执行到return的时候,就直接跳出函数了,所以return 语句会提前退出函数
那我们想看下我们返回的数据应该怎么办,有返回值的时候,我们可以将函数赋值给一个变量,变量保存函数的返回值,然后打印输出
num = add(3, 4)
print(num)
当然我们如果不需要返回,也可以不写return,这样就会默认返回None的
def add(a, b):
a + b
num = add(3, 4)
print(num)
None
是一个特殊的常量,表示空或缺失,和 False 不同,它不表示 0,也不表示空字符串,而表示没有值,也就是空值。
None 常用于 assert、判断以及函数无返回值的情况
我们可以使用return返回多个值
def add(a, b):
return a + b, a - b
上述函数返回两个数的和,和两个数的差,我们打印看一下:
print(add(5, 3))
# 执行结果
(8, 2)
return 多个返回值,得到的是一个元组,后续我们可以根据元祖取值
这里,我们也可以分别接受一下对应的值
he, cha = add(5, 3)
print('和:', he)
print('差:', cha)
初级:
1.定义函数名add,接受两个数字参数,求和返回值
2.定义函数名Area,参数接受r半径的值,求圆的面积,(计算公式:π*r*r)
3.定义函数名season,接受一个月份参数,返回其对应的季节(春夏秋冬)
4.定义函数名reverse,接受一个字符串参数,并返回逆序后的字符串(比如传入abcdef,返回fedcba)
中级:
1.编写一个函数 find_max(numbers)
,接受一个整数列表 numbers,并返回列表中的最大值
2.编写一个函数 is_prime(n)
,判断一个正整数 n 是否为素数(质数)
3.编写一个函数 remove_list(numbers)
,接受一个整数列表 numbers,并移除列表中的重复元素,返回去重后的列表
高级
1.请写一个函数 equals ,该函数参数为任意数量的数字,请在函数中统计出这些参数数字中重复的数字有多少个
比如 :
equals(3, 4, 3, 4, 1, 6, 2)
输出为:
数字 3 出现了 2 次
数字 4 出现了 2 次
数字 1 出现了 1 次
数字 6 出现了 1 次
数字 2 出现了 1 次