Python高阶函数与函数装饰器

作者: 网络编程  发布:2019-09-03

2015-11-22 第五天,2015-11-22

Python高阶函数与函数装饰器-day4,python-day4

  • 上节回顾
  • 高阶函数
  • 闭包函数
  • 函数装饰器
  • 模块导入

 

装饰器

一、上节回顾

  Python2与Python3字符编码问题,不管你是初学者还是已经对Python的项目了如指掌了,都会犯一些编码上面的错误。我在这里简单归纳Python3和Python2各自的区别。

  首先是Python3-->代码文件都是用utf-8来解释的。将代码和文件读到内存中就变成了Unicode,这也就是为什么Python只有encode没有decode了,因为内存中都将字符编码变成了Unicode,而Unicode是万国码,可以“翻译”所以格式编码的格式。Python3中str和bytes是两种格式,bytes可以当做二进制的表现形式。
  Python2使用系统默认的字符编码解释代码,所以要用utf-8解释代码,就必须在头部申明;并且Python2中有解码和编码,但是解码动作是必须的而编码动作可以忽略,因为Python代码加载到内存中就是Unicode,这一点和python3一样;Python2中还需要注意的就是str和bytes是一个意思。Python2 里面的str就是Python3中的bytes格式,而Python3中的str其实就是Unicode.

    

  函数基础(这里我就是用递归函数中的二分查找)

  为什么使用函数:将将程序进行模块设计

  定义函数有三种形式:

    - 无参函数

    - 有参函数

    - 空函数

  PS:如果函数有多个返回值,那么返回的来的数据格式是元组
  - 如何在函数传入参数时限定参数数据格式。

  def leon(x:int,y:int)->int:

    pass

  其中这里指定了x,y都必须是int类型 " -> "的意思是函数返回值也必须是int类型

     print(yan.__annotations__):显示形参的限定数据格式以及返回值的格式

a = [1,2,3,4,5,7,9,10,11,12,14,15,16,17,19,21]  #形参中的num
def calc(num,find_num):
    print(num)
    mid = int(len(num) / 2)                       #中间数的下标
    if mid == 0:        #递归函数非常重要的判断条件
        if num[mid] == find_num:
            print("find it %s"%find_num)
        else:
            print("cannt find num")
    if num[mid] == find_num:     #直接找到不用递归,结束函数
        print("find_num %s"%find_num)
    elif num[mid] > find_num:     #find_num应该在左边,向下递归
        calc(num[0:mid],find_num)

    elif num[mid] <  find_num:     #find_num应该在右边,向下递归
        calc(num[mid+1:],find_num)
calc(a,12)

      匿名函数

c = lambda x:x+1   #x就是形参,c就是这个匿名函数的对象
print(c(22))   

  高阶函数-特性

  1. 把一个函数的内存地址传给另外一个函数,当做参数

  2.一个函数把另外一个函数的当做返回值返回

def calc(a,b,c):
    print(c(a) + c(b))

calc(-5,10,abs) #引用上一节的实例,将-5和10绝对值相加

 

 

 

必备知识

1.装饰器由至少两层函数定义出来

2.动态函数

3.函数返回值

4.三层及以上函数定义的装饰器

5.多个装饰器

 

二、高阶函数(补充) 

  函数是第一类对象

  • 函数可以被赋值
  • 可以被当做参数
  • 可以当做返回值
  • 可以作为容器类型的元素
#函数可以被赋值
def leon():
    print("in the leon")

l = leon
l()

#函数可以被当做参数
def yan(x):  #这里x形参,其实就是我们调用实参的函数名
    x()        #运行函数

y = yan(leon)


#函数当做返回值
def jian(x):   和上面一样这这也必须传入一个函数
    return x
j = jian(leon) #这里需要注意一点就是这里的意思是运行jian这个函数而这个函数返回的是x 也就是leon这个函数的内存地址,也就是说这时候leon这个函数并没有被执行
j()  #运行 leon函数

#可以做为容器类型的元素
leon_dict = {"leon":leon}

leon_dict["leon"]() #这样也可以运行leon这个函数

最简单的装饰器的实现

#!/usr/bin/env python
# -- coding: utf-8 --
__author__ = 'EchoRep'


#定义一个函数,作为装饰器函数,目的是把原函数加一些功能返回
def auth(args):
    # 定义一个函数内部的函数,目的是给原函数加一些功能
    def inner():
        #这个是给原函数加入的新功能
        print "this is before!!!n**********************"
        #这个是调用原函数
        args()
    #这个是返回内部函数,确保下面运行可以获取原函数的功能和新加入的功能
    return inner

#给原函数加上装饰器
@auth
#定义原函数有一些功能
def f1():
    print "this is function one."

#定义一个主函数
def main():

    #调用已经修改过的函数
    f1()


#限制程序入口
if __name__ == "__main__":
    main()

三、闭包函数

1.什么是闭包?我来看一下,比较官网的概念(这不是我在官网上面找的,不过没有关系,反正你们也看不懂):

  闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,闭包是由函数和与其相关的引用环境组合而成的实体。

  懵逼了?不存在的。下面我用简洁的说一下,但是有一点很重要,闭包是装饰器中的重点,如果没有把闭包正真理解,那么学完装饰器之后会很快忘记。我们通过一个列子来说明下

import requests   #首先导入一个模块,这个可以不用记

def get(url):    #定义一个get函数里面需要传一个url的位置参数
    def wapper():  #在定义一个wapper函数
        res = requests.get(url) #这一步就是打开一个网页
        return res.text   #将网页以文字的形式返回
    return wapper   #返回最里层的wapper函数

g = get("http://www.baidu.com")  #调用:首先因为作用域的原因,我们无法访问到里层的wapper函数,所以我们直接调用get函数这里返回了一个wapper函数
print(g())  # 然后我在调用g(get函数)的对象,这样是不是就访问到里层的wapper函数呢

图片 1

PS:这里我们可以把函数当做一个特殊的变量,当代码从上向下执行的时候,如果函数不被调用话,函数内的代码是不会被执行的。就拿上面的上面的举例,当我们执行get函数的时候,这时候会返回一个wapper函数的内存地址,但是这个时候wapper函数并没有被执行也就是说g()这时候返回的状态其实就是wapper,这是我们只需要将g运行,就等于运行了wapper内的代码。

 

带有参数的装饰器

#!/usr/bin/env python
# -- coding: utf-8 --
__author__ = 'EchoRep'


#定义一个新的能带参数的装饰器
def authArgs(args):
    #定义一个内部函数给原来的函数加入新的功能
    def inner(argsin):
        #在原函数执行之前加入的功能
        print "before"
        #执行原函数
        args(argsin)
        #在原函数执行之后加入的新功能
        print "after"
    #返回加入了新功能的内部函数
    return inner

#定义一个函数,作为装饰器函数,目的是把原函数加一些功能返回
def auth(args):
    # 定义一个函数内部的函数,目的是给原函数加一些功能
    def inner():
        #这个是给原函数加入的新功能
        print "this is before!!!n*{22}"
        #这个是调用原函数
        args()
        #这个是在函数执行之后再加入的功能
        print "~~~~~~~~~~~~~~~~~~~~~nthis is after!!!"
    #这个是返回内部函数,确保下面运行可以获取原函数的功能和新加入的功能
    return inner

#给原函数加上装饰器
@auth
#定义原函数有一些功能
def f1():
    print "this is function one."

#给原函数加入带有参数的装饰器
@authArgs
#定义一个带有参数的原函数
def f2(args):
    #这是原函数的功能
    print "this is function %s" %args

#定义一个主函数
def main():
    #调用已经修改过的函数
    f1()
    #加入参数,执行修改以后的函数
    f2("hi,nice to meet you!!!!")


#限制程序入口
if __name__ == "__main__":
    main()

上面只有一个参数,如果我们想要实现带两个参数,三个参数。。。。乃至于n个参数有两种做法:

第一种做法是依次定义n个带参数的装饰器来实现
第二种做法是定义一个能够兼容无参数,一个,两个,三个。。。n个参数的装饰器函数
*第二种做法的实现需要通过动态参数来配合
def func(*arg,**kwargs):
        func2(*arg,**kwargs)

#!/usr/bin/env python
# -- coding: utf-8 --
__author__ = 'EchoRep'



#定义一个函数,作为装饰器函数,目的是把原函数加一些功能返回
def auth(args):
    # 定义一个函数内部的函数,目的是给原函数加一些功能,通过加入动态函数的方式解决了参数不固定的问题
    def inner(*arg,**kwargs):
        #这个是给原函数加入的新功能
        print "this is before!!!n*{22}"
        #这个是调用原函数,配合函数使用动态函数的方式,解决了参数不固定的问题
        args(*arg,**kwargs)
        #这个是在函数执行之后再加入的功能
        print "~~~~~~~~~~~~~~~~~~~~~nthis is after!!!"
    #这个是返回内部函数,确保下面运行可以获取原函数的功能和新加入的功能
    return inner

