Python函数式编程

Stella981
• 阅读 420

函数式编程

[TOC]

函数式编程。

函数参数中的魔法

coming...

位置参数

关键字参数

默认参数

动态参数

位置参数收集

关键字参数收集

收集参数行为的逆向过程(解参,这个逆向过程用于实参上)

参数传递

嵌套函数定义

顾名思义就是一个函数定义中嵌套了一个函数的定义。说道嵌套函数,就必须先介绍命名空间和作用域。(适用于整个python的名称空间)

命名空间NameSpace

python分三类命名空间。内置,全局,本地。包括一个区域,封闭区域。

  • Builtins 内置空间,在main模块加载前创建
  • Globals 全局空间,模块为单位。包空间其实是__init__.py模块
  • Enclosing 封闭区域
  • Locals 本地空间,函数,类,对象等

作用域Scope

python对象的作用域是遵照LEGB规则。

闭包

一般普通的函数嵌套并没什么特别的用处。但是嵌套函数有个突出的应用,也是非常强大的应用。就是,通过外层函数创建一个函数,即返回内层函数,外层函数每一调用返回的内层函数,这些返回的内层函数之间是独立的函数对象。他们都“继承”了外层函数调用时的作用域。这个“继承”行为我们称为闭包。

闭包显著的特点就是:返回的内层函数中,使用了外层函数的局部变量或者参数参与内层函数的运算。这些变量和参数的值还是外部函数调用返回前时的值。
要注意的是:返回内层函数时,使用外层函数时的变量或者参数,如果在返回后发生了变化。那么内层函数后面调用会使用变化后的值,而不是返回内层函数那个时间点的值了。所以很多博客都在强调,闭包返回的内层函数不要引用还会在外层函数发生变化的变量。
要解决这个问题,可以采取中间变量(中间变量就是固定的了)或者再嵌套一个中间函数,让这个中间函数来获取变化的变量,在传递给最内层的函数,并返回最内层的函数。

记住:内层函数返回时是不会执行的。

__自己猜想__
有点类似于面向对象,一个对象方法返回另一个对象,新对象的实例化是受到返回它的对象影响,展现除了多态的特性。闭包有点多态,继承,封装(函数本来就是一种封装形式)的意思,将规律相似的作为一层。多层次之间可以看作多层次的嵌套函数来设计,能产生“继承变量”的哪一层就作为上层,有几层规律就嵌套几层函数,从外到内,每一层都是和上一层有一个或多个“继承变量”关系。算一种设计范式了吧。
如:乘法运算,将第一个乘数作为一层,第二乘数提供后算出乘积来作为第二层。那么就可以使用闭包将拆分有关系的两层进行程序设计。
再如:装饰器函数的inner()函数,就是要继承装饰函数说传递的参数。返回的inner函数调用就会间接调用被装饰的函数。inner函数实现了扩展需求的功能。
找点装饰器或者其它闭包例子来佐证猜想
再如:Django的中间件工厂函数:


# 通过闭包实现中间件工厂函数,返回中间件函数。
def simple_middleware(get_response):  # 外层函数是中间件工厂函数
    # One-time configuration and initialization.

    def middleware(request):  # 内层函数是中间件函数,get_response是从闭包区域空间extend 过来的,所以会随着get_response的变化变化。所以呼应上面说的:很多博客提醒,内层函数最好不要使用外层函数变化的变量,否则可能会得到你意想不到的结果。
        # Code to be executed for each request before
        # the view (and later middleware) are called.

        response = get_response(request)

        # Code to be executed for each request/response after
        # the view is called.

        return response

    return middleware

函数式编程

摘自廖雪峰python教程

函数式编程是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数是没有变量的,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此这种函数是有副作用的。 函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数! python 对函数式编程提供部分支持。由于Python允许使用变量,因此Python不是纯函数式编程语言。

装饰器Decorator

装饰器是函数式编程的集大成应用。涉及高阶函数,闭包,参数传递等。 如果只是要扩展功能,只需要再创建一个函数,并嵌套需要扩展的函数,就是在创建的函数中实现扩展功能后,再执行扩展前的函数就可以了。但是这样做有一个缺点就是,原函数名被改变了,这样会被调用原函数的其它部门打死的,你这样方便了自己却麻烦了其他人。 那就要想办法呀,那重命名这个扩展的函数为原函数名呢?肯定不行,这样函数调用就编程了递归函数了。 那就这样设计:用一个函数返回一个函数,返回的函数重命名为原来的函数名。调用这个重命名返回函数,就是执行扩展后再调用原函数。这就是通过闭包,实现装饰器。

  • 装饰外层函数:返回内层函数
  • 装饰内层函数:实现原功能上的“封闭-开发”原则的扩展功能。内层函数对扩展功能实现后,再调用原函数,并return原函数调用返回值。

显示方式

# 我们已一个廖老师装饰器思考题为例,说明两种方式表示装饰器的应用
# 题目:请设计一个decorator,它可作用于任何函数上,并打印该函数的执行时间
import time

def runtime(func):
    def wrapper(*args, **kwargs):
        t1 = time.time()
        res = func(*args, **kwargs)
        t2 = time.time()
        print((t2-t1))
        return res
    return wrapper

def get_sum(*args):
    sums = 0
    for i in args:
        sums += i
    return sums

