py_23 面向过程编程和函数式
一 编程范式
编程范式指的就是编程的套路,打个比方,如果把编程的过程比喻为练习武功,那编程范式指的就是武林中的各种流派,而在编程的世界里常见的流派有:`面向过程、函数式、面向对象`
二 面向过程
面向过程的核心是过程二字,过程指的是解决问题的步骤,即先干什么再干什么
优点:复杂的问题流程化,进而简单化
缺点:可扩展性差,修改流水线的任意一个阶段,都会牵一发而动全身
三 函数式
lambda: lambda 参数1,参数2: 表达式
调用:
1.res=(lambda x,y,z:x+y+z)(1,2,3)
2.func=lambda x,y,z:x+y+z res=func(1,2,3)
有名字的函数与匿名函数的对比
有名函数:循环使用,保存了名字,通过名字就可以重复引用函数功能
匿名函数:一次性使用,随时随时定义
应用: max,min,sorted,map,reduce,filter
max(iterable, key=lambda k:dict[k])
min(iterable, key=lambda k:dict[k])
sorted(iterable,key=lambda k:dict[k],reverse=True) False 升序 返回是一个新列表
map(lambda x:x*2, iterable) 映射 得到一个迭代器对象
filter(lambda x:x>3, iterable) 过滤不满足函数返回条件x>3的,得到一个迭代器对象
reduce(lambda x,y:x+y,[2,4,6],1) 累加操作或字符串拼接 返回值是一个合并之后的结果
一 编程范式
很多初学者在了解了一门编程语言的基本语法和使用之后,面对一个’开发需求‘时仍然会觉得无从下手、没有思路/套路,本节主题“编程范式”正是为了解决该问题,那到底什么是编程范式呢?
编程范式指的就是编程的套路,打个比方,如果把编程的过程比喻为练习武功,那编程范式指的就是武林中的各种流派,而在编程的世界里常见的流派有:面向过程、函数式、面向对象
。
在正式介绍前,我们需要强调:“功夫的流派没有高低之分,只有习武的人才有高低之分“,在编程世界里更是这样,各种编程范式在不同的场景下都各有优劣,谁好谁坏不能一概而论,下面就让我们来一一解读它们。
二 面向过程
2.1 定义
面向过程的核心是过程二字,过程指的是解决问题的步骤,即先干什么再干什么
基于面向过程设计程序就好比在设计一条流水线,是一种机械式的思维方式
2.2优点:
复杂的问题流程化,进而简单化
2.3缺点:
可扩展性差,修改流水线的任意一个阶段,都会牵一发而动全身
2.4应用:
函数的参数传入,是函数吃进去的食物,而函数return的返回值,是函数拉出来的结果,
面向过程的思路就是,把程序的执行当做一串首尾相连的功能,该功能可以是函数的形式,
然后一个函数吃,拉出的东西给另外一个函数吃,另外一个函数吃了再继续拉给下一个函数吃.
扩展性要求不高的场景,典型案例如linux内核,git,httpd
举例
流水线1:
用户输入用户名、密码--->用户验证--->欢迎界面
流水线2:
用户输入sql--->sql解析--->执行功能
复制代码
"""
#=============复杂的问题变得简单
#注册功能:
#阶段1: 接收用户输入账号与密码,完成合法性校验
def talk():
while True:
username=input('请输入你的用户名: ').strip()
if username.isalpha():
break
else:
print('用户必须为字母')
while True:
password1=input('请输入你的密码: ').strip()
password2=input('请再次输入你的密码: ').strip()
if password1 == password2:
break
else:
print('两次输入的密码不一致')
return username,password1
#阶段2: 将账号密码拼成固定的格式
def register_interface(username,password):
format_str='%s:%s\n' %(username,password)
return format_str
#阶段3: 将拼好的格式写入文件
def handle_file(format_str,filepath):
with open(r'%s' %filepath,'at',encoding='utf-8') as f:
f.write(format_str)
def register():
user,pwd=talk()
format_str=register_interface(user,pwd)
handle_file(format_str,'user.txt')
register()
#=============牵一发而动全身,扩展功能麻烦
#阶段1: 接收用户输入账号与密码,完成合法性校验
def talk():
while True:
username=input('请输入你的用户名: ').strip()
if username.isalpha():
break
else:
print('用户必须为字母')
while True:
password1=input('请输入你的密码: ').strip()
password2=input('请再次输入你的密码: ').strip()
if password1 == password2:
break
else:
print('两次输入的密码不一致')
role_dic={
'1':'user',
'2':'admin'
}
while True:
for k in role_dic:
print(k,role_dic[k])
choice=input('请输入您的身份>>: ').strip()
if choice not in role_dic:
print('输入的身份不存在')
continue
role=role_dic[choice]
return username,password1,role
#阶段2: 将账号密码拼成固定的格式
def register_interface(username,password,role):
format_str='%s:%s:%s\n' %(username,password,role)
return format_str
#阶段3: 将拼好的格式写入文件
def handle_file(format_str,filepath):
with open(r'%s' %filepath,'at',encoding='utf-8') as f:
f.write(format_str)
def register():
user,pwd,role=talk()
format_str=register_interface(user,pwd,role)
handle_file(format_str,'user.txt')
register()
#ps:talk内对用户名\密码\角色的合法性校验也可以摘出来做成单独的功能,
# 但本例就写到一个函数内了,力求用更少的逻辑来为大家说明过程式编程的思路
# 示例:复杂的问题变得简单,但扩展功能麻烦
三 函数式
函数式编程并非用函数编程这么简单,而是将计算机的运算视为数学意义上的运算,比起面向过程,函数式更加注重的是执行结果而非执行的过程,代表语言有:Haskell、Erlang。而python并不是一门函数式编程语言,但是仍为我们提供了很多函数式编程好的特性,如lambda,map,reduce,filter
3.1 匿名函数与lambda
对比使用def关键字创建的是有名字的函数,使用lambda关键字创建则是没有名字的函数,即匿名函数,语法如下
lambda 参数1,参数2,...: expression
举例
# 1、定义
#与函数有相同的作用域,但是匿名意味着引用计数为0,使用一次就释放,除非让其有名字
lambda x,y,z:x+y+z
#等同于
def func(x,y,z):
return x+y+z
# 2、调用
# 方式一:
res=(lambda x,y,z:x+y+z)(1,2,3)
# 方式二:
func=lambda x,y,z:x+y+z # “匿名”的本质就是要没有名字,所以此处为匿名函数指定名字是没有意义的
res=func(1,2,3)
lambda 格式 | 说明 |
---|---|
lambda x, y: x*y | 函数输入是 x 和 y,输出是它们的积 x*y |
lambda :None | 函数没有输入参数,输出是 None |
lambda :aaa(3,4) | 函数没有输入参数,输出是 aaa(3,4)的结 果 |
lambda *args:sum(args) | 输入是任意个数的参数,输出是它们的和 |
lamdba **kwargs : 1 | 输入是任意键值对参数,输出是 1 |
有名字的函数与匿名函数的对比
#有名函数与匿名函数的对比
有名函数:循环使用,保存了名字,通过名字就可以重复引用函数功能
匿名函数:一次性使用,随时随时定义
应用:max,min,sorted,map,reduce,filter
案例1
如果有这样一个列表[1,2,3,4,5,6,7,8,9,10] ,
现在需要将列表里的每个元素乘以2,应该怎么做呢?
# 方式一
a = [1,2,3,4,5,6,7,8,9,10]
for m,n in enumerate(a):
a[m] = n*2
print(a)
# 方式2 map(func,iterable)
new_a = list(map(lambda x:x*2, a))
print(new_a)
# 方式3
b = [x*2 for x in a]
print(b)
案例2
匿名函数与有名函数有相同的作用域,但是匿名意味着引用计数为0,使用一次就释放,所以匿名函数用于临时使用一次的场景,匿名函数通常与其他函数配合使用,我们以下述字典为例来介绍它
salaries={
'siry':3000,
'tom':7000,
'lili':10000,
'jack':2000
}
要想取得薪水的最大值和最小值,我们可以使用内置函数max和min(为了方便开发,python解释器已经为我们定义好了一系列常用的功能,称之为内置的函数,我们只需要拿来使用即可)
>>> max(salaries)
'tom'
>>> min(salaries)
'jack'
内置max和min都支持迭代器协议,工作原理都是迭代字典,取得是字典的键,因而比较的是键的最大和最小值,而我们想要的是比较值的最大值与最小值,于是做出如下改动
# 函数max会迭代字典salaries,每取出一个“人名”就会当做参数传给指定的匿名函数,然后将匿名函数的返回值当做比较依据,最终返回薪资最高的那个人的名字
salaries={
'siry':3000,
'tom':7000,
'lili':10000,
'jack':2000
# max工作原理:迭代iterable 还回元素,key 比较依据。
a = max(salaries, key=lambda k:salaries[k])
print(a) # lili
b = min(salaries, key=lambda k:salaries[k])
print(b) # jack
同理,我们直接对字典进行排序,默认也是按照字典的键去排序的
>>> sorted(salaries)
['jack', 'lili', 'siry', 'tom']
3.2 sorted() 排序
salaries={
'siry':3000,
'tom':7000,
'lili':10000,
'jack':2000
}
# sorted(iterable,key=None,reverse=False) 返回一个新列表 默认升序 True降序
list1 = sorted(salaries,key=lambda k:salaries[k],reverse=True)
print(list1[0])
3.3 map、reduce、filter
-
map 返回是一个迭代器对象
-
reduce 返回是一个合并过后的值
-
filter 返回是一个迭代器对象
函数map、reduce、filter都支持迭代器协议,用来处理可迭代对象,我们以一个可迭代对象array为例来介绍它们三个的用法
array=[1,2,3,4,5]
要求一:对array的每个元素做平方处理,可以使用map函数
map函数可以接收两个参数,一个是函数,另外一个是可迭代对象,具体用法如下
>>> array=[1,2,3,4,5
>>> res=map(lambda x:x**2,array)
>>> res
<map object at 0x1033f45f8>
>>>
解析:map会依次迭代array,得到的值依次传给匿名函数(也可以是有名函数),而map函数得到的结果仍然是迭代器。
>>> list(res) #使用list可以依次迭代res,取得的值作为列表元素
[1, 4, 9, 16, 25]
要求二:对array进行合并操作,比如求和运算,这就用到了reduce函数
reduce函数可以接收三个参数,一个是函数,第二个是可迭代对象,第三个是初始值
# reduce在python2中是内置函数,在python3中则被集成到模块functools中,需要导入才能使用
>>> array=[1,2,3,4,5
>>> from functools import reduce
>>> res=reduce(lambda x,y:x+y,array)
>>> res
15
解析:
1 没有初始值,reduce函数会先迭代一次array得到的值作为初始值,作为第一个值数传给x,然后继续迭代一次array得到的值作为第二个值传给y,运算的结果为3
2 将上一次reduce运算的结果作为第一个值传给x,然后迭代一次array得到的结果作为第二个值传给y,依次类推,知道迭代完array的所有元素,得到最终的结果15
也可以为reduce指定初始值
>>> array=[1,2,3,4,5]
>>> res=reduce(lambda x,y:x+y,array,100)>>> res
115
要求三:对array进行过滤操作,这就用到了filter函数,比如过滤出大于3的元素
>>> array=[1,2,3,4,5
>>> res=filter(lambda x:x>3,array)
解析:filter函数会依次迭代array,得到的值依次传给匿名函数,如果匿名函数的返回值为真,则过滤出该元素,而filter函数得到的结果仍然是迭代器。
>>> list(res)
[4, 5]
提示:我们介绍map、filter、reduce只是为了带大家了解函数式编程的大致思想,在实际开发中,我们完全可以用列表生成式或者生成器表达式来实现三者的功能。
演示代码
# ---------------------------map()映射--------------------
# map(func,iterabal) 映射
L = ["ab","fdsa","dasf","faf","fea","fafa"]
# 需求 将L里面的元素后缀加上SB
# 方法1
L = [x+"SB" for x in L]
print(L) # ['abSB', 'fdsaSB', 'dasfSB', 'fafSB', 'feaSB', 'fafaSB']
# 方法2
L = map(lambda x :x+"SB", L) # map返回是一个迭代器对象
print(L) # <map object at 0x000000000227B160>
print(list(L)) # ['abSBSB', 'fdsaSBSB', 'dasfSBSB', 'fafSBSB', 'feaSBSB', 'fafaSBSB']
#-------------------------------filter过滤-------------------------
# filter(func, iterable) 返回是一个 迭代器对象
L = ['ab', 'fdsa', 'dasfSb', 'faf', 'feaSB', 'fafaSB']
# 过滤掉SB
# 方法1
res = [x for x in L if x.lower().endswith("sb")]
print(res) # ['dasfSb', 'feaSB', 'fafaSB']
# 方法2
res = filter(lambda x:x.lower().endswith("sb"), L)
print(list(res)) # ['dasfSb', 'feaSB', 'fafaSB']
#------------------reduce合并操作--------------
from functools import reduce
# 累加操作 reduce(function, sequence, initial=None)
res = reduce(lambda x,y:x+y,[2,4,6],1)
"""
x=1,y=2传进函数 得到的返回值 3赋值给x
x=3,y=4
x=7,y=6
res = reduce(lambda x,y:x+y,[2,4,6]) 结果为12
如果第三个参数没有,那么就是序列类型第1 第2个元素传给x,y.(x+y的和,第三个元素)
传进函数,依次类推。
"""
print(res)
# 字符串拼接
res = reduce(lambda x,y:x+y,["a","b","c"],"阿登")
print(res) # 阿登abc
res = reduce(lambda x,y:x+y,["a","b","c"])
print(res) # abc