【Python杂烩】通过python学习函数式编程

通过python学习函数式编程

说实话,网络上对函数式编程的说法,重说纷纭,我也查了很多资料,感觉还是迷迷糊糊的,哈哈哈。所以我就不针对函数式编程的概念去深究,因为我觉得目前阶段的我并不能很好的整体剖析函数式编程。所以这里仅仅是针对编程语言对函数式编程的应用的角度去诉说我的个人感受

  • 函数式编程
    • 什么是函数式编程
    • 命令式编程和函数式编程
    • 函数式编程的好处
    • 函数式编程的继续学习
  • 闭包
    • 什么是闭包
    • 闭包的作用
    • Java闭包与Python闭包的区别
  • python匿名函数,高阶函数,装饰器
    • 匿名函数
    • map函数
    • reduce函数
    • filter函数
    • 装饰器

函数式编程


什么是函数式编程

函数式编程是面向数学的抽象,将计算描述为一种表达式求值,一句话,函数式程序就是一个表达式。命令式编程的变量就是函数式编程中表达式的参数。参数应该是无状态不可变的,只要你传入表达式永远是这个参数,那么得到的结果就应该是永远不变的。就像一个数学公式一样。


命令式编程和函数式编程

函数式编程是一种编程范式,我们常见的编程范式有命令式编程,函数式编程,逻辑式编程,常见的面向对象编程是也是一种命令式编程。

  • 命令式编程是面向计算机硬件的抽象,有变量(对应着存储单元),赋值语句(获取,存储指令),表达式(内存引用和算术运算)和控制语句(跳转指令),一句话,命令式程序就是一个冯诺依曼机的指令序列。
  • 函数式编程是面向数学的抽象,将计算描述为一种表达式求值,一句话,函数式程序就是一个表达式。

简单的说,命令式编程需要我们写出逻辑计算的每一个步骤和流程,一步一步达成目的。而函数式编程追求的是过程无关性,只需要知道要什么参数,你要什么结果,过程让函数自己去做。相对比命令式编程,平时我们写的SQL就很像函数式编程,我只要select * from table order by id, 你就能从table表中,找到所有的数据,并根据ID去排序。而不需要你自己去做获取数据和排序的过程。


函数式编程的好处

  • 因为传入表达式的参数必须是不可变的,所以参数是没有状态的。参数不变,无论执行表达式多少次,结果都是一样的。是没有副作用。所以非常利于我们使用并发计算
  • 容易书写,不需要在乎代码执行的过程,在函数式编程中,只需要在乎,参数是什么,要得到的结果是什么。

函数式编程的继续学习

总之,我目前并不能很好的剖析函数式编程的概念,只能从语言应用上表层的角度去看待我所指导的函数式编程。所以如果后序还需要继续研究,那么可以对下面几个概念有更加深入的理解

  • 高阶函数
  • 函数柯里化
  • 闭包
  • 尾递归优化
  • map/reduce模型

闭包


什么是闭包?

@维基百科 -闭包

  • 在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数

@百度百科 - 闭包

  • 闭包包含自由(未绑定到特定对象)变量,这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)。“闭包” 一词来源于以下两者的结合:要执行的代码块(由于自由变量被包含在代码块中,这些自由变量以及它们引用的对象没有被释放)和为自由变量提供绑定的计算环境(作用域)

简而言之,闭包就是一个函数引用了外部环境的自由变量(这个外部环境是指创建该函数的环境)。这个变量本质是不属于该函数的作用域,但是却可以该函数所引用到,并且在创建该自由变量的外部环境消失了,该函数依然可以引用到(按道理,该变量在创建它的环境消失后,应该也要被内存锁释放)。能够满足上面条件的行为,我们就称之为闭包

很多的语言支持嵌套函数, 有的语言不支持。但是没有关系,只要能够让一个函数读取到外部环境的变量,那么我们都可以称之为支持闭包。在PHP、Scala、Scheme、Common Lisp、Smalltalk、Groovy、JavaScript、Ruby、 Python、Go、Lua、objective c、swift 以及Java等语言中都能找到对闭包不同程度的支持。


闭包的作用?

闭包最主要的作用或用途就是:

  • 让内部函数可以访问到外部环境所声明的变量。并且不会随着外部环境的消失,而导致内部函数所引用的外部变量被置零

Java闭包与Python闭包的区别

很多语言都支持闭包,但每种语言所支持的程度都不太一样

  • Java中的闭包就不支持对不可变变量进行改变,仅仅是闭包创建时,将引用变量的值拷贝一份到内部环境中
  • python则可以通过globalnonlocal关键字去声明闭包环境变量,让不可变的环境变量可以在内部函数中得到改变

Java匿名内部类

public class Closure {

    public static void main(String[] args) {
        //外部自由变量
        final Integer num = 10;
        
        //匿名类
        new Thread(new Runnable() {
            
            //匿名类中的内部函数调用外部自由变量,构成闭包
            @Override
            public void run() {
                int sum = num * 2;
                System.out.println(sum);
            }
        }).start();
    }
}

Java lambda表达式

public class Closure {

    public static void main(String[] args) {
        //外部自由变量
        Integer num = 10;
        
        //lambda表达式调用外部自由变量,构成闭包
        new Thread(() -> {
            int sum = num * 2;
            System.out.println(sum);
        }).start();
    }
}

Python嵌套函数

# 定义嵌套函数
def closure():
    num = 10
    def print_sum():
        sum = num * 2
        print(sum)
    return print_sum

func = closure()
func()

python匿名函数,高阶函数,装饰器


匿名函数