get_sum = runtime(get_sum)  # 这就是显示方式
print(get_sum(*list(range(10000))))

@方式

# 装饰器函数一样
import time

def runtime(func):
    def wrapper(*args, **kwargs):
        t1 = time.time()
        res = func(*args, **kwargs)
        t2 = time.time()
        print((t2-t1))
        return res
    return wrapper

runtime
def get_sum(*args):
    sums = 0
    for i in args:
        sums += i
    return sums

print(get_sum(*list(range(10000))))

结果:

0.0010001659393310547 #运行时间 49995000 #函数返回值

多个装饰器顺序

主要根据装饰器的作用,决定顺序。 举个栗子:

  1. Flask的route装饰器和我们自定义的认证装饰器auth。

  2. route用户将url于被装饰的函数视图放入一个映射中。

  3. 我们自定义auth装饰器,则是用作在视图执行前,对用户登陆状态的认证,如果没有登陆则装饰器直接返回认证失败。

  4. 这时候就考虑route和auth的顺序了:

    #第一种顺序: 这时候python解释器显示会执行auth装饰的,auth返回的内置函数再被app.route('/index/')执行装饰。这是符合业务逻辑的 @app.route('/index/') @auth def index(): pass

    第二种顺序:如果了解app.route的代码,可以知道该装饰器的主要作用就是将'/index/' 与 index函数放入一个映射中,装饰器最终返回的的自己二就是index函数,相当于没有起到装饰添加逻辑的作用,而时加入一个映射中。最终系统会根据映射去执行对应函数,auth实际再业务逻辑中根本没有被执行。第二种按照逻辑要求就是错误的装饰顺序。

    @auth @app.route('/index/') def index(): pass

小结:多个装饰器顺序问题,首先是顺序是python解释器从下后上进行装饰器的穿衣过程(直接点就是从内到外)。而最后决定顺序,要知道装饰器具体逻辑和符合业务逻辑。

使用装饰器扩展的功能变得更具兼容性,高内聚。(带参数装饰器)

什么意思呢?翻译成‘人话’就是:一般装饰器只扩展一类相似功能的一种,现在要给这一功能提供并传递参数,是装饰器展现出不同的行为。 装饰器带上参数,只需要再装饰器包裹一个函数,这个函数接收参数,返回一个外层装饰器函数,内层函数接受到包裹的哪层函数的参数,在扩展代码中进行应用,这样使得扩展代码具有更多变数,理所当然,也会拿到外层还是传递的原函数。内层函数拿到这两个东西,就可以大施拳脚,做出各种功能来了。 直接代码说话:

# 廖老师的打印调用一个函数的日志。
def log(text):
    def outer(func):
        def inner(*args, **kwargs):
            print("%s begin call " % text)
            return func(*args, **kwargs)
        return inner
    retrun outer

log('求和')
def get_sum(*args):
    sums = 0
    for i in args:
        sums += i
    return sums

print(get_sum(*list(range(10000))))

运行结果:

求和 begin call # 扩展的功能:打印执行日志 49995000 # 函数返回结果

让装饰器更安全

因为装饰器的内层函数被重新绑定给了原函数名,内层函数的__name__还是内层函数名,而不是原函数名。有些依赖函数签名的代码可能会报错。python的functools提供了 将内层函数的__name__值变为重新绑定到的变量的名字。 用法:

import functools
def decorator(func)
    @functools.wraps(func)  # 用在这里,就是wrapper之前,无论带参数还是不带参数的装饰器
    def wrapper(*args, **kwargs):
        pass
        return func(*args, **kwargs)
    return wrapper

装饰器还可以不止一个参数,多个参数,多层包裹,产生多个维度。

匿名函数

匿名函数就是不需要显示的通过def定义函数。匿名函数通常用于高阶函数。匿名函数直接用于高阶函数的参数或者return中,如果在return中,就形成了闭包。 关键字lambda 代表匿名函数 匿名函数表示方法: lambda x, y : x + y 冒号前x, y是参数列表,冒号后面表达式的值作为函数的return值,且冒号后面只能是一个表达式,不能包含其它语句。如赋值语句,复合语句。可以使用三元表达式。 匿名函数可以缩短代码行数。 匿名函数例子:

L = list(map(lambda *args: lambda *innerargs: print(innerargs+args),[1,2,3,4,5,6,7]))
for f in L:
    f(10)

结果: (10, 1) (10, 2) (10, 3) (10, 4) (10, 5) (10, 6) (10, 7)

高阶函数

什么是高阶函数? 参数或者返回中使用函数 coming...

三剑客

filter()

map()

reduce()

递归函数

coming....

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
2年前
JS 对象数组Array 根据对象object key的值排序sort,很风骚哦
有个js对象数组varary\{id:1,name:"b"},{id:2,name:"b"}\需求是根据name或者id的值来排序,这里有个风骚的函数函数定义:function keysrt(key,desc) {  return function(a,b){    return desc ? ~~(ak
Stella981 Stella981
2年前
HIVE 时间操作函数
日期函数UNIX时间戳转日期函数: from\_unixtime语法:   from\_unixtime(bigint unixtime\, string format\)返回值: string说明: 转化UNIX时间戳(从19700101 00:00:00 UTC到指定时间的秒数)到当前时区的时间格式举例:hive   selec
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
4个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这