PYTHON 之 函数式编程

函数式编程(FunctionalProgramming)

参考图灵学院python课程
  • 基于lambda演算的一种编程方式

    • 程序中只有函数
    • 函数可以作为参数,同样可以作为返回值
    • 纯函数式编程语言: LISP, Haskell
  • Python函数式编程只是借鉴函数式编程的一些特点,可以理解成一半函数式一半Python

  • 需要讲述

    • 高阶函数
    • 返回函数
    • 匿名函数
    • 装饰器
    • 偏函数

lambda表达式

  • 函数: 最大程度复用代码

    • 存在问题: 如果函数很小,很短,则会造成啰嗦
    • 如果函数被调用次数少,则会造成浪费
    • 对于阅读者来说,造成阅读流程的被迫中断
  • lambda表达式(匿名函数):

    • 一个表达式,函数体相对简单
    • 不是一个代码块,仅仅是一个表达式
    • 可以有参数,有多个参数也可以,用逗号隔开
# “小”函数举例
def printA():
    print("AAAAAAAA")
    
    
printA()
# lambda表达式的用法
# 1. 以lambda开头
# 2. 紧跟一定的参数(如果有的话)
# 3. 参数后用冒号和表达式主题隔开
# 4. 只是一个表达式,所以,没有return

# 计算一个数字的100倍数
# 因为就是一个表达式,所以没有return
stm = lambda x: 100 * x 
# 使用上跟函数调用一模一样
stm(89)
stm2 = lambda x,y,z: x+ y*10 + z*100
stm2(4,5,6)

高阶函数

  • 函数作为参数使用的函数,叫高阶函数
# 变量可以赋值
a = 100
b = a
# 函数名称就是一个变量
def funA():
    print("In funA")
    
funB = funA
funB()

以上代码得出的结论:

  • 函数名称是变量
  • funB 和 funA只是名称不一样而已
  • 既然函数名称是变量,则应该可以被当做参数传入另一个函数
# 高阶函数举例
# funA是普通函数,返回一个传入数字的100倍数字

def funA(n):
    return n * 100

# 再写一个函数,把传入参数乘以300倍,
def funB(n ):
    # 最终是想返回300n
    return funA(n) * 3

print(funB(9))

# 写一个高阶函数
def funC(n, f):
    # 假定函数是把n扩大100被
    return f(n) * 3

print( funC(9, funA) )

# 比较funC和funB, 显然funC的写法要优于funB
# 例如:
def funD(n):
    return n*10

# 需求变更,需要把n放大三十倍,此时funB则无法实现
print(funC(7, funD))

系统高阶函数-map

  • 原意就是映射,即把集合或者列表的元素,每一个元素都按照一定规则(通常是函数)进行操作,生成一个新的列表或者集合
  • map函数是系统提供的具有映射功能的函数,返回值是一个迭代对象
# map举例
# 有一个列表,想对列表里的每一个元素乘以10, 并得到新的列表

l1 = [i for i in range(10)]
print(l1)
l2 = []
for i in l1:
    l2.append(i * 10)

print(l2)


# 利用map实现
def mulTen(n):
    return n*10



l3 = map(mulTen, l1 )
# map类型是一个可迭代的结构,所以可以使用for遍历
for i in l3:
    print(i)
  
# 以下列表生成式得到的结果为空, why?
l4 = [i for i in l3]
print(l4)

reduce

  • 原意是归并,缩减
  • 把一个可迭代对象最后归并成一个结果
  • 对于作为参数的函数要求: 必须由两个参数,必须由返回结果
  • reduce([1,2,3,4,5]) == f( f(f(f(1,2),3), 4),5)
  • reduce 需要导入functools包
from functools import reduce

# 定义一个操作函数
# 加入操作函数只是相加
def myAdd(x,y):
    return x + y
    
# 对于列表[1,2,3,4,5,6]执行myAdd的reduce操作
rst = reduce( myAdd, [1,2,3,4,5,6] )
print(rst)

filter 函数

  • 过滤函数: 对一组数据进行过滤,符合条件(通常是函数)的数据会生成一个新的列表并返回
  • 跟map相比较:
    • 相同:都对列表的每一个元素逐一进行操作
    • 不同:
      • map会生成一个跟原来数据想对应的新队列
      • filter不一定,只要符合条件的才会进入新的数据集合
    • filter函数怎么写:
      • 利用给定函数进行判断
      • 返回值一定是个布尔值
      • 调用格式:filter(f, data), f是函数, data是数据
# filter案例
# 对于一个列表,对其进行过滤,偶数组成一个新列表

# 需要定义过滤函数
# 过滤函数要求有输入,返回布尔值
def isEven(a):
    return a % 2 == 0

l = [3,4,56,3,2,3,4556,67,4,4,3,23455,43]

rst = filter(isEven, l)
# 返回的filter内容是一个可迭代对象
print(type(rst))
print(rst)

print([i for i in rst])

高阶函数-排序

  • 把一个序列按照给定算法进行排序
  • key: 在排序钱对每一个元素进行key函数运算,可以理解成按照key函数定义的逻辑进行排序
  • python2 和 python3 相差巨大