#给原函数加上装饰器
@auth
#定义原函数有一些功能
def f1():
    print "this is function one."

#给原函数加入带有参数的装饰器
@auth
#定义一个带有参数的原函数
def f2(args):
    #这是原函数的功能
    print "this is function %s" %args

#定义一个主函数
def main():
    #调用已经修改过的函数
    f1()
    #加入参数,执行修改以后的函数
    f2("hi,nice to meet you!!!!")


#限制程序入口
if __name__ == "__main__":
    main()

装饰器函数的返回值不仅在原函数里面有体现,在装饰器函数里面也需要再次返回,这样最后才可以获得我们需要的返回值。

多个装饰器可以参考“盒子模型”

三层以上的函数可以构成带多个参数的装饰器

函数内部获取不到同等级函数的名字作为赋值


四、函数的嵌套调用

  嵌套调用其实很好理解,就是在一个函数中调用另一个函数的结果,也就是return的东西,同样的我们看一段非常简单的代码来看一下。

#嵌套调用,在一个函数中调用另一个函数的功能
#calc这个函数就是在对比两个数字的大小
def calc2(x,y):
    if x >y :
        return x
    else:
        return y

#我靠老板非常变态,然你直接计算四个数字的大小,擦。
def calc4(a,b,c,d):
    res1 = calc2(a,b)   #res1的值,这里不就是calc2这个函数比较时最大的哪一个吗。
    res2 = calc2(res1,c)
    res3 = calc2(res2,d)
    return res3

  通过上面的代码我们做一记忆。什么时候会用到嵌套调用呢?很显然,就是我们这个函数(calc4)需要另外一个函数的实行结果(return的y或者x)。

 

递归

递归的使用:

1.计算器

2.前值相加数列

递归里面的返回值问题:

#func 没有返回值,或者说没有获取到返回值,原来是因为递归调用的时候,最后依次虽然返回了,
# 但是倒数第二次没有返回,所以一路返回的是none,也就是函数默认的返回值

五、装饰器(高级的闭包函数)

  就拿下面的这段代码来说。如何在不改源代码的情况下实现计算代码的运行时间

def geturl(url):
    response = requests.get(url)
    print(response.status_code)

geturl("http://www.baidu.com")

def timer(func):
    def wapper(url):
        start_time = time.time()
        func(url)
        stop_time = time.time()
        so_time_is = stop_time - start_time
        print("运行时间%s"%so_time_is)
    return wapper


@timer
def geturl(url):
    response = requests.get(url)
    print(response.status_code)

python = geturl("http://www.baidu.com")

   图解代码

图片 2

装饰器必备:

模块

六、有参装饰器

  听着名字顾名思义,就是在装饰器中还有位置参数。

#一个low得不能再low得验证脚本,如果是显示环境中所有数据必须是由数据库或者一个静态文件提供,并且登录成功时,需要保存用户的一个状态

def auth(auth_type):   #有参装饰器名称
    def auth_deco(func):   #定义第二层函数名称
        def wrapper(*args,**kwargs):  #最里层函数,主要实现认证功能
            if auth_type == "file":
                username = input("username>>:").strip()
                password = input("username>>").strip()
                if username == "leon" and password == "loveleon":
                    res = func(*args,**kwargs)
                    return res
            elif auth_type == "mysql_auth":
                print("mysql_auth...")
                return func(*args,**kwargs)
        return wrapper  #第二层返回的是wrapper函数,其实就是home
    return auth_deco    #第一层返回的结果等于第二层函数的名称

@auth('file')
def home():
    print("welcome")

home() #执行home-->wrapper

有参函数必备知识:

三种模块:

自定义模块

内置模块

第三方模块

 七、模块导入

  import ,创建一个leonyan.py的模块文件,等待被导入

a = 10
b = 20
c = 30

def read1():
    print("in the read1")

def read2():
    print("in the read2")

  导入leonyan.py文件(调用模块文件和模块文件在同一目录下)

import leonyan #Python IDE这行会爆红,但是不用管

leonyan.read1() #执行leonyan这个包中的read1函数

leonyan.read2() #执行leonyan这个包中read2函数

