Python 极简核心:函数
1. 函数
Python中有很多自带的函数,这里我们讨论的是一般意义上的函数,也就是自定义函数。
1.1 定义
(1)函数
这里的函数跟数学意义上的函数并没有实质上的差异,由函数名、参数、函数体、和返回值几个组成。
函数的使用主要包括函数的建立和函数的调用两个部分。
举个例子:
def add(a,b): # 函数名:add;参数传递:()必须有
return a + b # 返回值,可以不写return(则默认为None)
print(add(1,1)) # 函数调用
2
(2)函数与类中的方法的区别:
方法也接受参数,处理、输出。但是它无法独立执行,需要有对象来调用执行。而且,方法在类中定义时总会有一个参数self
,且这个参数在方法被实例对象调用时并不需要传递实参。这两点就是方法与函数的区别。
class My_module():
'''
country = 'China' # 类属性——共性——静态(赋值)
location = 'SH' # 类属性——共性——静态(赋值)
'''
country = 'China'
location = 'SH'
def __init__(self, name, gender):
'''
:param name: 对象属性——个性——静态(借助 self 赋值)
:param gender: 对象属性——个性——静态(借助 self 赋值)
'''
self.name = name
self.gender = gender
def say_hi(self, words):
'''
:param words: 方法——个性——动态(函数)
:return: 打印出输入的字符串
'''
print(words)
my_module = My_module("Robin", "male") # 创建My_module()类的一个对象
print(f"In {my_module.country}") # 引用类属性
print(f"This is {my_module.name}.") # 引用对象属性
my_module.say_hi("Nice to meet you sir!") # 调用方法
In China
This is Robin.
Nice to meet you sir!
1.2 核心
函数是Python中的第一类对象 ,可以把函数赋值给变量,对该变量进行调用,可实现原函数的功能。
(变量——函数——函数式编程)
变量的作用域
由于函数也是对象,这部分可以参考:Python:变量的作用域与LEGB原则
参数传递
这里主要介绍两个特殊的参数传递方法(包裹传参):*args
和 **kwargs
。
首先注意一点,*args
和 **kwargs
只是参数传递的形式,并不是说参数名一定要使用这里的args
和kwargs
。
-
*args
传递可变长度的参数,在参数数目未知的情况下可以使用。def args_passing1(*args): print(args) args_passing1('what', 'should', 'I', 'do') def args_passing2(*args): for i in args: print(i)
('what', 'should', 'I', 'do') just do it
-
**kwargs
传递的参数为键值对(key-value)的形式。def kwargs_passing(**kwargs): for k, v in kwargs.items(): print(f'{k} = {v}') print((k,v)) kwargs_passing(key_name='robin')
key_name = robin ('key_name', 'robin')
2. 高级玩法
2.0 写在前面
普通的函数已经不能再满足人们的欲望,所以根据需求,又诞生了很多相对来说较为高级的玩法。
简单理解
- 懒得定义函数名:匿名函数——便于作为参数传递出去
- 在函数里面定义和调用另一个函数:嵌套函数——便于封装(隐藏数据)
- 将一个函数作为参数:高阶函数——接受别的函数作为参数
- 在函数内部递归调用函数自身:递归函数
- 闭包函数
- 装饰器函数
本质框架
上面一下列举了这么多的函数,却没有讲清楚他们之间的联系,而这才是重点!
- 函数为一等对象
- 高阶函数
- 函数作用域
- 嵌套函数
- 闭包函数.:①代码复用②减少函数参数->减少函数参数
- 装饰器
- 闭包函数.:①代码复用②减少函数参数->减少函数参数
- 嵌套函数
注:
闭包函数:函数+自由变量
装饰器:函数+函数型自由变量
2.1 匿名函数 lambda
首先,匿名函数是一个函数表达式;
其次, 匿名函数很好地体现了函数式编程的特征(同map、filter、reduce等其它高阶函数一样)
匿名函数语法:lambda argument_list: expression
即 lambda 参数: 表达式
注意:这里的表达式(expression)实质上就是匿名函数的返回值,因此只能是表达式,而不能是语句!
# 以下两种相等同
# 1.不使用匿名函数
def f(x):
return x * x
# 2.使用匿名函数
lambda x: x * x
与普通函数一样,匿名函数除了普通调用,也可以作为其他函数的输入参数。
f = lambda x: x * x
print(f(2)) # 4
# 匿名函数f作为map()函数的第一个参数
f = lambda x: x * x
print(list(map(f, [1, 2]))) # [1, 4]
# lambda 与 sort()搭配
d = ["ale","apple","monkey","plea"]
d.sort(key = lambda x : [-len(x), x])
print(d)
['monkey', 'apple', 'plea', 'ale']
[-len(x), x]
完成了两个操作:
一是将长的字符串放在前面(本身是升序,但是加了负号,就将最大的长度变为了最小的负数),第二是将字符串按照字母排序。
2.2 嵌套函数
定义:在函数内部定义一个函数
# 函数中套着另一个函数
def f1():
print("outer func")
def f2():
print('inner func')
f2()
f1()
outer func
inner func
2.3 高阶函数
满足下述条件之一的函数称之为高阶函数(Higher-order Functions)
(1)接收函数作为参数
(2)或者返回一个函数
# ①函数作为参数
def f1():
print('Hi')
def f2():
f1()
print('This is Robin')
f2()
Hi
This is Robin
结果:
# ②函数作为返回值
def f1():
print('Hi')
def f2():
print('This is Robin')
return f2()
f1()
结果:
Hi
This is Robin
常见高阶函数:map,reduce、filter
- map
map(function, iterable) -> iterator
map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。
- reduce
reduce(function, iterable) —> value
reduce把一个函数作用在一个可迭代对象,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算
- filter
filter(function, iterable) —> iterator
filter 将可迭代对象中的每一个元素,传递给函数进行判断,最后返回结果为True的元素组成的一个iterator
- sorted
sorted(iterable, key, reverse)—> list
sorted 函数默认将序列升序排列后返回一个新的 list,还可以自定义键函数(key)来进行排序,也可以设置reverse参数确定是升序还是降序
关于更为强大map,reduce、filter ,这里还有一份较为详细的总结:Python 高阶函数:一文搞透 map()、reduce()、filter()
2.4 递归函数
递归概念:所谓递归函数,就是在内部调用自己的函数。
递归的关键有三点:
- 明确函数要解决的问题
- 寻找递归结束条件
- 找出等价表达式
例子:这里简单的给出求0-100之和的两个例子
# 计算0到100的和
# ①
# def sum(n):
# if n == 0:
# return 0
# else:
# return sum(n-1) + n
#
# r = sum(100)
# print(r)
# ②
# def sum(n):
# if n < 2: # or n <= 1
# return n
# else:
# return sum(n-1) + n
#
# r = sum(100)
# print(r)
这两个例子尽管递归结束条件选取的不同,但是都是正确的,结果都为5050
。
更多详细的内容在这里有总结:Python 递归
2.5 闭包函数
闭包概念
刚开始学习闭包是,网上看到了以下定义:
理解闭包:
- 延伸了作用域的函数
- 如果一个函数定义在另一个函数的作用域内,并且引用了外层函数的变量,则该函数称为闭包
- 闭包是由函数及其相关的引用环境组合而成的实体(即:闭包=函数+引用环境)
后来才发现,它掉了非常重要的一点:还需要return内部函数,正确的闭包定义应该如下:
function1里嵌套function2,内部的function2可以访问外部function1里的变量,且外部function1 return 的是内部function2时,外部函数就是一个闭包,
def function1(x):
def function2(y):
return x + y
return function2
即:
闭包必须包含三个元素
①嵌套函数
②环境/自由变量
③return 内部函数
具体点说闭包的三个特征就是:
1.闭包函数必须有内嵌函数
2.内嵌函数需要引用该嵌套函数上一级namespace中的变量
3.闭包函数必须返回内嵌函数
举例:
def fun_out(a):
def fun_in(b):
return a + b
return fun_in
fun1 = fun_out(1)
其中fun1函数是一个闭包,它携带着fun_out中定义的a变量,值为1。
why 闭包
(1)闭包 VS 类
还是上面得到例子,再定义一个函数fun5。
def fun_out(a):
def fun_in(b):
return a + b
return fun_in
fun1 = fun_out(1)
fun5 = fun_out(5)
fun1(5) # 6
fun1(10) # 11
fun5(5) # 10
fun5(10) # 15
我们可以发现fun1和fun5两个函数的定义相同,只是携带的自由变量不同,便成为了两个函数。
由此闭包可以作为函数工厂,生产出功能类似,但是会有细微差别的函数。
所以,我们也可说,闭包就是引用了自由变量的函数。
观察上面的例子,很容易发现跟Python类的使用很接近!但是这比用类来实现更优雅
总的来说,闭包使得函数式编程拥有了类似面向对象编程的一种手法。
(2)闭包(局部变量) VS 全局变量
闭包作用
- 想要改进如下代码:
def line1(x):
return x + 1
def line2(x):
return 2*x + 1
def line3(x):
return 5*x + 10
def line4(x):
return -2*x - 6
- 可能的思路
def line(x, a, b):
return a*x + b
line1 = line(x, 1, 1)
line2 = line(1, 1, 1)
但是这样就出问题了,因为需要知道全部的参数值
- 使用闭包
def line_conf(a, b): # 使用外部函数的参数a和b说明直线的参量,这样可以复用内同一个闭包
def line(x):
return a*x + b
return line # 再将返回值设为(内部)函数体
line1 = line_conf(1, 1) # 记住闭包本质也是函数
line2 = line_conf(2, 1)
可以看出闭包作用:
- ① 提高代码复用性
- ② 减少函数的参数:line()函数有三个参数(自变量x以及a和b),通过line_conf()这个闭包,我们可以预设a和b的值,从而起减参的效果
换一种思路:理解本质
之前是由表及里,从概念、实例和作用等方面讲了闭包,现在我们换一种思路:从接近本质的地方出发,去思考。
2.6 装饰器函数
概念
装饰器是闭包的一个应用,只是携带的自由变量是一个函数。
待看,参考:python闭包
换一种思路
待看!
装饰器
参考:
- Python基础总结(1)
- Python进阶:函数式编程(高阶函数,map,reduce,filter,sorted,返回函数,匿名函数,偏函数)…啊啊啊——比较好的总结!
- python闭包——讲闭包及系列知识很不错
- 装饰器——别开生面,讲的浅显易懂,接近本质