一个规范的值得借鉴的Python程序,除非代码量很少(比如 10 行、20 行以下),基本都应该由多个函数组成,这样的代码才更加模块化、规范化

函数是Python程序中不可或缺的一部分
事实上,在前面的学习中已经用到了很多Python的内置函数,比如sorted()表示对一个集合序列排序,len()表示返回一个集合序列的长度大小等等

接下来学习一下Python的自定义函数

一、函数基础

那么,到底什么是函数?如何在Python程序中定义函数呢?

1.1 函数的定义

说白了,函数就是为了实现某一功能的代码段,只要写好以后就可以重复利用。先来看下面一个简单的例子

def my_func(message):print('Got a message: {}'.format(message))# 调用函数 my_func()
my_func('Hello World')
# 输出
Got a message: Hello World

其中:

  • def是函数的声明
  • my_func是函数的名称
  • 括号里面的message则是函数的参数
  • 而print那行则是函数的主体部分,可以执行相应的语句
  • 在函数最后可以返回调用结果(returnyield),也可以不返回

总结一下,大概是下面的这种形式:

def name(param1, param2, ..., paramN):statementsreturn/yield value # optional

和其他需要编译的语言(比如C语言)不一样的是,def是可执行语句,这意味着函数直到被调用前,都是不存在的。当程序调用函数时,def语句才会创建一个新的函数对象,并赋予其名字

一起来看几个例子,加深对函数的印象:

def my_sum(a, b):return a + bresult = my_sum(3, 5)
print(result)# 输出
8

上面代码处理的步骤:

  1. 定义了my_sum()这个函数,它有两个参数a和b,作用是相加
  2. 调用my_sum()函数,分别把3和5赋于a和b
  3. 返回其相加的值赋于变量result,并输出得到8

1.2 函数的执行

再来看一个例子:

def find_largest_element(l):if not isinstance(l, list):print('input is not type of list')returnif len(l) == 0:print('empty input')returnlargest_element = l[0]for item in l:if item > largest_element:largest_element = itemprint('largest element is: {}'.format(largest_element)) find_largest_element([8, 1,-3, 2, 0])# 输出
largest element is: 8

这个例子中,定义了函数find_largest_element,作用是遍历输入的列表,找出最大的值并打印。因此,当调用它并传递列表 [8, 1, -3, 2, 0] 作为参数时,程序就会输出largest element is: 8

需要注意,主程序调用函数时必须保证这个函数此前已经定义过,不然就会报错,比如:

my_func('hello world')
def my_func(message):print('Got a message: {}'.format(message))# 输出
NameError: name 'my_func' is not defined

但是,如果在函数内部调用其他函数,函数间哪个声明在前、哪个在后就无所谓,因为def是可执行语句,函数在调用之前都不存在,只需保证调用时所需的函数都已经声明定义:

def my_func(message):my_sub_func(message) # 调用my_sub_func()在其声明之前不影响程序执行def my_sub_func(message):print('Got a message: {}'.format(message))my_func('hello world')# 输出
Got a message: hello world

1.3 参数默认值

Python 函数的参数可以设定默认值,比如下面这样的写法:

def func(param = 0):...

这样,在调用函数func()时,如果参数param没有传入,则参数默认为0,而如果传入了参数 param,其就会覆盖默认值

1.3 参数类型

Python和其他语言相比的一大特点是,Python是dynamically typed的,可以接受任何数据类型(整型,浮点,字符串等等)
对函数参数来说这一点同样适用,比如还是刚刚的my_sum函数,可以把列表作为参数来传递,表示将两个列表相连接:

print(my_sum([1, 2], [3, 4]))# 输出
[1, 2, 3, 4]

同样,也可以把字符串作为参数传递,表示字符串的合并拼接:

print(my_sum('hello ', 'world'))# 输出
hello world

当然,如果两个参数的数据类型不同,比如一个是列表、一个是字符串,两者无法相加,那就会报错:

print(my_sum([1, 2], 'hello'))
TypeError: can only concatenate list (not "str") to list

可以看到,Python不用考虑输入的数据类型,而是将其交给具体的代码去判断执行,同样的一个函数(比如相加函数my_sum()),可以同时应用在整型、列表、字符串等等的操作中