print(leonyan.a + leonyan.b + leonyan.c ) #输出60

 总结:在Python中包的导入(import ***)会干三个事情:1:创建新的作用域;2:执行该作用域的顶级代码,比如你导入的那个包中有print执行后就会直接在屏幕中输出print的内容;3:得到一个模块名,绑定到该模块内的代码

  

  在模块导入的时候给模块起别名

import leonyan as ly
import pandas as pd #这是一个第三方模块,以后的博客中会写到,这是一个用于做统计的

 给模块起别名还是挺多的,在有些模块的官方文档中,还是比较推荐这种方法的,比如pandas的官方文档中就是起了一个pd别名,总之as就是一个模块起别名

 

  from *** import ***

from leonyan import read1 #引入直接调用
read1() 

如果在调用模块的函数作用域中有相同的同名的,会将调用过来的覆盖。

 

在form ** import ** 中控制需要引用的变量(函数其实在未被执行的时候也是一个存放在内存中的变量)

from leonyan import read1,read2 在同一行中可以引用多个,只需要用逗号隔开就行了

print(read1)
print(read2)
#这里打印的就是read1和read2的内存地址

#需求我现在只需要导入read2

这时候我们就可以在leonyan这个函数中加上这么一行:

__all__ = ["read2"]  #这里的意思就是别的文件调用为的时候用from ** import ** 只能拿到read2 这个函数的内存地址,也就是只有read2可以被调用

 

  把模块当做一个脚本执行

   我们可以通过模块的全局变量__name__来查看模块名:
  当做脚本运行:
  __name__ 等于'__main__'

  作用:用来控制.py文件在不同的应用场景下执行不同的逻辑
  if __name__ == '__main__':

#fib.py

def fib(n):    # write Fibonacci series up to n
    a, b = 0, 1
    while b < n:
        print(b, end=' ')
        a, b = b, a+b
    print()

def fib2(n):   # return Fibonacci series up to n
    result = []
    a, b = 0, 1
    while b < n:
        result.append(b)
        a, b = b, a+b
    return result

if __name__ == "__main__":
    import sys
    fib(int(sys.argv[1]))

代码执行 Python flb.py 100

 

  只需要简单了解的Python模块导入搜索路径

  内建(build-in)  --> sys.path(sys.path是一个列表,而且第一个位置就是当前文件夹)

 

  模块导入的重点-->包的导入

  在实际的开发环境中,你不可能一个文件的代码写到底,当然你也有可能会引用同文件夹中的其他模块,但是你有没有想过这一个项目不可能是你一个写的,都是很多人协作开发。这样就存在这样的一个问题了;不同的人不可能用一台电脑,也不可能在一个文件夹下面写写功能。他们也有自己的代码文件夹,然后大家把功能通过接口的方式,提供调用。这时候就面临这不同文件夹的调用问题。而这种问题也需要通过from ** import ** 调用。

  图片 3

 

上图中我运行“模块导入.py”这个文件夹,首先我from Pythonscript.leonyan.command import config,因为我们得运行脚本和需要导入的包都在Pythonscript的目录下面所以我直接通过绝对路径导入,然后一层一层“.”下去,知道最后import了这个config文件,这里我们需要注意一点:当脚本在最外层运行的时候sys.path 列表中的第一个参数就是运行脚本的目录,这是什么意思,这代表着你在别的包中有调用了其他的东西比如说我的config.py是调用了bing.py文件,这时候就必须写绝对路径,因为这在sys.path文件夹中已经找不到了,也就是导入不进来。

总结:包的导入其实都是很简单的,你需要记住一点:当你导入Python内建或者下载的第三方模块直接用import 导入,如果是自己写的就用from ** import ** 使用绝对目录导入就行了,也就是从调用脚本的上级目录开始导入。这样可以保证不会报模块导入的错误了。

上节回顾 高阶函数 闭包函数 函数装饰器 模块导入 一、上节回顾 Python2与Python3字符编码问题,不...

两种导入模块的方法:

import os
os.argv
for os import argv
argv

导入模块需要注意一个问题,模块的路径问题“sys.path”

第五天,2015-11-22 装饰器 必备知识 1.装饰器由至少两层函数定义出来 2.动态函数 3.函数返回值 4.三层及以上函数定义的装饰器 5.多...

本文由王中王开奖结果发布于网络编程,转载请注明出处:Python高阶函数与函数装饰器

关键词:

上一篇:image和js静态文件生产意况布署
下一篇:没有了