# 排序的案例

a = [234,22312,123,45,43,2,3,66723,34]
al = sorted(a, reverse=True)
print(al)
# 排序案例2

a = [-43,23,45,6,-23,2,-4345]
# 按照绝对值进行排序
# abs是求绝对值的意思
# 即按照绝对值的倒叙排列
al = sorted(a, key=abs, reverse=True)

print(al)

# sorted案例

astr = ['dana', 'Danaa', 'wangxiaojing', 'jingjing', 'haha']

str1 = sorted(astr)
print(str1)

str2 = sorted(astr, key=str.lower)
print(str2)

返回函数

  • 函数可以返回具体的值
  • 也可以返回一个函数作为结果
# 定义一个普通函数

def myF(a):
    print('In myF')
    return None
a = myF(8)
print(a)
# 函数作为返回值返回, 被返回的函数在函数体内定义

def myF2():
    
    def myF3():
        print("In myF3")
        return 3
    return myF3
# 使用上面定义
# 调用myF2, 返回一个函数myF3,赋值给f3
f3 = myF2()
print(type(f3))
print(f3)

f3()

输出结果:
<class ‘function’>
<function myF2..myF3 at 0x7f5034036e18>
In myF3
3

# 负责一点的返回函数的例子
# args:参数列表
# 1 myF4定义函数,返回内部定义的函数myF5
# 2. myF5使用了外部变量,这个变量是myF4的参数

def myF4( *args):
    def myF5():
        rst = 0
        for n in args:
            rst += n
        return rst
    return myF5
f5 = myF4(1,2,3,4,5,6,7,8,9,0)
# f5的调用方式
f5()

输出结果:
45

f6 = myF4(10,20,30,40,50)
# f5的调用方式
f6()

输出结果:
150

闭包(closure)

  • 当一个函数在内部定义函数,并且内部的函数应用外部函数的参数或者局部变量,当内部函数被当做返回值的时候,相关参数和变量保存在返回的函数中,这种结果,叫闭包
  • 上面定义的myF4是一个标准闭包结构
  • 闭包关键特征:其变量为nonlocal类型
# 闭包常见坑
def count():
    # 定义列表,列表里存放的是定义的函数
    fs = []
    for i in range(1,4):
        # 定义了一个函数f
        # f是一个闭包结构
        def f():
            return i*i
        fs.append(f)
    return fs

f1,f2,f3 = count()
print(f1())
print(f2())
print(f3())

输出结果:
9
9
9

出现的问题:

  • 造成上述状况的原因是,返回函数引用了变量i, i并非立即执行,而是等到三个函数都返回的时候才统一使用,此时i已经变成了3,最终调用的时候,都返回的是 3*3
  • 此问题描述成:返回闭包时,返回函数不能引用任何循环变量
  • 解决方案: 再创建一个函数,用该函数的参数绑定循环变量的当前值,无论该循环变量以后如何改变,已经绑定的函数参数值不再改变

# 修改上述函数
def count2():
    def f(j):
        def g():
            return j*j
        return g
    fs = []
    for i in range(1,4):
        fs.append(f(i))
    return fs

f1,f2,f3 = count2()
print(f1())
print(f2())
print(f3())

输出结果:
1
4
9

装饰器

def hello():
    print("Hello world")
    
hello()

输出:
Hello world

f = hello
f()

输出:
Hello world

# f和hello是一个函数
print(id(f)) 
print(id(hello))

print(f.__name__)
print(f.__name__)

输出:
139982101828744
139982101828744
hello
hello

现在有新的需求:

对hello功能进行扩展,每次打印hello之前打印当前系统时间
而实现这个功能又不能改动现有代码
==>使用装饰器

装饰器(Decrator)

  • 在不改动函数代码的基础上无限制扩展函数功能的一种机制,本质上讲,装饰器是一个返回函数的高阶函数
    -== 装饰器的使用: 使用@语法, 即在每次要扩展到函数定义前使用@+函数名==
# 任务:
# 对hello函数进行功能扩展,每次执行hello万打印当前时间

import time

# 高阶函数,以函数作为参数
def printTime(f):
    def wrapper(*args, **kwargs):
        print("Time: ", time.ctime())
        return f(*args, **kwargs)
    return wrapper
# 上面定义了装饰器,使用的时候需要用到@, 此符号是python的语法糖
@printTime
def hello():
    print("Hello world")
    
hello()

输出:
Time: Mon Apr 2 21:14:52 2018
Hello world

# 装饰器的好处是,一点定义,则可以装饰任意函数
# 一旦被其装饰,则则把装饰器的功能直接添加到定义函数的功能上

@printTime
def hello2():
    print("今天很高兴,被老板揪着讲课了")
    print("还可以由很多的选择")

hello2()

输出:
Time: Mon Apr 2 21:14:52 2018
Hello world

# 装饰器的好处是,一点定义,则可以装饰任意函数
# 一旦被其装饰,则则把装饰器的功能直接添加到定义函数的功能上

@printTime
def hello2():
    print("今天很高兴,被老板揪着讲课了")
    print("还可以由很多的选择")

