函数
函数定义:
- 函数也是一个对象
- 函数可以用来保存一些可执行的代码,并且可以在需要时,对这些语句进行多次的调用
创建函数:
def 函数名([形参1,形参2,...形参n]):
代码块
调用函数:
def fn():
print("这是我的第一个函数")
fn()
print(type(fn))
输出:
函数的参数
定义一个函数,用来求任意两个数的和:
def add(a, b, c=20):
return a+b+c
1)参数传递的方式:
1、位置参数–将对应位置的实参赋值给对应位置的形参
print(add(10, 20, 30))
2、关键字参数
print(add(b=20, a=10, c=30))
注意:位置参数和关键字参数可以混合使用(位置参数要写在前面!!)
print(add(10, c=30, b=20))
2)实参的类型:
- 函数在调用时,解析器不会检查实参的类型
- 实参可以传递任意类型的对象
- 如果实参传递的是可变对象,那么形参对对象的改变会导致实参的改变
3)不定长参数:
1、带一个星号的形参
在定义函数时,可以在形参前面加上一个*,这样这个参数将会获取到所有的实参,它会将所有的实参保存到一个元组中(这个步骤可以称为装包)。
def fn(*a):
print("a = ", a, type(a))
fn(1, 2, 3, 4, 5)
求任意个参数的和:
def add(*a):
return sum(a)
add(1, 2, 3, 4, 5)
注意:带*号的参数只能有一个。而且带星号参数后面的参数,必须以关键字的形式传递。
如果第一个形参为*,则所有参数都必须以关键字形式传递
def fn(*, a, b, c):
return a + b + c
fn(a=10, b=20, c=30)
2、带两个星号的形参
可以接受其它关键字的参数,然后将这些参数统一保存到字典中。
def fn(**a):
print("a=", a, type(a))
fn(b=1, c=2, d=3)
输出:
注意:带两个星号的形参必须写在参数列表的最后
4)参数解包:
1、对元组解包
传递实参时,也可以在序列类型的参数前添加星号,这样他会自动将序列中的元素依次作为参数传递
def fn(a, b, c)
print("a = ", a)
print("b = ", b)
print("c = ", c)
s = (1, 2, 3)
def(*s)
2、对字典解包
def fn(a, b, c):
print("a = ", a)
print("b = ", b)
print("c = ", c)
d = {"b":100, "a":200, "c":300}
fn(**d)
输出:
返回值
返回值就是函数执行以后返回的值
def sum(a, b):
return a + b
print(sum(1, 2))
- return后面可以跟任意对象(所以返回一个函数也是可以的哦~~)
- 如果return后面没有也对象,或者不写return,则相当于 return None
- 在函数中,return后的代码都不会执行
文档字符串
help() 是 python 中的内置函数,通过这个函数可以查询 python 中的函数的用法
help(print)
输出:
文档字符串(doc str):
- 在定义函数时,可以在函数内部编写文档字符串,文档字符串就是函数的说明。
- 当我们编写了文档字符串后,就可以通过 help() 来查看函数的说明。
- 在函数的第一行写一个字符串即可
def fn(a, b, c):
'''
这是一个文档字符串示例
函数的参数:
a,作用,类型,默认值,...
b,作用,类型,默认值,...
c,作用,类型,默认值,...
'''
print("a = ", a)
print("b = ", b)
print("c = ", c)
help(fn)
输出:
作用域(scope)
作用域指的是变量生效的区域。python中一共有两种作用域:全局作用域和函数作用域。
全局作用域:
- 全局作用域在程序执行时创建,在程序执行结束时销毁
- 所有函数以外的区域都是全局作用域
- 在全局作用域中定义的变量,都属于全局变量,全局变量可以在程序的任意位置被访问。
函数作用域:
- 函数作用域在函数调用时创建,在调用结束时销毁
- 函数每调用一次就会产生一个新的函数作用域
- 在函数作用域中定义的变量,都是局部变量,它只能在函数内部被访问
变量查找:
- 当我们使用变量时,会优先在当前作用域中寻找该变量,如果有则使用,如果没有则继续去上一级作用域中寻找,依次类推。
- 如果找到全局作用域依然没有找到,则会抛出异常。
- 如果在函数内部想要访问全局全量,则需要使用 global 关键字
a = 3 def fn3(): global a a = 10 print(a)
命名空间(namespace)
- 命名空间就是变量存储的位置,每一个变量都需要存储到指定的命名空间当中。
- 每一个作用域都会有一个它对应的命名空间。
- 全局命名空间,用来保存全局变量。函数命名空间用来保存函数中的变量。
- 命名空间实际上就是一个字典,是一个专门用来存储变量的字典
- locals() 用来获取当前作用域的命名空间。如果在全局作用域中调用locals(),则获取全局命名空间。如果在函数作用域中调用locals(),则获取函数命名空间。返回的是一个字典。
- global() 可以在任意位置(包括函数内)获取全局的命名空间。
输出:a = 3 def fn3(): a = 10 print(a) print(locals()) fn3() print(locals())
递归
递归简单来理解,就是自己调用自己
尝试求10的阶乘:
def factorial(n):
result = n
for i in range(1, n):
result *= i
return result
print(factorial(10))
递归函数的两个要件:
- 结束条件。即问题可以被分解为更小的问题,直到满足结束条件就不再执行了。
- 递归条件。将问题继续分解的条件。
将以上条件改为递归的形式:
def factorial(n):
if n==1:
return 1
else:
return n * factorial(n-1)
print(factorial(10))
PS:递归和循环类似,基本是可以互相代替的。循环编写起来比较容易,阅读起来难。递归编写起来难,但是方便阅读。
函数式编程
在python中,函数是一等对象。一等对象具有以下的特点:
- 对象是在运行时创建的
- 能赋值给变量或作为数据结构中的元素
- 能作为参数传递
- 能作为返回值返回
高阶函数:接收函数作为参数,或者将函数作为返回值的函数都是高阶函数
1、将函数作为参数传递
def fn1(n):
if n % 2 == 0:
return True
else:
return False
def fn2(n):
if n % 2 == 0:
return False
else:
return True
def fn3(n):
if n > 5:
return True
else:
return False
def fn(func, lis):
new_lis = []
for n in lis:
if func(n):
new_lis.append(n)
return new_lis
lis = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(fn(fn1, lis))
这样写的好处是:在传入参数时就可以决定是取偶数、奇数、还是大于5的数。
匿名函数:
在前面的例子中我们看到,fn1、fn2、fn3都只会被调用一次,而为了这一次调用需要在代码里写上完整的这个函数,感觉有点不值。下面用匿名函数来解决:
filter()函数可以用来过滤可迭代对象:
lis = [1, 2, 3, 4, 5, 6, 7, 8, ,9 ,10]
s = filter(lambda i : return i % 2, lis)
print(s)
lambda函数表达式专门用来创建一些简单的函数,它是函数创建的又一种方式。
fn6 = lambda a, b : a + b
print(fn6(3, 4))
map() 函数可以对可迭代对象中所有元素做指定的操作,然后将其添加到一个新的对象中返回。
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
r = map(lambda i : i + 1, l)
print(r)
sort() 方法可以用来对列表中的元素排序:
l = ["aaa", "bbb", "ccc", "ddd"]
l.sort()
print(l)
# 也可以接收一个函数作为参数,来确定排序规则
# 以长度来作为排序规则
l.sort(key=len)
print(l)
l = ['2', 5, 6, '8', '3']
l.sort(key=int)
print(l)
sorted() 函数的使用与sort() 使用方法类似,只是sorted() 函数不会改变原来的列表,而是返回一个新的排完序的列表
以上代码的输出:
2、将函数作为返回值–闭包
def fn():
def fn2():
print("inner")
return fn2
fn()
- 这个函数是在 fn() 内部定义的,所以总是能访问到 fn() 函数内的变量。
- 通过闭包,可以创建一些只有当前函数能访问的变量,即可以讲一些私有的数据藏到闭包中。
def make_averager():
# 创建一个列表,用来保存数值
nums = []
# 创建一个函数,用来计算平均值
def averager(n):
nums.append(n)
return(sum(nums) / len(sums))
avg = make_averager()
print(avg(10))
print(avg(10))
print(avg(5))
输出:
这样做的好处是,nums列表在函数内,其它人不好改!!
形成闭包的要件:
- 函数嵌套
- 将内部函数作为返回值返回
- 内部函数必须要使用外部函数的变量
装饰器:
希望在不修改原函数的情况下,来对函数进行扩展
def fn():
print("我是fn函数...")
def new_fn():
print("函数开始执行")
fn()
print("方式执行结束")
但是每扩展一个函数就手动创建一个新函数,实在太麻烦。
def begin_end(old):
'''
用来对其它函数进行扩展,使其它函数可以在执行前打印开始执行, 执行后打印执行结束
参数:
old:被扩展的函数
'''
def new_function(*args, **kwargs):
print("开始执行~~")
#调用被扩展的函数
old(*args, **kwargs)
print("执行结束")
return new_function
类似于begin_end() 这样的函数,我们就称它为装饰器。通过装饰器可以在不修改原来函数的情况下,对函数进行扩展。
在定义函数时,可以通过@装饰器,来使用指定的装饰器。并且可以同时使用多个,那样就是装饰一个装饰器
@begin_end
def say_hello():
print("大家好!!")
say_hello()
输出: