- 函数也是一个对象。
- 对象是内存中专门用来存储数据的一块区域。
- 函数用来保存一些可执行代码,并且在需要时,可以重复调用。
- 创建函数:
def 函数名( [形参1,形参2,.....形参n ] ):
代码块
函数名必须要符合标识符规范
可以包含字母、数字、下划线,但不能以数字开头。
- 函数中保存的代码,需要被调用才会执行。
- 调用函数:
二、函数参数函数对象()
- 定义函数时,可以在函数名后定义数量不等的形参,多个形参以,隔开。
- 定义形参时,可以为形参指定默认值。若调用者没有传值,则使用默认值。
def fn(a=5, b=10):
print("a=", a)
print("b=", b)
2.2 实参的传递
- 位置参数
- 将对应位置的实参赋值给对应的形参。
fn(100, 200)
- 关键字参数
- 可以不按照形参定义的顺序,而是根据参数名传递。
fn(b=222, a=111)
- 位置参数和关键字参数可以混合使用
- 混合使用时,必须将位置参数写在前面
def fn(a=5, b=10, c=15):
print("a=", a)
print("b=", b)
print("c=", c)
fn(3, 6, c=9)
2.3 实参的类型
- 函数在调用时,解析器不会检查实参的类型。
- 实参可以传递任意类型的对象。
- 如果形参执行的是一个对象,通过形参修改对象时,会影响到所有指向该对象的变量。
-
在形参前边加上*,这样这个形参将会获取到所有的实参。
-
它会将所有的实参保存到一个元组中。
def sum(*a):
result = 0
# 遍历元组
for n in a:
result += n
print(result)
sum(1, 2)
sum(5, 15, 25)
sum(6, 66, 666)
- 带*的形参只能有一个。
- 可以和其他参数配合使用。
def fn(a,b,*c):
print("a=",a)
print("b=",b)
print("c=",c)
fn(1,2,3,4,5)
# a= 1
# b= 2
# c= (3, 4, 5)
- 如果在形参开头直接写一个*,则要求所有的参数必须以关键字形式传递。
def fn(*, a, b, c):
print("a=", a)
print("b=", b)
print("c=", c)
fn(a=1, b=2, c=3)
- *形参只能接受位置参数,不能接收关键字参数。
- **形参可以接受其他的关键字参数,它会将这些参数统一保存到字典中。
- 字典的key就是参数的名,value就是参数的值
- **形参只能有1个,且必须在所有参数最后。
def fn(b, c, **a):
print("a=", a)
print("b=", b)
print("c=", c)
fn(b=1, d=2, c=3, e=10, f=20)
# a= {'d': 2, 'e': 10, 'f': 20}
# b= 1
# c= 3
2.5 参数的解包(拆包)
- 传递实参时,可以在序列类型的参数前添加*,这样会自动将序列解包,传递给形参。
- 要求序列中元素的个数必须和形参个数一致。
def fn(a, b, c):
print("a=", a)
print("b=", b)
print("c=", c)
t = (10, 20, 30)
fn(*t)
- 通过**对字典进行解包。
d = {"a": 100, "b": 200, "c": 300}
fn(**d)
三、函数返回值
- return 后面跟什么值,函数就返回什么值。
- 返回值可以是任意对象,甚至可以是函数。
- return 后的代码不会执行。
def fn():
def fn2():
print("hello")
return fn2()
四、文档字符串(doc str)
- help() 是python的内置函数,可以查询函数的用法。
help(函数对象)
- 在定义函数时,可以在函数内部编写文档字符串,即为函数的说明。
- 当编写了文档字符串时,调用help()就可以来查看函数的说明。
- 直接在函数的第一行写一个字符串,就是文档字符串。
def fn(a:int, b:str, c:bool) ->int:
'''
:param a:作用,类型,默认值...
:param b:作用,类型,默认值...
:param c:作用,类型,默认值...
:return:10
'''
return 10
五、作用域与命名空间 5.1 作用域(scope)def fn(a:int, b:str, c:bool) ->int:
表示希望传参的类型 a为int,b为str,c为bool,但是不强制
返回值时int
- Python中共有2中作用域
- 全局作用域
- 在程序执行时创建,在程序执行结束时销毁。
- 所有函数以外的区域都是全局作用域。
- 在全局作用域中定义的变量,都属于全局变量,全局变量可以在程序的任意位置访问。
- 函数作用域
- 在函数调用时创建,在调用结束时销毁。
- 函数每调用一次就会产生一个新的作用域。
- 在函数作用域中定义的变量,都是局部变量,它只能在函数的内部被访问。
- 全局作用域
- 在函数中为变量赋值,默认都是为局部变量赋值。
a = 20
def fn():
a = 10
print("函数内部a=", a)
fn()
print("函数外部a=", a)
# 输出结果
# 函数内部a= 10
# 函数外部a= 20
- 如果希望在函数内部修改全局变量,则需要使用关键字global来声明变量。
a = 20
def fn():
global a
a = 10
print("函数内部a=", a)
fn()
print("函数外部a=", a)
# 输出结果
# 函数内部a= 10
# 函数外部a= 10
5.2 命名空间(namespace)
- 命名空间指的是变量存储的位置,每一个变量都需要存储到指定的命名空间当中
- 每一个作用域都会有一个它对应的命名空间。
- 全局命名空间用来保存全局变量。
- 函数命名空间用来保存函数中的变量。
- 命名空间实际上就是一个字典,是一个专门用来存储变量的字典。
- locals():来获取当前作用域的命名空间
- 如果在全局作用域中调用locals()则获取全局命名空间;
- 如果在函数作用域中调用locals()则获取函数命名空间;
- 返回一个字典。
- globals():在任意位置获取全局命名空间
a = 100
scope = locals() # 当前命名空间
print(scope)
#{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001CF5B1F6CD0>, '__spec__': None, '__annotations__': {}, '__builtins__': , '__file__': 'E:\developer\idea\workspace\PythonBase\lesson_05\code\06.作用域与命名空间.py', '__cached__': None, 'a': 100, 'scope': {...}}
def fn4():
a = 10
scope = locals() # 函数内部命名空间
print(scope) # {'a': 10}
global_scope = globals() # 全局命名空间
print(global_scope)
#{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001CF5B1F6CD0>, '__spec__': None, '__annotations__': {}, '__builtins__': , '__file__': 'E:\developer\idea\workspace\PythonBase\lesson_05\code\06.作用域与命名空间.py', '__cached__': None, 'a': 100, 'scope': {...}}
fn4()
六、递归
- 简单理解为在函数中自己调用自己。
- 递归式函数的两个条件
- 基线条件
- 问题可以被分解为的最小问题,当满足基本条件时,递归就不再执行。
- 递归条件
- 将问题分解的条件。
- 基线条件
#定义阶乘函数
def factorial(n):
if n == 1:
return 1
return n * factorial(n - 1)
print(factorial(4))
练习1
创建一个函数power来为任意数字做幂运算 n**i。
def power(n, i):
if i == 1:
return n
return n * power(n, i - 1)
练习2
创建一个函数,用来检查一个任意的字符串是否是回文字符串。如果是返回True,否则返回False。回文字符串:字符串从前往后和从从后往前都是一样的,abcba。
def check(str):
# 字符串长度小于2,一定是回文
if (len(str) < 2):
return True
# 第一个字符串跟最后一个不一样,不是回文
if (str[0] != str[-1]):
return False
# 递归检查第二个到倒数第二个的子字符是否是回文
return check(str[1:-1])
七、高阶函数
- Python中,函数是一等对象
- 一等对象一般有以下特点
- 对象在运行时创建
- 能赋值给变量或作为数据结构中的元素
- 能作为参数传递
- 能作为返回值返回
- 这就意味着Python支持函数式编程。
- 一等对象一般有以下特点
- 高阶函数至少要符合以下特点中的一个
- 接收1个或多个函数作为参数
- 将函数作为返回值返回
- 当使用一个函数作为参数时,实际上时将指定的代码传递进了目标函数。
list = [1, 2, 3.4, 5, 6, 7, 8, 9, 10]
# 检查一个任意数是否是偶数
def fn2(i):
if i % 2 == 0:
return True
return False
# 检查一个任意数是否大于5
def fn3(i):
if i > 5:
return True
return False
# 定义函数:返回一个子列表
def fn(func,list):
new_list = []
for n in list:
if (func(n)):
new_list.append(n)
return new_list
print(fn(fn2,list)) #[2, 6, 8, 10]
print(fn(fn3,list)) #[6, 7, 8, 9, 10]
- filter() 可以从序列中过滤出符合条件的元素,保存到新序列中
- 参数:
- 函数,根据该函数来过滤序列
- 需要过滤的序列(可迭代的结构)
- 返回值:
- 过滤后的新序列(可迭代的结构)
上面的 print(fn(fn3,list)) 等价于:
print(list(filter(fn2, ls)))
匿名函数lambda
- 专门创建一些简单的函数,只调用一次。
lambda 参数列表:返回值
ls = [1, 2, 3.4, 5, 6, 7, 8, 9, 10]
# 判断奇偶函数
def fn2(i):
if i % 2 == 0:
return True
return False
# fn2改造成lombda
lambda i: i % 2 == 0
r = filter(lambda i: i % 2 == 0, ls)
print(list(r))
# 输出结果
# [2, 6, 8, 10]
map()函数
- 可以对可迭代对象中所有元素做指定操作,返回一个新对象
ls = [1, 2, 3.4, 5, 6, 7, 8, 9, 10] r = map(lambda i: i + 1, ls) print(list(r)) # 输出结果 # [2, 3, 4.4, 6, 7, 8, 9, 10, 11]
sort()方法
- 对列表元素进行排序。
- 默认直接比较元素的大小。
- sort() 可以接受一个关键字参数,key
- key需要一个函数作为参数,当设置了函数作为参数
- 每次都会以列表中的一个元素来调用函数,并且使用函数的返回值来比较元素的大小。
ls = ["bb", "aaaa", "c", "ddddddd", "fff"] ls.sort(key=len) print(ls) # 输出结果 ['c', 'bb', 'fff', 'aaaa', 'ddddddd']
sorted()函数
- 与sort()用法基本一致
- 区别:
- 可以对任意序列进行排序
- 不会影响原来的对象,而是返回新对象
ls = ["bb", "aaaa", "c", "ddddddd", "fff"] r = sorted(ls, key=len) print(ls) print(r) # 输出结果 ['bb', 'aaaa', 'c', 'ddddddd', 'fff'] ['c', 'bb', 'fff', 'aaaa', 'ddddddd']
闭包
- 函数嵌套。
- 将内部函数作为返回值返回。
- 内部函数必须要使用外部函数的变量。
def make_avg():
nums = []
def avg(n):
nums.append(n)
return sum(nums) / len(nums)
return avg
averager = make_avg()
print(averager(10))
print(averager(20))
print(averager(30))
#-------------------------
#输出结果
10.0
15.0
20.0



