一、编程范式

编程范式主要有三种:命令式编程(Imperative)、声明式编程(Declarative)和函数式编程(Functional)。

1、命令式编程(Imperative):

命令式编程的主要思想是关注计算机执行的步骤,即一步一步告诉计算机先做什么再做什么。

import random
list1=[random.randrange(10) for i in range(5)]
result=[]
for x in list1:if x>5:result.append(x)
print(result)

很明显,这个样子的代码是很常见的一种,不管你用的是 C, C++ 还是 C#, Java, Javascript, BASIC, Python, Ruby 等等,你都可以以这个方式写。

2、声明式编程(Declarative):

声明式编程是以数据结构的形式来表达程序执行的逻辑。它的主要思想是告诉计算机应该做什么,但不指定具体要怎么做。 SQL 语句就是最明显的一种声明式编程的例子,例如:

SELECT * FROM collection WHERE num > 5

除了 SQL,网页编程中用到的 HTML 和 CSS 也都属于声明式编程。通过观察声明式编程的代码我们可以发现它有一个特点是它不需要创建变量用来存储数据。另一个特点是它不包含循环控制的代码如 for, while。

3、函数式编程(Functional):

函数式编程和声明式编程是有所关联的,因为他们思想是一致的:即只关注做什么而不是怎么做。但函数式编程不仅仅局限于声明式编程。函数式编程最重要的特点是“函数第一位”,即函数可以出现在任何地方,比如你可以把函数作为参数传递给另一个函数,不仅如此你还可以将函数作为返回值,还可以将函数赋值给一个变量(即让变量指向函数)。常见的编程语言一半都已经提供了对这种编程方式的支持,如 JavaScript,再有 C# 中的 LINQ 和 Java 中的 Lambda 和闭包的概念。

二、函数式编程(Functional Programming)

函数式编程是一种编程方式,它将电脑运算视为函数的计算。函数编程语言最重要的基础是λ演算(lambda calculus),而且λ演算的函数可以接受函数当作输入(参数)和输出(返回值)。其中将一个个函数视为最小的运算单元,不关心中间的过程,而重在最后的输出,对于同样的输入,必定有同样的输出,不依赖周围环境,不改变其它变量或者状态。这里的函数可理解成是数学意义上的函数:给定一个数集A,对A施加对应法则f,记作f(A),得到另一数集B,也就是B=f(A)。那么这个关系式就叫函数关系式,简称函数。函数概念含有三个要素:定义域A、值域C和对应法则f。其中核心是对应法则f,它是函数关系的本质特征。函数式编程,虽然也可以归结到面向过程的程序设计,但其思想更接近数学计算。

1、函数嵌套定义及变量的作用域

Python允许函数的嵌套定义,在函数内部可以再定义另外一个函数。下面这个例子中,在嵌套函数里,演示了全局变量、局部变量、nonlocal变量的作用域关系及关键字global、nonlocal的作用。

spam="我是最初的全局变量"
def scope_test():def do_local():spam="我是局部变量"def do_nonlocal():nonlocal spamspam="我不是局部变量也不是全部变量"def do_global():global spamspam="我是全部变量"spam="原来的值"do_local()print("局部变量赋值后:",spam)do_nonlocal()print("nonlocal变量赋值后:",spam)do_global()print("全局变量赋值后:",spam)
print(spam)
scope_test()
print(spam)

2、可调用对象

函数属于Python可调用对象之一,由于构造方法的存在,类也是可调用的。像list(),tuple(),dict()、set()这样的工厂函数实际上都是调用了类的构造方法。另外,任何包含__call__方法的类的对象也是可调用的。实质上,可调用对象是由于实现了方法__call__,内置函数callable()就是检测该方法的存在情况。故此,判断Python对象是否可调用有三种方法:

(1)使用内置的callable()函数

callable(func)

(2)判断对象是否实现__call__方法

hasattr(obj,"__call__")

(3)判断对象类型是否FunctionType

type(func) is FunctionType
#或者
isinstance(func,FunctionType)