hello2()

输出:
Time: Mon Apr 2 21:17:50 2018
今天很高兴,被老板揪着讲课了
还可以由很多的选择

# 上面对函数的装饰使用了系统定义的语法糖
# 下面开始手动执行下装饰器
# 先定义函数

def hello3():
    print("我是手动执行的")
    
hello3()

hello3 = printTime(hello3)
hello3()

f = printTime(hello3)
f()
# 作业:
# 解释下面的执行结果

输出:
我是手动执行的
Time: Mon Apr 2 21:22:39 2018
我是手动执行的
Time: Mon Apr 2 21:22:39 2018
Time: Mon Apr 2 21:22:39 2018
我是手动执行的

偏函数

# 把字符串转化成十进制数字
int("12345")

# 求八进制的字符串12345,表示成十进制的数字是多少
int("12345", base=8)

输出:
5439

# 新建一个函数,此函数是默认输入的字符串是16进制数字
# 把此字符串返回十进制的数字
def int16(x, base=16):
    return int(x, base)

int16("12345")

输出:
74565

偏函数

  • 参数固定的函数,相当于一个由特定参数的函数体
  • functools.partial的作用是,把一个函数某些函数固定,返回一个新函数
import functools
#实现上面int16的功能
int16 = functools.partial(int, base=16)

int16("12345")

输出:
74565

zip

  • 两个可迭代内容生成一个可迭代的tuple元素类型组成的内容
# zip 案例
l1 = [ 1,2,3,4,5]
l2 = [11,22,33,44,55]

z = zip(l1, l2)

print(type(z))
print(z)

for i in z:
    print(i)

输出:
<class ‘zip’>
<zip object at 0x7f61c457ab88>
(1, 11)
(2, 22)
(3, 33)
(4, 44)
(5, 55)

l1 = ["wangwang", "mingyue", "yyt"]
l2 = [89, 23, 78]

z = zip(l1, l2)

for i in z:
    print(i)
    
    
# 考虑下面结果,为什么会为空
l3 = [i for i in z]
print(l3)

输出:
(‘wangwang’, 89)
(‘mingyue’, 23)
(‘yyt’, 78)
[]

enumerate

  • 跟zip功能比较像
  • 可迭代对象里的每一元素,配上一个“索引”
  • 然后索引和内容构成tuple类型
# enumerate案例1
l1 = [11,22,33,44,55]

em = enumerate(l1)

l2 = [i for i in em]
print(l2)

输出:
[(0, 11), (1, 22), (2, 33), (3, 44), (4, 55)]

em = enumerate(l1, start=100)

l2 = [ i for i in em]
print(l2)

输出:
[(100, 11), (101, 22), (102, 33), (103, 44), (104, 55)]

collections模块

  • namedtuple
  • deque

namedtuple

  • tuple类型
  • 是一个可命名的tuple
import collections
Point = collections.namedtuple("Point", ['x', 'y'])
p = Point(11, 22) 
print(p.x)
print(p[0])

输出:
11
11

Circle = collections.namedtuple("Circle", ['x', 'y', 'r'])

c = Circle(100, 150, 50)
print(c)
print(type(c))

# 想检测以下namedtuple到底属于谁的子类
isinstance(c, tuple)

输出:
Circle(x=100, y=150, r=50)
<class ‘main.Circle’>
True

dequeue

  • 比较方便的解决了频繁删除插入带来的效率问题
from collections import deque

q = deque(['a', 'b', 'c'])
print(q)

q.append("d")
print(q)

q.appendleft('x')
print(q)

输出:
deque([‘a’, ‘b’, ‘c’])
deque([‘a’, ‘b’, ‘c’, ‘d’])
deque([‘x’, ‘a’, ‘b’, ‘c’, ‘d’])

defaultdict

  • 当直接读取dict不存在的属性时,直接返回默认值
d1 = {"one":1, "two":2, "three":3}
print(d1['one'])
print(d1['four'])

输出:
1

KeyError Traceback (most recent call last)
in ()
1 d1 = {“one”:1, “two”:2, “three”:3}
2 print(d1[‘one’])
----> 3 print(d1[‘four’])

KeyError: ‘four’

from collections import defaultdict
# lambda表达式,直接返回字符串
func = lambda: "刘大拿"
d2 = defaultdict(func)

d2["one"] = 1
d2["two"] = 2

print(d2['one'])
print(d2['four'])

输出:
1
刘大拿

Counter

  • 统计字符串个数
from collections import Counter

# 为什么下面结果不把abcdefgabced.....作为键值,而是以其中每一个字母作为键值
# 需要括号里内容为可迭代
c = Counter("abcdefgabcdeabcdabcaba")
print(c)

输出:
Counter({‘a’: 6, ‘b’: 5, ‘c’: 4, ‘d’: 3, ‘e’: 2, ‘f’: 1, ‘g’: 1})

s = ["liudana", "love", "love", "love", "love", "wangxiaona"]
c = Counter(s)

print(c)

输出:
Counter({‘love’: 4, ‘liudana’: 1, ‘wangxiaona’: 1})

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值