1.4 多态

在编程语言中,把这种行为称为多态
这也是Python和其他语言,比如Java、C等很大的一个不同点。当然,Python这种方便的特性,在实际使用中也会带来诸多问题。因此,必要时需要在开头加上数据的类型检查

1.5 函数嵌套

Python函数的另一大特性,是Python支持函数的嵌套。所谓的函数嵌套,就是指函数里面又有函数,比如:

def f1():print('hello')def f2():print('world')f2()
f1()# 输出
hello
world

这里函数f1()的内部,又定义了函数f2(),执行逻辑是在调用函数f1()时先打印字符串hello,然后f1()内部再调用f2()打印字符串world。那为什么需要函数嵌套?这样做有什么好处呢?

其实,函数的嵌套,主要有下面两个方面的作用

  • 第一,函数的嵌套能够保证内部函数的隐私

内部函数只能被外部函数所调用和访问,不会暴露在全局作用域。因此,如果函数内部有一些隐私数据(比如数据库的用户、密码等)不想暴露在外,那可以使用函数的的嵌套将其封装在内部函数中,只通过外部函数来访问。比如:

def connect_DB():def get_DB_configuration():...return host, username, passwordconn = connector.connect(get_DB_configuration())return conn

这里的函数get_DB_configuration便是内部函数,它无法在connect_DB()函数以外被单独调用。也就是说,下面这样的外部直接调用是错误的:

get_DB_configuration()# 输出
NameError: name 'get_DB_configuration' is not defined

只能通过调用外部函数connect_DB()来访问它,这样一来,程序的安全性便有了很大的提高

  • 第二,合理的使用函数嵌套,能够提高程序的运行效率

来看下面这个例子:

def factorial(input):# validation checkif not isinstance(input, int):raise Exception('input must be an integer.')if input < 0:raise Exception('input must be greater or equal to 0' )...def inner_factorial(input):if input <= 1:return 1return input * inner_factorial(input-1)return inner_factorial(input)print(factorial(5))

这里,使用递归的方式计算一个数的阶乘。因为在计算之前,需要检查输入是否合法,所以写成了函数嵌套的形式,这样一来输入是否合法就只用检查一次
而如果我们不使用函数嵌套,那么每调用一次递归便会检查一次,这是没有必要的,也会降低程序的运行效率

实际工作中,如果遇到相似的情况,输入检查不是很快且还会耗费一定的资源,那么运用函数的嵌套就十分必要

二、函数变量作用域

Python函数中变量的作用域和其他语言类似

2.1 局部变量

如果变量是在函数内部定义的,就称为局部变量,只在函数内部有效。一旦函数执行完毕,局部变量就会被回收,无法访问,比如下面的例子:

def read_text_from_file(file_path):with open(file_path) as file:...

在函数内部定义了file这个变量,这个变量只在read_text_from_file这个函数里有效,在函数外部则无法访问

2.2 全局变量

相对应的,全局变量则是定义在整个文件层次上的,比如下面这段代码:

MIN_VALUE = 1
MAX_VALUE = 10
def validation_check(value):if value < MIN_VALUE or value > MAX_VALUE:raise Exception('validation check fails')

如果运行这段代码,程序便会报错:

UnboundLocalError: local variable 'MIN_VALUE' referenced before assignment

2.3 函数内部使用全局变量

这是因为,Python的解释器会默认函数内部的变量为局部变量,但是又发现局部变量MIN_VALUE并没有声明,因此就无法执行相关操作。所以,如果一定要在函数内部改变全局变量的值,就必须加上global这个声明:

MIN_VALUE = 1
MAX_VALUE = 10
def validation_check(value):global MIN_VALUE...MIN_VALUE += 1...
validation_check(5)

这里的global关键字,并不表示重新创建了一个全局变量MIN_VALUE,而是告诉Python解释器,函数内部的变量MIN_VALUE就是之前定义的全局变量,并不是新的全局变量,也不是局部变量。这样,程序就可以在函数内部访问全局变量并修改它的值

2.4 局部变量和全局变量同名

另外,如果遇到函数内部局部变量和全局变量同名的情况,那么在函数内部,局部变量会覆盖全局变量,比如下面这种:

MIN_VALUE = 1
MAX_VALUE = 10
def validation_check(value):MIN_VALUE = 3...

在函数validation_check()内部,定义了和全局变量同名的局部变量MIN_VALUE,那么,MIN_VALUE 在函数内部的值就应该是3而不是1

2.5 嵌套函数内部函数使用外部函数变量

类似的,对于嵌套函数来说,内部函数可以访问外部函数定义的变量,但是无法修改,若要修改,必须加上nonlocal这个关键字:

def outer():x = "local"def inner():nonlocal x # nonlocal关键字表示这里的x就是外部函数outer定义的变量xx = 'nonlocal'print("inner:", x)inner()print("outer:", x)
outer()
# 输出
inner: nonlocal
outer: nonlocal

如果不加上nonlocal这个关键字,而内部函数的变量又和外部函数变量同名,那么同样的,内部函数变量会覆盖外部函数的变量

def outer():x = "local"def inner():x = 'nonlocal' # 这里的x是inner这个函数的局部变量print("inner:", x)inner()print("outer:", x)
outer()
# 输出
inner: nonlocal
outer: local

三、闭包

第三个重点,介绍一下闭包(closure)
闭包其实和刚刚讲的嵌套函数类似,不同的是,这里外部函数返回的是一个函数而不是一个具体的值。返回的函数通常赋于一个变量,这个变量可以在后面被继续执行调用

3.1 闭包的定义

举个例子更容易理解一些。比如,想计算一个数的n次幂,用闭包可以写成下面的代码:

def nth_power(exponent):def exponent_of(base):return base ** exponentreturn exponent_of # 返回值是exponent_of函数square = nth_power(2) # 计算一个数的平方
cube = nth_power(3) # 计算一个数的立方
square
# 输出
<function __main__.nth_power.<locals>.exponent(base)>cube
# 输出
<function __main__.nth_power.<locals>.exponent(base)>print(square(2))  # 计算2的平方
print(cube(2)) # 计算2的立方
# 输出
4 # 2^2
8 # 2^3

这里外部函数nth_power()返回值,是函数exponent_of(),而不是一个具体的数值
需要注意的是,在执行完square = nth_power(2)cube = nth_power(3)后,外部函数nth_power()的参数exponent,仍然会被内部函数exponent_of()记住。这样,之后调用square(2)或者cube(2)时,程序就能顺利地输出结果,而不会报错说参数exponent没有定义

3.2 闭包的使用场景

看到这里,也许会疑问为什么要闭包呢?上面的程序,也可以写成下面的形式

def nth_power_rewrite(base, exponent):return base ** exponent

确实可以,不过要知道,使用闭包的一个原因是让程序变得更简洁易读。设想一下,比如需要计算很多个数的平方,那么写成下面哪一种形式更好呢?

# 不适用闭包
res1 = nth_power_rewrite(base1, 2)
res2 = nth_power_rewrite(base2, 2)
res3 = nth_power_rewrite(base3, 2)
...# 使用闭包
square = nth_power(2)
res1 = square(base1)
res2 = square(base2)
res3 = square(base3)
...

显然是第二种,是不是?首先直观来看,第二种形式每次调用函数都可以少输入一个参数,表达更为简洁

其次,和上面讲到的嵌套函数优点类似,函数开头需要做一些额外工作,而又需要多次调用这个函数时,将那些额外工作的代码放在外部函数,就可以减少多次调用导致的不必要的开销,提高程序的运行效率

另外还有一点,闭包常常和装饰器(decorator)一起使用

四、总结

学习了Python函数的概念及其应用,有几点需要注意:

  • Python中函数的参数可以接受任意的数据类型,使用起来需要注意,必要时请在函数开头加入数据类型的检查
  • 和其他语言不同,Python中函数的参数可以设定默认值
  • 嵌套函数的使用,能保证数据的隐私性,提高程序运行效率
  • 合理地使用闭包,则可以简化程序的复杂度,提高可读性

【Python核心】不可或缺的自定义函数相关推荐

  1. python学习笔记之自定义函数

    live long and prosper 自定义函数 def greet_user():"""现实简单的问候语"""print(" ...

  2. Python爬虫笔记——def()自定义函数的几种参数

    Python自定义函数是以def开头,空一格之后是这个自定义函数的名称,名称后面是一对括号,括号里放置形参列表,结束括号后面一定要有冒号":",函数的执行体程序代码也要有适当的缩排 ...

  3. 洗礼灵魂,修炼python(21)--自定义函数(2)—函数文档,doctest模块,形参,实参,默认参数,关键字参数,收集参数,位置参数...

    函数文档 1.什么是函数文档: 就是放在函数体之前的一段说明,其本身是一段字符串,一个完整的函数需要带有函数文档,这样利于他人阅读,方便理解此函数的作用,能做什么运算 2.怎么查看函数文档: func ...

  4. 54.Python的def语句自定义函数

    54.def语句自定义函数 文章目录 54.def语句自定义函数 1.课题导入-数学中的函数 1.1 计算圆的面积 1.2 计算不规则图形的面积 2. 什么是函数 3. 函数分类 3.1 内置函数 3 ...

  5. python中平均值函数_python自定义函数ma(x,y)求简单平均值输出结果到列表

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 def ma(x,y): ''' # 自定义函数"ma(x,y)"指南 函数格式:ma(x,y) 函数功能:求序列数据x的y周期的简单 ...

  6. python基础教程: 自定义函数

    多态 我们可以看到,Python 不用考虑输入的数据类型,而是将其交给具体的代码去判断执行,同样的一个函数(比如这边的相加函数 my_sum()),可以同时应用在整型.列表.字符串等等的操作中. 在编 ...

  7. python核心,内建函数,高阶函数

    晨测 global和nonlocal区别 写一个递归的阶乘 回顾 1.global和nonlocal 关键字 2.函数的递归 1.查找规律 2.设置退出条件 3.性能 3.闭包 外函数中定义一个内函数 ...

  8. 09 | 不可或缺的自定义函数

    1.函数基础 函数是程序中不可或缺的一部分,之前也已经学了很多python的内置函数. def my_func(message):print('Got a message: {}'.format(me ...

  9. python核心教程:max函数怎么使用

    max函数可以用于返回多个数字中最大的那个值,如果没有传递参数,则结果为"-Infinity",如果至少有一个参数无法转换为数字,则结果为NaN.下面我们就来看看max函数的具体使 ...

最新文章

  1. json数据在前端(javascript)和后端(php)转换
  2. 禁用当前的账户win7_拯救你的win7系统,电脑优化到位,打游戏才会流畅
  3. 谁都可能是凶手:《八面埋伏》观看手记
  4. NO.1_python_scrapy组成爬取多页数据连接数据库配置文件书写
  5. paip.c3p0 nullpointexcept 配置文件根路径读取bug 解决
  6. 如何让Ubuntu联网
  7. 电脑安装有道后打开word文档很慢
  8. Qt无边框窗体实现方案
  9. 考研复习--高等数学
  10. Matlab数学建模(八):评价型模型
  11. 论大数据时代下的海量数据存储技术
  12. nginx+keepalive实现高可用负载均衡
  13. 将SkeyeVSS综合安防监控视频流媒体云平台监控画面嵌入微信公众号进行直播
  14. O2O、C2C、B2B、B2C、F2C的区别在哪里?
  15. MySQL 数据(字段)类型
  16. 谈谈 SAP iRPA Studio 创建的本地项目的云端部署问题
  17. 2022年校招互联网大厂薪酬状况如何?“白菜”总包接近40W是真是假?
  18. 基于stc15f2k60s2芯片单片机编程(闹铃)
  19. Libtorch的介绍与使用方法
  20. atom linux64下载,ATOM下载

热门文章

  1. Unity Shader——夜晚视觉屏幕特效(night vision Screen Effect)
  2. 基于人脸识别的课堂签到管理系统2020,7,19
  3. NISP和CISP中渗透测试的思路是什么?
  4. 使用foreach标签遍历数组
  5. 基于MSP430G2231实现的频率计
  6. 基于ffmpeg实现音视频转码
  7. .NET Framework4.5 .NET Framework4.0
  8. 复制Excel模板,填写Excel模板,然后下载
  9. 指挥调度系统内部进行视频会议的方法
  10. matlab 比较矩阵差异,Matlab矩阵操作