python的匿名函数必须由lambda开头, py的匿名函数更应用倾向于叫lambda表达式,而不像是Java, 匿名函数是匿名函数,lambda表达式只是描述匿名函数的其中一种方式。

所以,在Python中,我们所说的lambda表达式其实就是py的匿名函数

  • lambda 表达式
    lambda params : expression
  • py的lambda表达式的表达式部分,不能是一段代码块
# 定义了一个两变量相加的匿名函数
# py中,函数也是对象,可以赋值给一个对象
func = lambda x,y: x + y 
print(func(1,2))
'''
output:
3
'''

map函数

  • 将传入的参数经过map函数运算后得到另一个结果
  • 返回的结果是map类型
# 单参数
list_num = [1,2,3,4,5,6]
result = map(lambda x: x*2,list_num)
print(list(list_num))

"""
output:
[2, 4, 6, 8, 10, 12]
"""
# 多参数
list_num = [1, 2, 3, 4, 5, 6]
list_num2 = [2, 3, 4, 5, 6, 7]
result = map(lambda x,y: x * y, list_num,list_num2)
print(list(result))
"""
output:
[2, 6, 12, 20, 30, 42]
"""

reduce函数

  • 连续计算,例如累加,累乘,累X,后面的结果依赖前面的结果,具体是连续怎么计算 ,要看定义的函数
  • 不同于map函数,py3之后,reduce就不再全局命名空间了,所以需要导入from functools import reduce
  • 返回的结果是累x运算后的类型
# 求1...10的累加结果
from functools import reduce

list_num = [1, 2, 3,4,5,6,7,8,9,10]
result = reduce(lambda x, y: x + y, list_num)
print(result)
"""
output:
55
"""

filter函数

  • 帮助我们从一个系列的元素中过滤掉不需要的元素
  • 返回的是中间结果,filter类型
# 过滤掉值为0的元素
list_num = [1, 0, 0, 1, 0, 1, 0]
result = filter(lambda x: x != 0, list_num)
print(list(result))
"""
output:
[1,1,1]
"""

装饰器

什么是装饰器?

菜鸟教程:
装饰器(Decorators)是 Python的一个重要部分。简单地说:他们是修改其他函数的功能的函数。他们有助于让我们的代码更简短,也更Pythonic(Python范儿)

装饰器有什么作用?

  • 动态代理在Java中通常可以通过拦截器或AOP去实现,但在Python中,装饰器就可以很好的去实现动态代理的功能
  • 装饰器在一定程度上,可以实现Java中类似注解的功能

维基百科

  • Python decorators were inspired in part by Java annotations, and have a similar syntax

装饰器模式 or 代理模式

  • 要说装饰器到底体现的是装饰器模式还是代理模式的思想呢,个人觉得都有,毕竟代理模式和装饰器模式本身在实现上就很相似,仅仅是目的不同而已,所以真的要区分的话,应用针对具体的应用场景去判断当前环境下装饰器在更倾向于那种设计思想
  • Python 的装饰器是通过函数式编程来实现的, Java 的注解通过反射来实现。但在语法上,他们之间是非常相似的

装饰器的使用

当我们想要实现在两个方法f1,f2执行前,都先打印一下执行时间

普通代码场景

import time

# function 1
def f1():
    print('This is a func 1')

# function 2
def f2():
    print('This is a func 2')

# print_time function
def print_time(func):
    print(time.time())
    func()

print_time(f1)
print_time(f2)
"""
output:
1561617754.6278098
This is a func 1
1561617754.6278098
This is a func 2
"""

不完善的装饰器使用场景:

import time

# 装饰器
def decorator(func):
    def wrapper():
        print(time.time())
        func()
    return wrapper

# function 1
def f1():
    print('this is func 1')

# function 2
def f2():
    print('this is func 2')

# 将具体的对象传入,抽象装饰器
# 得到了具体的装饰者
decorator_f1 = decorator(f1)
decorator_f2 = decorator(f2)
decorator_f1()
decorator_f2()
"""
output:
1561617803.2912695
This is a func 1
1561617803.2912695
This is a func 2
"""

装饰器:

import time

# 装饰器
def decorator(func):
    def wrapper():
        print(time.time())
        func()
    return wrapper

# function 1 with decorator
@decorator
def f1():
    print('this is func 1')

# function 2 with decorator
@decorator
def f2():
    print('this is func 2')

f1()
f2()
"""
output:
1561617803.2912695
This is a func 1
1561617803.2912695
This is a func 2
"""

  • 装饰器的本质实现就是一个嵌套函数,一个语法糖。当然这个糖的确是甜。三种装饰器都可以体现出一定的装饰器设计模式的思想,即不修改对象的本身代码,而是通过装饰者对象嵌套要被修饰的对象, 也或者可以说体现了动态代理模式的思想, 实现了AOP的功能
  • 总而言之,三个场景中,最后面类似注解方式的装饰器会更加的有意义,这个糖也会更加的甜,它也不仅仅散发出设计模式的体现。也能让人感受到在Java中使用注解去做切面的感觉(AOP), 当然代码量上是更加的简洁易用

如果想要装饰器更加通用,支持多个参数,或是可变关键字参数,那么我们只需要修改装饰器模板为

# 装饰器
def decorator(func):
    def wrapper(*args,**kwargs):
        print(time.time())
        func(*args,**kwargs)
    return wrapper

参考资料


  • 什么是函数式编程思维?- @作者:掘金
  • 什么是闭包? - @作者:知乎

你可能感兴趣的:(Python杂烩,函数式编程,lambda,python)