上面第1、2种验证方法是等价的,第3种方法,需要注意的是,在验证类方法和实例方法时,会返回False,原因是它们的类型都是,不是FunctionType。在Python中分为函数(function)和方法(method)。函数是Python中一个可调用对象(用户自定义的可调用对象,lambda表达式创建的函数,其类型都是FunctionType),方法是一种特殊的函数,类方法和类绑定,实例方法与实例绑定,所以两者的类型都是method,而静态方法,本身既不和类进行绑定,也不和实例绑定,不符合上述定义,故此其类型应该是FunctionType。

class ClassA:@staticmethoddef func_a():pass@classmethoddef func_b(cls, arg):passdef func_c(self, arg):passdef func_d():passclass_a = ClassA()

3、高阶函数

变量可以指向函数,函数名也是变量

把函数当做参数传入函数——map(),reduce(),filter(),sort()

函数作为返回值

4、函数闭包(Closure)与自由变量

闭包并不只是一个python中的概念,在函数式编程语言中应用较为广泛。理解python中的闭包一方面是能够正确的使用闭包,另一方面可以好好体会和思考闭包的设计思想。

维基上对闭包的解释:在计算机科学中,闭包(Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。(注:与点集里的闭包联想)

程序被加载到内存执行时,函数定义的代码被存放在代码段中。函数被调用时,会在栈上创建其执行环境,也就是初始化其中定义的变量和外部传入的形参以便函数进行下一步的执行操作。当函数执行完成并返回函数结果后,函数栈帧便会被销毁掉。函数中的临时变量以及存储的中间计算结果都不会保留。下次调用时唯一发生变化的就是函数传入的形参可能会不一样,栈帧会重新初始化。

#举例:
def cook():food = 'apple'def wrapper():print(food)return wrapper
#以下运行情况
value = cook()
value()    #按道理来说 food 变量就应该随cook()调用结束被销毁掉了,但实际上 value() 顺利的输出了 food 的值。
#以下删除cook()函索后再运行
del cook
value()   #即使将 cook() 函数销毁了,food 的值都还存在。

可见,高阶函数中,内层函数携带外层函数中的参数、变量及其环境,一同存在的状态(即使已经离开了创造它的外层函数)被称之为函数闭包。被携带的外层变量被称为自由变量,有时候也被形容为外层变量被闭包捕获了。外层函数传递的参数甚至可以是个函数。总之闭包是一个嵌套函数,是从外层函数返回的内层函数,它可以访问外层作用域中的自由变量。

既然外层函数可以携带参数,那被返回的内层函数当然也可以带参数。用闭包携带参数并返回函数的这个特性,可以很方便的在一个底层的函数框架上,组装出不同的功能。

闭包中的自由变量有两个神奇的特性:闭包可以持有状态,即自由变量在闭包存在的期间,其中的值也会一直存在;闭包与闭包之间的状态是隔离的。如:

# 记录每次取得的分数
def make_score():lis = []      #自由变量是可修改变量def inner(x):lis.append(x)print(lis)return inner
score1 = make_score()
score1 = make_score()
score1(1)
score1(1)
score2(3)
score2(4)
# 记录成绩总分
def make_score():total = 0    #自由变量是不可修改变量,需要内层函数里加关键字nonlocaldef inner(x):nonlocal totaltotal += xprint(total)return innertotal1 = make_score()
total2 = make_score()
total1(2)
total1(5)
total2(10)
total2(20)

因为内层函数只有在调用时才执行(延迟执行的),故此有延迟陷阱的现象。如:

funcs = []
for i in range(3):def inner():print(i)funcs.append(inner)print(f'i is: {i}')
funcs[0]()
funcs[1]()
funcs[2]()
# 输出:
# 2
# 2
# 2

#解决方案就是用闭包将 i 的值立即捕获:

funcs = []
for i in range(3):def outer(a):def inner():print(a)return innerfuncs.append(outer(i))print(f'i is: {i}')
funcs[0]()
funcs[1]()
funcs[2]()

函数闭包(function closures)有许多应用,如装饰器、组合函数、函数柯里化等。

5、修饰器(decorator)

修饰器是函数闭包的一个重要应用。修饰器本质上是一个嵌套函数,只不过这个函数接收被修饰函数作为参数并对其进行一定改造之后返回新函数(内层函数)。静态方法@staticmethod、类方法@classmethod、属性@property等也都是通过修饰器实现的,Python中还有很多这样的用法。

def a_new_decorator(a_func):def wrapTheFunction():print("I am doing some boring work before executing a_func()")a_func()print("I am doing some boring work after executing a_func()")return wrapTheFunctiondef a_function_requiring_decoration():print("I am the function which needs some decoration to remove my foul smell")a_function_requiring_decoration()
#outputs: "I am the function which needs some decoration to remove my foul smell"a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)
#now a_function_requiring_decoration is wrapped by wrapTheFunction()a_function_requiring_decoration()
#outputs:I am doing some boring work before executing a_func()
#        I am the function which needs some decoration to remove my foul smell
#        I am doing some boring work after executing a_func()

上面展示了Python 运用了函数嵌套定义实现装饰器的原理!它们封装一个函数,并且用这样或者那样的方式来修改它的行为。现在你也许疑惑,我们在代码里并没有使用 @ 符号?那只是一个简短的方式来生成一个被装饰的函数。这里是我们如何使用 @ 来运行之前的代码:

@a_new_decorator
def a_function_requiring_decoration():"""Hey you! Decorate me!"""print("I am the function which needs some decoration to remove my foul smell")a_function_requiring_decoration()
#outputs: I am doing some boring work before executing a_func()
#         I am the function which needs some decoration to remove my foul smell
#         I am doing some boring work after executing a_func()

但是,如果我们运行如下代码会存在一个问题:

print(a_function_requiring_decoration.__name__)
# Output: wrapTheFunction
print(a_function_requiring_decoration.__doc__)
# Output: ""

这并不是我们想要的!Ouput输出应该是"a_function_requiring_decoration"。这里的函数被warpTheFunction替代了。它重写了我们函数的名字和注释文档(docstring)。幸运的是Python提供给我们一个简单的函数来解决这个问题,那就是functools.wraps。我们修改上一个例子来使用functools.wraps:

from functools import wrapsdef a_new_decorator(a_func):@wraps(a_func)def wrapTheFunction():print("I am doing some boring work before executing a_func()")a_func()print("I am doing some boring work after executing a_func()")return wrapTheFunction@a_new_decorator
def a_function_requiring_decoration():"""Hey yo! Decorate me!"""print("I am the function which needs some decoration to remove my foul smell")

现在再查看被修饰函数的函数名字和注释文档(docstring),显示的就是正确的了。一般定义修饰器的蓝本规范如下:

from functools import wrapsdef decorator_name(f):@wraps(f)def decorated(*args, **kwargs):if not can_run:return "Function will not run"return f(*args, **kwargs)return decorated@decorator_name
def func():return("Function is running")can_run = True
print(func())
# Output: Function is runningcan_run = False
print(func())
# Output: Function will not run

注意:@wraps接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等的功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性。

编程范式及函数式编程相关推荐

  1. 编程范式:函数式编程防御式编程响应式编程契约式编程流式编程

    不长的编码生涯,看到无数概念和词汇:面向对象编程.过程式编程.指令式编程.函数式编程.防御式编程.流式编程.响应式编程.契约式编程.进攻式编程.声明式编程--有种生无可恋的感觉. 本文试图加以汇总和整 ...

  2. 编程范式——初探函数式编程

    引入 策略模式 VS 函数式编程 Java8中介绍Lambda表达式时,从硬编码到策略模式,再到内部类一步步优化而来.理论上来说,我们用Lambda表达式实现的功能,用策略模式.匿名函数也能实现,但为 ...

  3. 函数式编程和面向对象式编程_比较函数式编程,命令式编程和面向对象的编程

    函数式编程和面向对象式编程 As Oracle Corporation has introduced some Functional constructs in Java SE 8, now-a-da ...

  4. Scala 编程基础 C(函数式编程)

    文章目录 1. 函数定义和高阶函数 2. 针对集合的操作 3. 函数式编程实例:WordCount 1. 函数定义和高阶函数 Scala是一门多范式编程语言,混合了面向对象编程和函数式编程的风格.在过 ...

  5. 成为javascript大神要掌握,面向对象编程技术、面向方面编程技术和函数式编程技术

    面向对象编程技术.面向方面编程技术和函数式编程技术 mart一下,面向对象,面向切面,有一定的了解,函数式编程没接触过,得安排时间深入下.还有元编程.

  6. c语言是函数式原型的编程,编程范式|程序世界里的编程范式,探索编程本质

    最近看了一些关于编程范式的文章,简要做一些小结和记录 什么是编程范式 在现实生活中,为了适配各种规格的螺帽,我们需要许多种类的螺丝刀. 在编程世界中,静态语言有许多种类的数据类型. 不过,我们可以发现 ...

  7. python函数式编程模式_函数式编程指引

    概述¶ 本章介绍函数式编程的基本概念.如您仅想学习 Python 语言的特性,可跳过本章直接查看 迭代器. 编程语言支持通过以下几种方式来解构具体问题: 大多数的编程语言都是 过程式 的,所谓程序就是 ...

  8. java函数式编程_说说函数式编程的那些事

    今天这篇文章我们主要来聊聊函数式编程的思想. 函数式编程有用吗? 什么是函数式编程? 函数式编程的优点. 总所周知 JavaScript 是一种拥有很多共享状态的动态语言,慢慢的,代码就会积累足够的复 ...

  9. java8 函数式编程_Java 8函数式编程:延迟实例化

    java8 函数式编程 单例通常会延迟实例化自己,有时,如果对象足够重,则可以延迟实例化类字段. 通常,在走惰性路线时,getter方法(或accessor )必须具有一段代码,该代码块在返回对象之前 ...

最新文章

  1. python内置属性类_Python内置类属性,元类研究
  2. 客户端远程连接Oracle数据库
  3. Docker本地私有仓库的建立
  4. umail邮件服务器如何保证数据安全性?
  5. python varchar_在python中声明varchar
  6. HDU 1754 I Hate It(线段树版)
  7. 如何查看CentOS版本方法
  8. 系统性能优化策略案例
  9. nodejs实现桌面程序exe
  10. 第十一章:项目风险管理 - (11.4 实施定量风险分析)
  11. 浏览器是先执行js还是先加载HTML,在HTML中使用JavaScript(浏览器对js的加载机制分析)...
  12. 用anaconda启动Spyder报错
  13. html立方体旋转展开,html5—旋转立方体
  14. metasploit msfvenom与veil绕过杀毒软件
  15. excel之工作表工作簿保护暴力撤销
  16. 工作介绍xml书包文件
  17. linux删除带用户的群组,Linux账号群组删除等相关操作
  18. 性能测试——各大厂质量保障实践分享汇总
  19. ios wkweb设置图片_在iOS中使用WKWebView如何支持展示webp格式图片(包括本地html)?...
  20. 新浪微博被约谈下架“热搜榜”事件舆情监测报告

热门文章

  1. 将PNG图片转成eps图片的方法
  2. OpenGL基础图形编程(一)
  3. Linux如何被启动(一)
  4. 挖财的 Kubernetes 容器化之路
  5. 【BZOJ2725】【Violet 6】故乡的梦
  6. GIS矢量数据上传到postgresql数据库的三种方式
  7. oracle报错分类,localdateTime与oracle映射报错无效列类型
  8. 全能测试库Go Monkey,已支持arm64,还有了这些功能增强
  9. 华为畅享7鸿蒙,触手可及的千元手机精品:华为畅享7体验评测
  10. 图片怎么转换成文字?这四种方法分分钟完成转换