Python学习日志
RBHGO的主页欢迎关注
温馨提示:创作不易,如有转载,注明出处,感谢配合~
目录
前言
上次分享了函数,我们还讲到过Python中常用的数据类型,这些类型的变量都可以作为函数的参数或返回值,用好函数还可以让我们做更多的事情。接触了函数我们得明白在写代码尤其是开发商业项目的时候,一定要有意识的将相对独立且重复出现的功能封装成函数,这样不管是自己还是团队的其他成员都可以通过调用函数的方式来使用这些功能。字符串是非常重要的数据类型,字符串的常用运算和方法需要掌握,因为一般的商业项目中,处理字符串比处理数值的操作要更多。前面也系统得分享字符串的知识字符串,有兴趣可以阅读一下。
开始正题之前,在和大家分享一个题。首先谈谈栈结构
函数调用会通过内存中称为“栈”(stack)的数据结构来保存当前代码的执行现场,函数调用结束后会通过这个栈结构恢复之前的执行现场。栈是一种先进后出的数据结构,这也就意味着最早入栈的函数最后才会返回,而最后入栈的函数会最先返回。
"""
设计一个函数,检查字符串 (算术表达式) 中的括号是否完全匹配。
算法:循环遍历字符串的每个字符,如果遇到左括号,将元素添加到栈中;
如果遇到右括号,就从栈中取出一个左括号,如果取不出左括号,直接返回 False;
如果能够取出左括号,检查二者是否匹配;如果不匹配,直接返回 False;
如果匹配,继续检查。循环遍历结束以后,检查栈是否为空,如果不为空,返回 False。
栈(stack) ---> FILO ---> 有一个列表,如果我们将添加和删除元素都限制在列表的末尾
- 添加元素: append() ---> 入栈
- 删除元素: pop() ---> 出栈
队列(queue) ---> --->FIF0---> 有一个列表,如果我们限制它只能在尾部添加元素,只能在头部删除元素
- 添加元素: append() ---> 入队
- 删除元素: pop(0) ---> 出队
"""
def check(expression: str) -> bool:
"""
检查字符串中的括号是否完全匹配
:param expression: 表达式
:return: 括号匹配返回 True, 括号不匹配返回 False
"""
stack = []
bdict = {'(': ')', '[':']', '{': '}'}
for ch in expression:
if ch in ('(', '[', '{'):
stack.append(ch)
elif ch in (')', ']', '}'):
if len(stack) > 0:
left = stack.pop()
if bdict[left] != ch:
return False
else:
return False
return len(stack) == 0
if __name__ == '__main__':
print(check('{{{)(}{}}[][]])('))
print(check('{(12 - 10 = 10)}'))
进入正题
在面向对象的世界中,一切皆为对象,所以类和函数也是对象。函数的参数和返回值可以是任意类型的对象,这就意味着函数本身也可以作为函数的参数或返回值,这就是所谓的高阶函数。
Python学习日志10课 - 高阶函数和高级应用
把函数作为函数的参数或者返回值。
在Python中的函数是一等函数(一等公民):
- 函数可以作为函数的参数
- 函数可以作为函数的返回值
- 函数可以直接赋值给变量
(1).递归方法
根据上面提到的高阶函数的定义,我们首先提出函数调用自身的情况。
-
函数调用自己是以下这样一个过程:
-
保存现场 —> 调用函数 —> 恢复现场
-
每个函数都有属于自己的栈结构(大概512K-1M,因为要求要高效,所以不会很大),保存现场就是将整个栈结构保存起来 -
结论︰函数可以调别人,也可以调自己,但是递归调用一定要确保能够快速收敛(尽快结束,不要无限制的调用),在有限次调用下结束调用,无休止的调用,那么迟早会将栈空间消耗殆尽,导致程序崩溃。
def fac(num: int) -> int:
"""求阶乘(递归写法)"""
if num == 0:
return 1
return num * fac(num - 1)
if __name__ == '__main__':
for i in range(1, 21):
print(fac(i))
如果直接通过Python解释器运行当前代码这个文件,那么__name__ 的值是’__main__ ‘。
但是如果是在其他的模块(文件)中导入了当前模块,那么此时__name__ 的值就是后面的模块
写函数时这样做就避免后面导入该模块运行程序时,会把导入模块的输出一起输出这种事情的出现。
小知识:
-
递归 —> 函数如果直接或间接的调用了自身,这种调用称为递归调用 -
在PyCharm 敲main 然后按制表键,if __name__ == '__main__' 这一段就会直接出来。
(2).高内聚低耦合
高阶函数除了函数本身也可以作为函数的参数或返回值这个特性之外,我认为实现了代码终极目标高内聚低耦合的函数,也能称之为高阶函数。
- **低内聚**:当把函数中的`print`、`二元运算符`、`循环`、`input`都减少甚至去掉,并且代码变得更易读,作用效果更大时
- **高耦合**:当把函数中的副作用清除的时候(如:要求程序进行排序输出元素下标,你若改变一段数据本身的顺序,那么下标也被改变)
例子1如下,其中init_value 代表运算的初始值,op 代表二元运算函数。
import operator
def calculatec(init_value, op, *args, **kwargs):
total = init_value
for arg in args:
if type(arg) in (int, float):
total = op(total, arg)
for value in kwargs.values():
if type(value) in (int, float):
total = op(total, value)
return total
print(calculatec(0, operator.add, 11, 22, 33, 44))
print(calculatec(1, operator.mul, 11, 22, 33, 44))
print(calculatec(249, operator.sub, 11, 22, 33, 44))
print(calculatec(10000, operator.truediv, 11, 22, 33, 44))
通过对高阶函数的运用,calculatec 函数不再和加法运算,乘法运算耦合,所以灵活性和通用性会变强。需要注意的是,将函数作为参数和调用函数是有显著的区别的,调用函数需要在函数名后面跟上圆括号,而把函数作为参数时只需要函数名即可。Python内置函数中有不少高阶函数,我们前面提到过的map 函数就是高阶函数,map 函数可以实现对序列中元素的映射。
例子2如下,函数一开时对列表进行全切片,相当于复制了一份代码来进行排序,后面的输出索引返回的时原列表的下标。
"""
冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字#3母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。
"""
def bubble_sort(items, ascending=True):
items = items[:]
for i in range(1, len(items)):
swapped = False
for j in range(0, len(items) - i):
if items[j] > items[j + 1]:
items[j], items[j + 1] = items[j + 1], items[j]
swapped = True
if not swapped:
break
if not ascending:
items = items[::-1]
return items
if __name__ == '__main__':
nums = [35, 96, 12, 78, 56, 64, 39, 80]
print(nums)
print(bubble_sort(nums))
(3).Lambda函数
Lambda函数 —> Python中的Lambda 函数是没有的名字函数,所以很多人也把它叫做匿名函数,而且它是用一句话就能写完的函数,唯一的表达式就是函数的返回值。在使用高阶函数的时候,如果作为参数或者返回值的函数本身非常简单,一行代码就能够完成,这时候我们就可以使用Lambda 函数。
def bubble_sort(items, ascending=True, gt=lambda x, y: x > y):
"""
对列表元素进行冒泡排序
:param items: 待排序的列表
:param ascending: 是否使用升序
:param gt: 比较两个元素大小的函数
:return: 返回冒泡排序后的列表
"""
items = items[:]
for i in range(1, len(items)):
swapped = False
for j in range(0, len(items) - i):
if gt(items[j], items[j + 1]):
items[j], items[j + 1] = items[j + 1], items[j]
swapped = True
if not swapped:
break
if not ascending:
items = items[::-1]
return items
if __name__ == '__main__':
nums = [35, 96, 12, 78, 56, 64, 39, 80]
letters = ['pink', 'ggbond', 'rainbow', 'zoo', 'internationalization']
print(nums)
print(bubble_sort(nums))
print(bubble_sort(letters, gt=lambda x, y: len(x) > len(y), ascending=False))
还可以对(2)中的例子1用Lambda 实现
def calculatec(*args, init_value, op, **kwargs):
total = init_value
for arg in args:
if type(arg) in (int, float):
total = op(total, arg)
for value in kwargs.values():
if type(value) in (int, float):
total = op(total, value)
return total
print(calculatec(25, init_value=100, op=lambda x, y: x // y))
有很多函数在Python中用一行代码就能实现,我们可以用Lambda函数来定义这些函数,调用Lambda函数就跟调用普通函数一样,代码如下所示。
import operator, functools
fac = lambda num: functools.reduce(operator.mul, range(1, num + 1), 1)
is_prime = lambda x: x > 1 and all(map(lambda f: x % f, range(2, int(x ** 0.5) + 1)))
print(fac(10))
print(is_prime(9))
提示1:上面使用的reduce 函数是Python标准库functools 模块中的函数,它可以实现对数据的归约操作,通常情况下,过滤(filter)、映射(map)和归约(reduce)是处理数据中非常关键的三个步骤,而Python的标准库也提供了对这三个操作的支持。
提示2:上面使用的all 函数是Python内置函数,如果传入的序列中所有布尔值都是True ,all 函数就返回True ,否则all 函数就返回False 。
小练习
斐波那契数列的递归方法实现
def fib(n):
"""斐波那契数列的实现(递归方法"""
if n in (1, 2):
return 1
return fib(n - 1) + fib(n - 2)
if __name__ == '__main__':
for i in range(1, 21):
print(fib(i))
其实这样做效率很低,不如直接写,但是主要是想让大家了解递归这个函数的高阶应用。
总结
函数的递归调用一定要注意收敛条件和递归公式,找到递归公式才有机会使用递归调用,而收敛条件确定了递归什么时候停下来。函数调用通过内存中的栈空间来保存现场和恢复现场,栈空间通常都很小,所以递归如果不能迅速收敛,很可能会引发栈溢出错误,从而导致程序的崩溃。Python中的函数也是对象,所以函数可以作为函数的参数和返回值,也就是说,在Python中我们可以使用高阶函数。写程序要尽量保证做到高内聚低耦合,不与python内置耦合同时保证程序无副作用。如果我们要定义的函数非常简单,只有一行代码且不需要名字,可以将函数写成Lambda函数(匿名函数)的形式。
感谢学习陪伴,您的点赞,评论就是我更新的动力
|