文章目录
函数上
1. 函数简介
- 函数也是一个对象
- 函数用来保存一些可执行的代码,并且可以在需要时,对这些语句进行多次调用
语法
def 函数名([形参1,形参2,形参3....]):
代码块
注意:
函数名必须符合标识符的规范(可以包含字母、数字、下划线但是不能以数字开头)
print是函数对象 print()是调用函数
2. 函数的参数
2.1 形参和实参
-
形参(形式参数) 定义形参就相当于在函数内部声明了变量,但是并不是赋值
-
实参(实际参数)指定了形参,那么在调用函数时必须传递实参,实参将会赋值给对应的形参,简单来说有几个形参就要有几个实参
2.2 函数的传递方式
- 定义形参时,可以为形参指定默认值。指定了默认值以后,如果用户传递了参数则默认值不会生效。如果用户没有传递,则默认值就会生效
def fn(name,age=18):
print(f"my name is {name},今年{age}岁了".format(name,age))
fn("小明",20)
def fn(name,age=18):
print(f"my name is {name},今年{age}岁了".format(name,age))
fn("小明")
# my name is 小明,今年18岁了
-
位置参数:位置参数就是将对应位置的实参赋值给对应位置的形参
-
关键字参数 : 关键字参数可以不按照形参定义的顺序去传递,而根据参数名进行传递
def fn(name,age=18):
print(f"my name is {name},今年{age}岁了".format(name,age))
fn(age=20,name="小花")
# my name is 小花,今年20岁了
注意:所有位置参数必须出现在默认参数前,包括函数定义和调用
def fn(name,love="花",age=18):
print(f"my name is {name},今年{age}岁了,喜欢{love}".format(name,age))
fn("小花","帅锅",age=20)
#my name is 小花,今年20岁了,喜欢帅锅
3可变参数
在定义函数的时候,可以在形参的前面加上一个*。这样这个形参就可以获取到所有的实参
它将获取到的实参放到一个元组当中*a 会接受所有位置实参,并且会统一保存到一个元组当中
def s(*args):
# 定义一个变量,保存结果
r = 0
# 遍历元祖,将元组中的数进行相加
for n in args:
r += n
print(r)
s(1,2,6,9)
- 带星号的形参只能有一个
def fn2(a,*b,*c):
^
SyntaxError: invalid syntax
def fn2(a,b,*c):
print('a =',a)
print('b =',b)
print('c =',c)
fn2(1,2,3,4,5)
# a = 1
# b = 2
# c = (3, 4, 5)
- 不定长参数不一定非要写在后面,但是要注意,带*号后面的参数,都必须以关键字参数的形式来进行传递
def fn2(a,*b,c):
print('a =',a)
print('b =',b)
print('c =',c)
fn2(1,2,3,4,c = 5)
# a = 1
# b = (2, 3, 4)
# c = 5
def fn2(*,a,b,c):
print('a =',a)
print('b =',b)
print('c =',c)
fn2(a = 3,b = 4,c = 5)
a = 3
# b = 4
# c = 5
**形参可以接收其他关键字参数,它会将这些参数统一保存到一个字典当中。
key就是参数的名字,字典的value就是参数的值
**形参只能有一个,并且只能写在所有参数的后面
def fn4(b,c,**a):
print('a =',a)
print('b =',b)
print('c =',c)
fn4(1,2,d=4,e=5)
a = {'d': 4, 'e': 5}
# b = 1
# c = 2
位置参数、默认参数、可变参数的混合使用
基本原则是:先位置参数,默认参数,包裹位置,包裹关键字(定义和调用都应遵循)
def func(name, age, sex=1, *args, **kargs):
print( name, age, sex, args, kargs)
函数中
1. 函数的返回值
- 返回值就是函数执行以后返回的结果
- 通过return来指定函数的返回值
- return后面可以跟任意对象,返回值甚至可以是一个函数
def s(*args):
# 定义一个变量,保存结果
r = 0
# 遍历元祖,将元组中的数进行相加
for n in args:
r += n # r = r + n
# print(r)
return r
r = s(1,2,6,9)
print(r - 10)
2. 文档字符串
- help()是Python中内置函数,通过help()函数可以查询Python中函数的用法
- 在定义函数时,可以在函数内部编写文档字符串,文档字符串就是对函数的说明
3. 函数的作用域
- 作用域(scope)
- 作用域指的是变量生效的区域
- 在Python中一共有两种作用域
全局作用域
全局作用域在程序执行时创建,在程序执行结束时销毁
- 所有函数以外的区域都是全局作用域
- 在全局作用域中定义的变量,都是全局变量,全局变量可以在程序的任意位置进行访问
函数作用域
函数作用域在函数调用时创建,在调用结束时销毁
- 函数每调用一次就会产生一个新的函数作用域
- 在函数作用域中定义的变量,都是局部变量,它只能在函数内部被访问
global用法
- 使用 global ,可以让 方法内的 局部变量 全局可用,并且在别的文件里也可以引用到
def fun():
global c
c =0
fun()
print(c)
- 在局部函数对全局变量重新赋值
a = 0
b = 0
def fun():
global a # 在a 之前添加 global
a = 2
b = 2
fun()
print(a)
print(b)
4. 命名空间
- 命名空间实际上就是一个字典,是一个专门用来存储变量的字典
- locals()用来获取当前作用域的命名空间
- 如果在全局作用域中调用locals()则获取全局命名空间,如果在函数作用域中调用locals()则获取函数命名空间
- 返回值是一个字典
5. 递归函数
- 递归是解决问题的一种方式,它的整体思想,是将一个大问题分解为一个个的小问题,直到问题无法分解时,在去解决问题
递归式函数有2个条件 - 1.基线条件
问题可以被分解为最小问题,当满足基线条件时,递归就不执行了 - 2.递归条件
可以将问题继续分解的条件
练习1:
创建一个函数用递归的方式来解决 求任意数的阶乘
先分析:
# 10! = 10 * 9!
# 9!= 9 * 8!
# 8! = 8 * 7!
# ....
# 1! = 1
代码
创建一个函数用递归的方式来解决 求任意数的阶乘
def fn1(n):
# 参数n 要求阶乘的数字
# 1 基线条件
# 判断如果n为1的时候,就是它本身就不需要递归了
if n == 1:
return 1
# 2 递归条件8 * 7!
return n * fn1(n-1)
print(fn1(10))
问题2:定义一个函数 来为任意数字做任意幂运算
分析:
# 10 ** 5 = 10 * 10 ** 4
# 10 ** 4 = 10 * 10 ** 3
# .....
# 10 ** 1 = 10
代码
def fn2(n,i):
# 参数n 要做幂运算的数字
# 参数i 要做幂运算的次数
# 1 基线条件
if i == 1:
return n
# 2 递归条件 10 ** 5 = 10 * 10 ** 4
return n * fn2(n,i-1)
print(fn2(5,6))
print(5 ** 6)
函数下
高阶函数
- 接收函数作为参数,或者将函数作为返回值返回的函数就是高阶函数
lst = [1,2,3,4,5,6,7,8,9,10]
# 定义一个函数 实现偶数
def fn2(i):
if i % 2 == 0:
return True
# 定义一个函数,将指定列表中的所有的偶数,保存到一个新的列表
def fn(func,l):
# 参数func:需要传递一个函数对象,其实这个函数对象就是我们想要的一个规则
# 参数l:要进行筛选的列表
# 创建一个新的列表
new_lst = []
# 对列表进行遍历在筛选
for n in l:
# 判断n的奇偶
if func(n):
new_lst.append(n)
# 返回新的列表
return new_lst
print(fn(fn2,lst))
匿名函数
匿名函数 lambda表达式
lambda函数表达式可以用来创建一些简单的函数,它是函数的另一种创建方式
语法 lambda 参数列表 : 返回值
定义一个函数 实现2个数的和
def fn(a,b):
return a + b
print(fn(1,2))
print(lambda a,b : a + b)
嵌套函数
- 在一个函数里面嵌套一个函数
def waihanshu():
def neihanshu():
print('内函数调用了')
neihanshu() #内部函数需要在调用,要不只调用外部函数的功能
print('我是外函数')
waihanshu()
# 内函数调用了
# 我是外函数
注意:不能在 外面单独的调用其内嵌函数
闭包
- 定义:闭包是一个可以由另一个函数动态生成的函数,并且可以改变和存储函数外创建的变量的值
- 将函数作为返回值也是高阶函数我们也称为闭包
闭包的好处
-
通过闭包可以创建一些只有当前函数能访问的变量
-
可以将一些私有数据藏到闭包中
-
行成闭包的条件:
-
函数嵌套
-
将内部函数作为返回值返回
-
内部函数必须要使用到外部函数的变量
>>> def fun():
... a = 1
... def fun1():
... return a
... return fun1
...
>>> f = fun()
>>> print(f())
1
在函数 fun() 里面,有 a = 1 和 函数 fun1() ,它们两个都在函数 fun() 的环境里面,但是它们两个是互不干扰的,所以 a 相对于 fun1() 来说是自由变量,并且在函数 fun1() 中应用了这个自由变量 – 这个fun1() 就是我们所定义的闭包。闭包实际上就是一个函数,但是这个函数要具有 1.定义在另外一个函数里面(嵌套函数);2.引用其所在环境的自由变量。上述例子通过闭包在 fun() 执行完毕时,a = 1依然可以在 f() 中,即 fun1() 函数中存在,并没有被收回,所以 print(f()) 才得到了结果
>>> def fun(a,b,c):
... def para(x):
... return a*x**2 + b*x + c
... return para
...
>>> f = fun(1,2,3)
>>> print(f(2))
11
上面的函数中,f = fun(1,2,3) 定义了一个一元二次函数的函数对象,x^2 + 2x + 3,如果要计算 x = 2 ,
该一元二次函数的值,只需要计算 f(2) 即可
装饰器的引入
- 我们可以直接通过修改函数中的代码来完成需求,但是会产生以下一些问题
- 如果修改的函数多,修改起来会比较麻烦
- 不方便后期的维护
- 这样做会违反开闭原则(ocp)
- 程序的设计,要求开发对程序的扩展,要关闭对程序的修改
装饰器的使用
- 通过装饰器,可以在不修改原来函数的情况下来对函数进行扩展
- 在开发中,我们都是通过装饰器来扩展函数的功能的
- 写一个计算加法代码,并装饰计算时间的功能
import time
def count_time(f):
#f指的是要装饰的函数,
def call_fun(*args,**kwargs):
#因为不确定要输入什么类型的参数,所以用*args,**kwargs表示形参
print("开始计算")
t1 = time.time()
time.sleep(2)
f(*args,**kwargs)
t2 = time.time()
t = t2-t1
print("一共用时{0}".format(t))
print("结束计算")
#最后返回函数,实现闭包功能,即可以在函数外部,调用内部函数
return call_fun
@count_time#装修函数,此行代码等同于yunsuan =count_time(yunsuan),可看代码3分析
def yunsuan(a,b):
s=a+b
# print(s)
return s
s = yunsuan(8,5)
print(s)
开始计算
一共用时2.0007264614105225
结束计算
None
结果发现返回值为None,原因是在装修器内没有返回值,可定义一个参数结束函数返回值,并return一下
代码改后如下:
import time
def get_time(f):
#f指的是要装饰的函数,
def call_fun(*args,**kwargs):
#因为不确定要输入什么类型的参数,所以用*args,**kwargs表示形参
print("开始计算")
t1 = time.time()
time.sleep(2)
r=f(*args,**kwargs)#调用要修饰的函数,并接受返回值
t2 = time.time()
t = t2-t1
print("一共用时{0}".format(t))
print("结束计算")
return r#不要忘了返回
return call_fun#最后返回函数,实现闭包功能,即可以在函数外部,调用内部函数
@get_time#装修函数,此行代码等同于yunsuan =count_time(yunsuan),可看代码3分析
def yunsuan(a,b):
s=a+b
# print(s)
return s
s = yunsuan(8,5)
print(s)
代码3:不使用装饰器
import time
def count_time(f):
def call_fun(*args,**kwargs):
print("开始计算")
t1 = time.time()
time.sleep(2)
r = f(*args,**kwargs)
t2 = time.time()
t = t2-t1
print("一共用时{0}".format(t))
print("结束计算")
return r
return call_fun
def yunsuan(a,b):
s=a+b
# print(s)
return s
#***********************************************************
yunsuan =count_time(yunsuan)
s = yunsuan(8,5)
print(s)
总结步骤:
- 给fn2函数扩展功能fn1
#第一步
def fn1(f):
#f指的是要装饰的函数,
def call_fun(*args,**kwargs):
#因为不确定要输入什么类型的参数,所以用*args,**kwargs表示形参
#此处可以添加需要扩展功能代码,,,,,,,,,
r=f(*args,**kwargs)#调用要修饰的函数,并接受返回值
#此处可以添加需要扩展功能代码,,,,,,,,,
return r#不要忘了返回
return call_fun#最后返回函数,实现闭包功能,及可以在函数外部,调用内部函数
#第二步
@fn1#装修函数
def fn2(*args,**kwargs):
#代码功能
return “需要返回的数据”
#第三步
#调用函数
命名空间
实际上没有太大价值
# locals()来获取当前作用域的命名空间 返回的是一个字典
a = 10
def fn():
print('我是函数fn....')
scope = locals()
print(scope)