面向过程: 分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了
面向对象: 把问题中的事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描述某个事物在整个解决问题的步骤中的行为
示例: 设计棋局
面向过程:
面向对象:
优缺点
面向过程
优点: 性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。
缺点: 没有面向对象易维护、易复用、易扩展
面向对象
优点: 易维护、复用、扩展,由于面向对象所具有的封装、继承、多态的特性,可以设计出低耦合的系统,使系统更加灵活
缺点: 性能比面向过程低
函数是Python内建支持的一种封装,通过把大段代码拆成函数,通过一层一层的函数调用就可以把复杂任务分解成简单的任务,这种分解称之为面向过程的程序设计。函数就是面向过程的程序设计的基本单元
函数式编程也可以归结到面向过程的程序设计,但其思想更接近数学计算。
函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的
函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!
函数式编程中要求函数是一等公民
通常通过以下几点来判断函数是否是一等公民
Python对函数式编程提供部分支持。由于Python允许使用变量,因此,Python不是纯函数式编程语言。
Python 对函数式编程提供部分支持。主要体现在下面几个方面:
一个函数可以接收另一个函数作为参数,这种函数就称之为高阶函数(map, filter, reduce, sorted)
MapReduce: Simplified Data Processing on Large Clusters
map 映射函数
map(function, iterable, …)
lst = [1,2,3,4,5,6,7]
lst2 = [10,100,1000,10000]
def f1(x,y):
return x + y
map后面可以接受多个可迭代对象,那传入几个可迭代对象,前面的函数就要接受几个参数
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)
例如:
去掉偶数,保留奇数[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 reduce
s = [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 = 0
for n in args:
ax = ax + n
return ax
return 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==f2
False
f1()和f2()的调用结果互不影响。
当一个函数返回了一个函数后,其内部的局部变量还被新函数引用
需要注意的问题是,返回的函数并没有立刻执行,而是直到调用了f()才执行
def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs
f1, f2, f3 = count()
实际结果是
>>> f1()
9
>>> f2()
9
>>> f3()
9
全部都是9!原因就在于返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了3,因此最终结果为9。
返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:
def count():
def f(j):
def g():
return j*j
return g
fs = []
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
使用闭包,就是内层函数引用了外层函数的局部变量。如果只是读外层变量的值,我们会发现返回的闭包函数调用一切正常:
def inc():
x = 0
def fn():
# 仅读取x的值:
return x + 1
return fn
f = inc()
print(f()) # 1
print(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