函数就是最基本的一种代码抽象的方式。是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段
内嵌函数
Python内置了很多有用的函数,我们可以直接调用,比如print,abs,int,type等,具体可查看官网文档
https://docs.python.org/3/library/functions.html
定义函数
使用def关键字,依次写出函数名、括号,括号中为参数、冒号:、在缩进块中编写函数体、返回值
def fun_name(params):fun_body
实例,编写一个求 绝对值 的函数
def my_abs(x):if not isinstance(x, (int, float)):raise TypeError('bad operand type')if x >= 0:return xelse:return -xmb_abs(1) # 1mb_abs(-3) # 3
函数参数
位置参数
positional argument 也称必备参数
def square(a, b):return a ** b
参数a,b就是位置参数
在调用函数时,必须按顺序传入这两个参数
>>> square(2,3) # 8>>> square(3,2) # 9>>> square(2) #TypeError: square() takes exactly two argument (1 given)
默认参数
default argument,特别的,默认参数要定义在位置参数之后,使用默认参数可以降低使用难度
def square(a, b=2):return a ** b
调用函数时,如果没有传参数b,就使用默认值。
>>>square(2) # 4>>>square(2, 3) # 8>>>square(2, b=3) # 8
可变参数
也称不定长参数,参数的个数是可变的,定义在常规参数(regular param 位置参数和默认参数)之后。
实际例子:对一组数据进行求和,先看普通的参数定义方法。
def calc(numbers):sum = 0for n in numbers:sum = sum + nreturn sum# 调用时候,需要先组装出一个tuple或者listcalc([1,2,3]) # 6calc((1,2,3,)) # 6
如果改用可变参数,这些可变参数在函数调用时自动组装为一个tuple
def calc(*numbers):sum = 0for n in numbers: # 此处numbers是一个tuplesum = sum + nreturn sumcalc(1,2,3) # 6calc() # 0nums = [1,2,3]calc(nums[0], nums[1], nums[2]) # 6calc(*[1,2,3]) # 6
关键字参数
类比可变参数,可变参数是在函数调用时自动组装成一个tuple。可关键字参数,而关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict
定义时,关键字参数要定义在可变参数之后
def person(name, age, **kw):if kw:print(type(kw))print('name:', name, 'age:', age, 'other:', kw)person("tao", 20, work="QA") # name: tao age: 20 other: {'work': 'QA'}
命名关键字参数
关键字未限制输出的参数名,若需要对参数名做限制,需要命名关键字参数。
定义函数时,如果没有可变参数,需要使用特殊分隔符”*”
def person(name, age=3, *, city, job):print('name:', name, 'age:', age, 'other:', city, job)
def person(name, age=3, *args, city, job):print('name:', name, 'age:', age, "args", args, 'other:', city, job)
参数组合
参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。
函数返回值
变量的作用域
Python查找一个变量时会按照“局部作用域”、“嵌套作用域”、“全局作用域”和“内置作用域”的顺序进行搜索
def foo():b = 'hello'# Python中可以在函数内部再定义函数def bar():c = Trueprint(a)print(b)print(c)bar()# print(c) # NameError: name 'c' is not definedif __name__ == '__main__':a = 100# print(b) # NameError: name 'b' is not definedfoo()
- 局部作用域 : 在函数内部定义的变量
- 嵌套作用域 : 对于函数bar,变量b即为嵌套作用域
- 全局作用域 : 变量a
- 内置作用域 :
input、print、int等
局部变量
def foo():a = 200print(a) # 200if __name__ == '__main__':a = 100foo()print(a) # 100,foo函数a是局部变量,不会继续去寻找全局变量,所以全局变量a不会被foo函数修改
全局变量
def foo():global aa = 200print(a) # 200if __name__ == '__main__':a = 100foo()print(a) # 200
嵌套变量
def foo():a = 200def change_a():nonlocal a #声明a为嵌套变量a = 300print(a)change_a() #300print(a) #300if __name__ == '__main__':a = 100foo()print(a) # 100
递归函数
如果一个函数在内部调用自身本身,这个函数就是递归函数。
实现阶乘
def fact(n):if n==1:return 1return n * fact(n - 1)===> fact(5)===> 5 * fact(4)===> 5 * (4 * fact(3))===> 5 * (4 * (3 * fact(2)))===> 5 * (4 * (3 * (2 * fact(1))))===> 5 * (4 * (3 * (2 * 1)))===> 5 * (4 * (3 * 2))===> 5 * (4 * 6)===> 5 * 24===> 120
高阶函数
变量可以指向函数,函数名也是变量。
把函数作为参数传入,这样的函数称为高阶函数。
map
map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。
将
>>> list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9]))['1', '2', '3', '4', '5', '6', '7', '8', '9']
reduce
reduce把一个函数作用在一个序列[x1, x2, x3, …]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是: reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
>>> from functools import reduce>>> def add(x, y):... return x + y...>>> reduce(add, [1, 3, 5, 7, 9])25
filter
和map()类似,filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素
def is_odd(n):return n % 2 == 1list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
sorted
它还可以接收一个key函数来实现自定义的排序,,例如按绝对值大小排序(abs)
sorted([36, 5, -12, 9, -21], key=abs, reverse=True)
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)['Zoo', 'Credit', 'bob', 'about']
返回函数
闭包
注意到返回的函数在其定义内部引用了局部变量args,所以,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用,所以,闭包用起来简单,实现起来可不容易。
另一个需要注意的问题是,返回的函数并没有立刻执行,而是直到调用了f()才执行。我们来看一个例子:
匿名函数
匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。
lambda [arg1 [,arg2,.....argn]]:expression
实现
sum = lambda x,y: x+y
配合高阶函数
sum = list
