Python语言之函数
Python语言之函数
前言
人生苦短,我用Python。
一、函数的定义和调用
什么是函数?
函数是组织好的,可重复使用的,用来实现单一或相关联功能的代码段,它能够提高应用的模块性和代码的重复利用率。
为什么需要函数
复用代码、隐藏实现细节、提高可维护性、提高可读性便于调试
函数的定义和调用
Python定义函数使用def关键字,格式如下:
def 函数名(参数列表):
函数体
例:
def printInfo():
print('-------------------------------------')
print(' 人生苦短,我用Python ')
print('-------------------------------------')
定义了函数之后,想要让这些代码能够执行,需要调用函数。通过“函数名()” 即可完成调用。
# 调用刚才定义的函数
printInfo()
二、函数的参数
函数的参数
如果希望定义的函数,可以计算任何两个数的和,我们在定义函数的时候,让函数接收数据,这就是函数的参数。
#定义接收两个参数的函数
def add2num(a, b):
c = a+b
print(c)
a和b就是函数的参数(形参),调用的时候,可以传入任意两个数(实参)。
#调用带有参数的函数
add2num(11, 22)
函数在被调用时会将实参按照相应的位置依次传递给形参,也就是说将第一个实参传递给第一个形参,将第二个实参传递给第二个形参,以此类推。
例:
def get_max(a, b):
if a > b:
print(a,"是较大的值!")
else:
print(b,"是较大的值!")
get_max(8, 5)
关键字参数的传递
关键字参数的传递是通过“形参=实参”的格式将实参与形参相关联,将实参按照相应的关键字传递给形参。
例:
def connect(ip, port):
print(f"设备{ip}:{port}连接!")
connect(ip="127.0.0.1", port=8080)
默认参数
调用函数时,如果没有传递参数,则会使用默认参数。
例:
def printinfo( name, age = 26 ):
print("Name:", name)
print("Age:", age)
printinfo(name="jack")
printinfo(name="jack",age=35)
定义函数时,带有默认值的参数一定要位于参数列表的最后面,否则程序会报错。
不定长参数
有时可能需要一个函数能处理比当初声明时更多的参数,这些参数叫做不定长参数,声明时不会命名。
def functionname([formal_args,] *args, **kwargs):
"函数_文档字符串"
function_suite
- 加了星号(*)的变量args会存放所有未命名的变量参数,args为元组;
- 加**的变量kwargs会存放命名参数,即形如key=value的参数, kwargs为字典。
代码举例
1.
def test(a,b,*args):
print(a)
print(b)
print(args)
test(11,22,33,44,55,66,77,88,99)
参数11传递给a,参数22传递给b,其余的未命名参数被放到了args(元组)中。
2.
def test(a,b,**kwargs):
print(a)
print(b)
print(kwargs)
test(11,22,name='hello',id='12345')
参数11传递给a,参数22传递给b,其余的命名参数(key=value)被放到了kwargs(字典)中。
3.
def test(a,b,**kwargs):
print(a)
print(b)
print(kwargs)
test(11,22,a=2,b=3)
一个形参只能接受一个实参,第一个实参11要传递给a,a=2中实参2也要传递给a,所以程序报错。
4.
def test(a, b, *args, **kwargs):
print(a)
print(b)
print(args)
print(kwargs)
test(1, 2, 3, 4, name = "hello", age = 20)
参数1传递给a,参数2传递给b,未命名参数放到args中,命名参数放到kwargs中。
5.
def fun(arg1,arg2):
print('arg1=',arg1)
print('arg2=', arg2)
arg1 = 100
arg2.append(10)
print('arg1=', arg1)
print('arg2=', arg2)
n1 = 11
n2 = [22,33,44]
print(n1)
print(n2)
fun(n1,n2) # 位置传参,实参名称与形参名称可以不一致
print(n1) # 11
print(n2) # [22, 33, 44, 10]
在函数调用过程中,进行参数的传递:
-
如果是不可变对象,在函数体的修改不会影响实参的值
-
如果是可变对象,在函数体的修改会影响到实参的值
三、函数的返回值
- 函数的返回值是使用return语句来完成的。
def add2num(a, b):
c = a+b
return c
函数add2num中包含return,意味着这个函数有一个返回值,其返回值就是a和b相加的结果。
- 函数返回多个值时,结果为元组。
def fun(num):
l1 = []
l2 = []
for i in num:
if i%2: # 0的布尔值为false,1的布尔值为true
l1.append(i)
else :
l2.append(i)
return l1,l2
print(fun([11,22,33,44,55]))
函数的返回值:
- 如果函数没有返回值(函数执行完毕后,不需要给调用处提供数据),return可以省略
- 函数的返回值,如果是一个,直接返回类型
- 函数的返回值,如果是多个,返回的结果为元组
四、函数的四种类型
根据函数的参数和返回值,函数可以分为四种类型:
-
无参数,无返回值的函数
-
无参数,有返回值的函数
-
有参数,无返回值的函数
-
有参数,有返回值的函数
无参数,无返回值的函数
def printMenu():
print('-------------------------')
print(' 菜单系统 ')
print(' 1.羊肉涮涮锅 ')
print(' 2.牛肉涮涮锅 ')
print(' 3.猪肉涮涮锅 ')
print('-------------------------')
printMenu()
无参数,有返回值的函数
# 获取温度
def getTemperature():
#这里是获取温度的一些处理过程
#为了简单起见,先模拟返回一个数据
return 24
temperature = getTemperature()
print('当前的温度为:',temperature)
有参数,无返回值的函数
该类函数使用情景较少
def test(num1,num2):
result=num1+num2
print('计算结果为:%d' %result)
有参数,有返回值的函数
像数据处理并需要结果的应用,可以使用此类函数。
def calculateNum(num):
result = 0
i = 1
while i <= num:
result = result + i
i += 1
return result
result = calculateNum(100)
print('1~100的累加和为:', result)
五、函数的嵌套
函数的嵌套调用:
def add_modify(a, b):
result = a + b
add() # 嵌套调用add函数
print(result)
add_modify(10, 20)
函数的嵌套定义:
def add_modify(a, b):
result = a + b
print(result)
def test():
print("我是内层函数")
add_modify(10, 20)
注:
-
函数外部无法直接调用内层函数
-
只能通过外层函数间接调用内层函数
六、变量的作用域
作用域
-
为了避免变量的名称发生冲突,所以Python引入了命名空间的概念。
-
命名空间指的是名称到对象的映射,类似于字典,键名是变量的名字,值是变量的值。
命名空间是相互独立存在的,而且它们被安排在某个特定的层次,把这些层次结构组合起来就是作用域。
-
本地作用域:函数体中声明的变量
-
函数嵌套作用域:嵌套函数中,外层函数体声明的变量。
-
文件作用域:py文件中声明的变量
-
内置作用域:python环境的作用域,主要是一些内置的函数名、内置的异常等关键字。例如open,range,quit等。
局部变量
-
所谓局部变量,就是在函数内部定义的变量。
-
局部变量的作用域是函数内部,意味着它只在定义它的函数中有效,一旦函数结束就会消失。
全局变量
-
定义在函数外的拥有全局作用域。
-
全局变量可以在整个程序范围内访问。
-
如果出现全局变量和局部变量名字相同的情况,则在函数中访问的是局部变量。
例:
a = 1
def test():
a = 10
print(f"局部变量a:{a}")
test()
print(f"全局变量a:{a}")
global和nonlocal关键字
- 修改函数中变量时,函数中使用global关键字用于声明变量为全局变量。如果不使用global关键字,变量可读但不可修改。
a = 1
def test():
global a
a = 10
print(f"声明变量a为全局变量:{a}")
test()
print(f"全局变量a:{a}")
- 修改闭包函数中变量时,使用nonlocal关键字声明变量为外层函数的变量。如果不使用nonlocal关键字,外层函数变量可读但不可修改。
def test():
b = 2
c = 3
def test2():
b = 20
def test3():
nonlocal c
c = 30
test2()
test3()
print(b)
print(c)
test()
闭包
Python函数是支持嵌套的。如果在一个内部函数中对外部函数作用域(非全局作用域)的变量进行引用,那么内部函数就会被称为闭包。闭包需要满足如下3个条件:
-
存在于两个嵌套关系的函数中,并且闭包是内部函数
-
内部函数引用了外部函数的变量(自由变量)
-
外部函数会把内部函数的函数名称返回(不带括号)
例:
def nth_power(exponent):
def exponent_of(base):
return base ** exponent
return exponent_of # 返回值是 exponent_of 函数
square = nth_power(2) # 计算一个数的平方,传递exponent为2
cube = nth_power(3) # 计算一个数的立方,传递exponent为3
print(square(2)) # 计算 2 的平方,传递base为2
print(cube(2)) # 计算 2 的立方,传递base为2
print(nth_power(2)(4))# 直接计算 2 的4次方
为什么使用闭包?
-
由于闭包用作回调函数,因此它们提供了某种数据隐藏,这有助于我们减少使用全局变量。
-
当我们的代码中没有几个函数时,闭包被证明是一种有效的方法,但是如果我们需要很多功能,那就需要使用类(OOP)。
七、递归函数和匿名函数
递归函数
一个函数的内部可以调用其他函数。但是,如果一个函数在内部不调用其它的函数,而是调用自己本身的话,这个函数就是递归函数。
递归函数的组成部分:递归调用与递归终止条件。
递归得调用过程:每递归调用一次函数,都会在栈内分配一个栈帧;每执行完一次函数,都会释放相应的空间。
递归的优缺点:占用内存多,效率低下;思路和代码简单。
例:
# 求阶乘
def fn(n):
if n==1:
return 1
return n*fn(n-1)
print(fn(5))
使用递归,实现阶乘 n! = 1 * 2 * 3 * ... * n的计算。
把问题归纳为递归函数的方法:
-
明确函数干什么:定义传入的参数
-
寻找递归结束条件:思考什么时候终止递归并返回什么样的值
-
找出递归的关系式:例如f(n)和f(n-1)什么关系?
匿名函数
匿名函数就是没有名称的函数,也就是不再使用def语句定义的函数。如果要声明匿名函数,则需要使用lambda关键字,匿名函数的声明格式如下所示:
lambda [arg1 [,arg2,.....argn]]:expression
匿名函数示例代码:
sum = lambda arg1, arg2: arg1 + arg2
# sum 为函数名;lambda arg1,arg2 为参数;arg1 + arg2 为函数体(返回值)
print("运行结果:", sum( 10, 20 ))
print("运行结果:", sum( 20, 20 ))
注意:使用Lambda声明的匿名函数能接收任何数量的参数,可以返回一个或者多个值(多个值可以用元组、列表等方式)。
八、装饰器
装饰器的概念
假设我们已经开发了一个本有的函数,后续可能会增加临时的需求,例如插入日志,我们可以增加一个包裹函数,由它来负责这些额外的需求,这个包裹函数就是装饰器 。
装饰器主要应用在如下场景:
- 引入日志;
- 函数执行时间统计;
- 执行函数前预备处理;
- 执行函数后清理功能;
- 权限校验;
- 缓存。
装饰器是一个函数,它需要接收一个被修饰的函数作为参数。
例如,有如下一个装饰器函数:
def w1(func):
print('正在装饰')
def inner():
print('正在验证权限')
func()
return inner
-
装饰器是个嵌套函数
-
内部函数是一个闭包
-
外部函数接收的是被修饰的函数(func)
通过在函数定义的前面添加@符号和装饰器名,实现装饰器对函数的包装。
给f1函数加上装饰器,示例如下:
@w1
def f1():
print('f1')
调用f1时候,程序会自动编译生成调用装饰器函数的代码,等价于:
f1 = w1(f1)
多个装饰器
多个装饰器应用在一个函数上,是一个嵌套的关系,调用顺序是从下至上,再从上至下。
def w1(func):
print('正在装饰w1')
def inner():
print('正在验证权限w1')
func()
return inner
def w2(func):
print('正在装饰w2')
def inner2():
print('正在验证权限w2')
func()
return inner2
@w1
@w2
def f1():
print('f')
#f1() #先注释掉,观察运行结果
#相当于w1(w2(f1))()
装饰有参数的函数
def w1(func):
def inner(a,b):
print('开始验证权限')
func(a,b)
return inner
@w1
def test(a,b):
print('a=%d,b=%d'%(a,b))
test(1,2)
装饰带有返回值的函数
def func(functionName):
print('进入装饰器')
def func_in():
print('调用被装饰函数')
return functionName()
return func_in
@func
def test():
return 'itheima'
print(test())
带有参数的装饰器
如果给装饰器添加参数,需要增加一层包装,先传递参数,然后再传递函数名。
def func_arg(args):
def func(functionName):
def func_in():
print('记录日志:%s'%args)
functionName()
return func_in
return func
@func_arg('这是一条日志')
def fun1():
print('记录完成')
fun1()
装饰器常见Python内置函数
map函数
map函数会根据提供的函数对指定的序列做映射。
map函数的定义如下:
map(function, iterable,…)
第1个参数是函数的名称;第2个参数表示支持迭代的容器或者迭代器。
map函数的作用是以参数序列中的每个元素分别调用function函数,把每次调用后返回的结果保存为对象。
func = lambda x:x+2
result = map(func, [1,2,3,4,5])
print(list(result))
执行过程:
filter函数
filter函数会对指定序列执行过滤操作。
filter函数的定义如下:
filter(function,iterable)
第1个参数可以是函数的名称;第2个参数表示的是序列、支持迭代的容器或迭代器。
func = lambda x:x%2
result = filter(func, [1, 2, 3, 4, 5])
print(list(result))
执行过程:
reduce函数
reduce函数会对参数序列中的元素进行迭代运算。
reduce函数的定义如下:
reduce(function, iterable[, initializer])
- function是一个带有两个参数的函数;
- iterable是一个可迭代对象;
- initializer表示固定的初始值。
函数将一个数据集合(链表,元组等)中的所有数据进行下列操作:
用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 function 函数运算,最后得到一个结果。
from functools import reduce
func = lambda x,y:x+y
result = reduce(func, [1, 2, 3, 4, 5])
print(result)
注意:function函数不能为None。
在Python3中,reduce函数已经被从全局名字空间里面移除了,它现在被放置在fucntools模块中,使用时需要先引入,格式如下:
from functools import reduce
九、一些函数
时间函数
在Python中,通常有如下几种方式表示时间:
(1)时间戳类型
(2)格式化的时间字符串类型
(3)时间元组类型(struct_time)
时间戳
通常来讲,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量,返回浮点秒数。
import time # 引入time模块
ticks = time.time()
print("当前时间戳为:", ticks)
格式化的时间字符串
我们可以使用time模块的strftime方法来格式化日期。
import time
# 格式化成2016-03-20 11:45:39形式
print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
格式化符号及其含义:
%y 两位数的年份表示(00-99)
%Y 四位数的年份表示(000-9999)
%m 月份(01-12)
%d 月内中的一天
%H 24小时小时数(0-23)
%I 12小时制小时数(01-12)
%M 分钟数(00-59)
%S 秒(00-59)
时间元组
返回struct_time类型的函数主要有gmtime()、localtime()和strptime()。
序号 字段 值
0 tm_year 2008
1 tm_mon 1到12
2 tm_mday 1到31
3 tm_hour 0到23
4 tm_min 0到59
5 tm_sec 0到61
6 tm_wday 0到6
7 tm_yday 1到366
8 tm_isdst -1,0,1,-1是决定是否为夏令时的旗帜
随机数函数
什么是随机?
真正的随机涉及到了物理的量子,现实生活不是完全的随机
通俗来讲,越难以被猜到=随机性越好
严格来讲,随机数有评判标准:NIST测试方法,TestU01的测试工具等等
- random.random()
用于生成一个0到1的随机符点数: 0 <= n < 1.0 。
import random
# 生成第一个随机数
print("random():", random.random())
# 生成第二个随机数
print("random():", random.random())
- random.uniform(a,b)
返回a,b之间的随机浮点数,范围[a,b]或[a,b]取决于四舍五入,a不一定要比b小。
import random
print("random:",random.uniform(50,100))
print("random:",random.uniform(100,50))
- random.randint(a,b)
返回a,b之间的整数,范围[a,b],注意:传入参数必须是整数,a一定要比b小。
import random
#生成的随机数n: 12 <= n <= 20
print(random.randint(12,20))
- random.randrange([start], stop[, step])
返回有个区间内的整数,可以设置step。只能传入整数,random.randrange(10, 100, 2),结果相当于从[10, 12, 14, 16, … 96, 98]序列中获取一个随机数。
- random.choice(sequence)
从sequence(序列,是有序类型的)中随机获取一个元素。列表、元组、字符串都属于sequence。
random.randrange(10,100,2)
结果等效于random.choice(range(10,100,2))
- random.shuffle(x[,random])
用于将列表中的元素打乱顺序,俗称为洗牌。
import random
p = ["Python","is", "powerful","simple"]
random.shuffle(p)
print(p)
- random.sample(sequence,k)
从指定序列中随机获取k个元素作为一个片段返回
import random
list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
s = random.sample(list1, 5)
print(list1)
print(s)
结尾
生活总让我们遍体鳞伤,但到后来,那些受伤的地方一定会变成我们最强壮的地方。