文章目录
- Python基础 P6 函数
- 函数基础
- 创建和调用
- 函数的参数
- 函数的返回值
- 函数进阶
- 函数文档
- 作用域
- 内嵌函数
- global和nonlocal
- lambda
- filter
- map
- 递归
- 递归基础
- 斐波那契数列
- 进阶练习
- 更多内容
Python基础 P6 函数
函数基础
随着编写的代码量不断增加,结构也日益复杂
编程大师Martin Fowler先生曾经说过:“代码有很多种坏味道,重复是最坏的一种!”,要写出高质量的代码首先要解决的就是重复代码的问题
需要找一个方法对这些复杂的代码进行重新打包整理,以降低代码结构的复杂性和冗余度。优秀的东西永远是经典的,而经典的东西永远是简单的;不是说复杂不好,单只有把复杂的东西简单化才能成为经典。为了使得程序的代码变得简单,需要把程序分解成较小的组成部分,函数就应运而生
创建和调用
在python中创建一个函数用def关键字,通过使用函数名来调用函数
def fkt1():
print("Cage的一个函数")
fkt1()
函数的调用和运行机制:
当函数fkt1()发生调用操作的时候,Python会自动往上找到该函数的定义过程,然后依次执行该函数所包含的代码块部分内容。
创建一个空函数,可以用来当做占位符使用
举个栗子
def fkt1():
pass
fkt1()
这里的pass关键字就是一个空语句,表示不做任何事情
函数的参数
函数后面的括号是用来放参数的,在函数刚开始被发明出来的时候,是没有参数的,很快就发现如果是这样函数不过是对同样内容的代码进行打包,这样与使用循环就没有什么本质不同了。因此,为了使每次调用的函数可以有不同的实现,加入了参数的概念。
def fkt1(name, num):
print(name, "的第", num, "个函数")
fkt1("Cage", 1)
fkt1("Cage", 2)
在函数中,参数与其他编程语言一样,也分为形式参数和实际参数,可以简称形参和实参
形参:形式参数就是函数定义的时候写的参数,就是上面例子定义fkt1的name和num
实参:实际参数是在调佣函数的时候传递进去的值,就是上面例子传入的Cage与1和Cage与2
但在Python中函数也有许多与其他编程语言不一样的地方
在Python中,参数的函数可以是默认值,也可以使用可变参数
举个栗子
def fkt1(name='Cage', num=1):
print(name, "的第", num, "个函数")
def fkt2(*num):
num_sum = 0
for val in num:
num_sum += val
return print(num_sum)
fkt1()
fkt1("David", 2)
fkt2(1, 2, 3)
fkt2(1, 3, 5, 7, 9)
位置参数
在函数被定义的时候,就已经把参数的名字以及位置确定下来,这类位置固定的参数叫做位置参数
举个栗子
def fkt1(name1, name2):
print(name2, '打了', name1)
fkt1('David', 'Cage')
可以看到,实参会根据形参的位置一一对应赋值
关键字参数
关键字参数就是在实参赋值时明确赋值的形参名称
举个栗子
def fkt1(name1, name2):
print(name2, '打了', name1)
fkt1(name2='Cage', name1='David')
位置参数专用符号/ ,在对应的形参后面加入/,前面的形参就是位置参数专用
关键字参数专用符号* ,在对应的形参前面加入* , 后面的形参就是关键字参数专用
举个栗子
def fkt1(name1, /, *, name2):
print(name2, '打了', name1)
fkt1('David', name2='Cage')
fkt1('David', 'Cage')
fkt1(name1='David', name2='Cage')
函数的返回值
有时候需要函数返回一些数据来显示函数的处理结果,可以使用return来实现
举个栗子
def fkt1(num1, num2):
sum1 = num1 + num2
return print(sum1)
fkt1(6, 7)
fkt1(15, 40)
函数进阶
有时候,评论一种编程语言是否优秀,往往是看它是否灵活
灵活并非意味着无所不能、无所不包,那样就会显得庞大和冗杂
灵活应该表现为多变,函数因参数而灵活
上面的基础内容已经介绍了许多Pyhton参数的特殊用法,现在再来看看其他灵活的表现
函数文档
给函数写文档是为了让后人可以更好地理解你的函数设计逻辑,对于一名优秀的程序员来说,养成编写函数文档的习惯无疑是非常必要的。因为在实际开发中,个人的工作量和能力确实相当有限,因此中、大型的程序永远都是团队来完成的。大家的代码要相互衔接,就需要先阅读别人提供的文档,因此适当的文档说明非常重要;而函数文档的作用是描述该函数的功能以及一些注意事项
先来看看官方的函数文档:
help(print)
这里可以看到函数的基本形式,函数功能和参数说明等函数的基本信息
举个栗子
def my_sum(num1, num2):
"""
my_sum(num1, num2)
功能: 计算两数相加的值
num1: 相加值1
num2: 相加值2
返回值: 返回两数相加值的结果并打印
"""
return print(num1 + num2)
my_sum(5, 9)help(my_sum)
这里定义的文档必须在函数内部的最上方,才能通过help来正常显示函数文档内容
作用域
作用域是程序运行时变量可被访问的范围,变量根据作用域的不同可以分为局部变量和全局变量
局部边量
定义在函数内部的变量是局部变量,局部变量的作用范围只能在函数的内部生效,它不能在函数外被引用
举个栗子
def fkt():
x = 10
print(x)
fkt()
print(x)
从输出结果可以看出,函数内部的x成功输出,而函数外面的x报错了,因为函数内部定义的x只有在函数内部才可以使用,函数外部的x会被认为是一个未被定义的变量
全局变量
与局部变量相对的是全局变量,在函数外面定义的对全局都有效的变量是全局变量,全局变量拥有更大的作用域
举个栗子
x = 50
def fkt():
x = 10
print(x)
fkt()
print(x)
LEGB原则
L-Local:函数内的名字空间
E-Enclosing function locals:嵌套函数中外部函数的名字空间
G-Global:函数定义所在模块的名字空间
B-Built-In:Python内置模块的名字空间
变量的查找顺序是从L->E->G->B
内嵌函数
python的函数定义是支持嵌套的,也就是允许在函数内部定义另一个函数,这种函数称为内嵌函数或者内部函数
def fkt1():
print("fkt1()正在被调用。。。")
def fkt2():
print("fkt2()正在被调用。。。")
fkt2()
fkt1()
闭包
闭包是函数式编程的一个重要的语法结构
python中的闭包从表现形式上定义为:如果在一个内部函数里,对在外部作用域但不是在全局作用域的变量进行引用,那么内部函数就被认为是闭包
def fkt1(x):
def fkt2(y):
return x * y
return fkt2
a = fkt1(8)
print(a(9))
装饰器
如果需要给函数增加额外的功能,那么就可以使用装饰器来实现
def log(func):
def wrapper(*params):
print("开始调用函数了。。。")
func(*params)
print("结束调用函数了。。。")
return wrapper
@log
def eat(name):
print(name, "开始吃了")
eat("Cage")
global和nonlocal
gloabal
全局变量的作用域是整个模块,也就是代码段内所有的函数内部都可以访问到全局变量,在python中,如果在函数中调用了全局变量它会默认在函数里面创建一个一模一样的局部变量来代替,而全局变量则不变,如果在局部函数依然想要使用全局变量并对其进行修改,可以使用global关键字来强调其实全局变量并进行修改
def fkt1():
a = 10
print("局部变量a=", a)
def fkt2():
global a
a = 30
print("在函数内的全局变量a=", a)
a = 20
fkt1()
print("全局变量a=", a)
fkt2()
print("全局变量a=", a)
nonlocal
一般情况下我们不能在嵌套函数的内部修改外部函数的变量值,但可以是用nonlocal语句进行修改
举个栗子
def fkt1():
x = 545
def fkt2():
nonlocal x
x = 676
print("In fkt2, x =", x)
fkt2()
print("In fkt1, x =", x)
fkt1()
上面的情况就是在fkt2中对fkt1的x进行了修改
lambda
可以使用lambda关键字来创建匿名函数,基本语法是使用冒号(:)分隔函数的参数及返回值:冒号的左边放置函数的参数,如果有多个参数,使用逗号(,)分隔即可:冒号右边是函数的返回值
a = lambda x, y: x * y
print(a(5, 10))
filter
filter()这个内置函数有两个参数
一:可以是一个函数,也可以是None
二:作为函数的参数进行遍历,如果一为None则输出真值
# 当参数一为None时
a = filter(None, [1, 0, False, True])
print(list(a))
def my_pow(x):
return x * x >= 16
# 正常版
b = filter(my_pow, range(10))
print(list(b))
# 最简式
print(list(filter(lambda x: x * x >= 16, range(10))))
map
map()这个内置函数也有两个参数,仍然是一个函数和一个可迭代对象,将可迭代对象的每个元素作为函数的参数进行运算加工,直到可迭代序列每个元素都加工完毕
def my_pow(x):
return x * x
# 正常版
b = map(my_pow, range(10))
print(list(b))
# 最简式
print(list(map(lambda x: x * x, range(10))))
递归
递归
从原理上来说就是函数调用自身的行为
要让递归正常工作,必须要有一个结束条件,并且每次调用都将向着这个结束条件推进
递归基础
求阶乘
# 递归求阶乘
def fkt1(n):
if n == 1:
return 1
else:
return n * fkt1(n-1)
number = int(input("请输入一个整数:"))
result = fkt1(number)
print(number, "的阶乘是:", result)
斐波那契数列
# 递归实现斐波那契数列
def fkt1(n):
if n < 1:
print("输入有误!")
return -1
elif n == 1 or n == 2:
return 1
else:
return fkt1(n-1) + fkt1(n-2)
number = int(input("请输入斐波那契数列的次数:"))
result = fkt1(number)
if number >= 1:
print(number, "次斐波那契数列后的结果是:", result)