面向过程,面向对象
面向过程:分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了
面向对象:把问题中的事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描述某个事物在整个解决问题的步骤中的行为
示例:设计棋局
面向过程:
开始游戏,红棋先走展示棋局情况判断战况(是否有棋子被吃、胜负情况)黑棋接着走展示棋局情况判断战况(是否有棋子被吃、胜负情况)……
面向对象:
黑棋和红棋对象:负责棋子的走向,两者行为相同棋盘系统:通过黑棋和红棋对象的行为展示棋局情况规则系统:判断“战场”的变化以及胜负等
优缺点
面向过程
优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。
缺点:没有面向对象易维护、易复用、易扩展
面向对象
优点:易维护、复用、扩展,由于面向对象所具有的封装、继承、多态的特性,可以设计出低耦合的系统,使系统更加灵活
缺点:性能比面向过程低
函数式编程(Functional Programming)
函数是Python内建支持的一种封装,通过把大段代码拆成函数,通过一层一层的函数调用就可以把复杂任务分解成简单的任务,这种分解称之为面向过程的程序设计。函数就是面向过程的程序设计的基本单元
函数式编程也可以归结到面向过程的程序设计,但其思想更接近数学计算。
函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的
函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!
函数式编程中要求函数是一等公民
通常通过以下几点来判断函数是否是一等公民
函数可以存储在变量中函数可以作为参数函数可以作为返回值
Python 的函数式编程
Python对函数式编程提供部分支持。由于Python允许使用变量,因此,Python不是纯函数式编程语言。
Python 对函数式编程提供部分支持。主要体现在下面几个方面:
Python 的一些语法,比如lambda、列表解析、字典解析、生成器、iter 等Python 的一些内置函数,包括 map、reduce、filter、all、any、enumerate、zip 等Python 的一些内置模块,比如 itertools、functools 和 operator 模块等Python 的一些第三方库,比如 fn.py, toolz 等Python 将函数视为“第一等公民” – 对象,因此可以作为函数参数也可以作为函数的返回值。包括高阶函数,返回函数,匿名函数,装饰器,偏函数等。
高阶函数
一个函数可以接收另一个函数作为参数,这种函数就称之为高阶函数(map, filter, reduce, sorted)
MapReduce: Simplified Data Processing on Large Clusters
map 映射函数
接收两个参数,第一个参数 function 以参数序列中的每一个元素调用 function 函数,map(function, iterable, …)
返回包含每次 function 函数返回值的新列表返回值是一个迭代器
lst = [1,2,3,4,5,6,7]lst2 = [10,100,1000,10000]def f1(x,y):return x + ymap后面可以接受多个可迭代对象,那传入几个可迭代对象,前面的函数就要接受几个参数print(list(map(f1,lst,lst2)))print(list(map(lambda x,y:x+y, lst, lst2)))
例题:
有列表[1, 2, 3, 4, 5],将所有元素转换成str: [‘1’, ‘2’, ‘3’, ‘4’, ‘5’]
lst = [1,2,3,4,5]print(list(map(str,lst)))
filter 过滤函数
接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判断,filter(function, iterable)
然后返回 True 或 False,最后将返回True 的元素放到新列表中返回值是一个迭代器
例如:
去掉偶数,保留奇数[1,2,3,4,5,6,7,8,9]
print(list(filter(lambda x : x % 2, [1,2,3,4,5,6,7,8,9])))
在一个list中,删掉偶数,只保留奇数
lst=['A','','B',None,‘C’,' ','a',1,0]print(list(filter(lambda x:x and str(x).strip(), lst)))
reduce() 函数会对参数序列中元素进行累积。(这个函数用的时候需要导入)
函数将一个数据集合(链表,元组等)中的所有数据进行下列操作:用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 function 函数运算,最后得到一个结果。
from functools import reduces = [1,3,5,7,9]print(reduce(lambda x,y:x*10+y, s))
sorted 排序函数
sorted()函数也是一个高阶函数,它还可以接收一个key函数来实现自定义的排序key指定的函数将作用于list的每一个元素上,并根据key函数返回的结果进行排序。
把一个序列中的字符串,忽略大小写排序
list1=['bob','about','Zoo','Credit']print(sorted(list1,key=lambda x:x.lower()))print(sorted(list1,key=str.lower))
按value来排序
d1 = {"a":3,"b":4,"c":2,"d":5}print(dict(sorted(d1.items(), key=lambda x:x[1])))
返回函数
高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。
示例:
def lazy_sum(*args):def sum():ax = 0for n in args:ax = ax + nreturn axreturn sum
在这个例子中,我们在函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。
注意一点,当我们调用lazy_sum()时,每次调用都会返回一个新的函数,即使传入相同的参数:
>>> f1 = lazy_sum(1, 3, 5, 7, 9)>>> f2 = lazy_sum(1, 3, 5, 7, 9)>>> f1==f2False
f1()和f2()的调用结果互不影响。
闭包
当一个函数返回了一个函数后,其内部的局部变量还被新函数引用
需要注意的问题是,返回的函数并没有立刻执行,而是直到调用了f()才执行
def count():fs = []for i in range(1, 4):def f():return i*ifs.append(f)return fsf1, f2, f3 = count()
实际结果是
>>> f1()9>>> f2()9>>> f3()9
全部都是9!原因就在于返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了3,因此最终结果为9。
返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:
def count():def f(j):def g():return j*jreturn gfs = []for i in range(1, 4):fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()return fs
结果:
>>> f1, f2, f3 = count()>>> f1()1>>> f2()4>>> f3()9
nonlocal
使用闭包,就是内层函数引用了外层函数的局部变量。如果只是读外层变量的值,我们会发现返回的闭包函数调用一切正常:
def inc():x = 0def fn():# 仅读取x的值:return x + 1return fnf = inc()print(f()) # 1print(f()) # 1
使用闭包时,对外层变量赋值前,需要先使用nonlocal声明该变量不是当前函数的局部变量。
匿名函数
匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。
用匿名函数有个好处,因为函数没有名字,不必担心函数名冲突。此外,匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数
>>> f = lambda x: x * x>>> f<function <lambda> at 0x101c6ef28>>>> f(5)25
同样,也可以把匿名函数作为返回值返回,比如:
def build(x, y):return lambda: x * x + y * y
装饰器
偏函数
functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单
>>> import functools>>> int2 = functools.partial(int, base=2)>>> int2('1000000')64>>> int2('1010101')85