详谈Python高阶函数与函数装饰器(推荐)


Posted in Python onSeptember 30, 2017

一、上节回顾

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绝对值相加

 

二、高阶函数(补充)

函数是第一类对象

函数可以被赋值

可以被当做参数

可以当做返回值

可以作为容器类型的元素

#函数可以被赋值
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这个函数

三、闭包函数

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函数呢

详谈Python高阶函数与函数装饰器(推荐)

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

四、函数的嵌套调用

嵌套调用其实很好理解,就是在一个函数中调用另一个函数的结果,也就是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)。

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

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

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)

图解代码

详谈Python高阶函数与函数装饰器(推荐)

装饰器必备:

@timer就是装饰器,意思是装饰它下面的函数,而装饰器和被装饰的都是一个函数。

timer(装饰器函数),首先它会有一个位置参数(func)名字随意,但是必须并且只能是一个位置参数

func参数就是被装饰的geturl这个函数

为什么func是geturl这个函数呢-->上面写了一个装饰器功能:geturl=timer(geturl),我们看到这里的timer中传入的其实就是func函数所以func = geturl(被装饰的函数)

分析geturl=timer(geturl),首先我们可以得知timer这是一个闭包函数,当我们执行这个闭包函数,会把里层的函数(wapper)返回,也就是说timer(geturl)其实就是返回的wapper,所以就可以这样理解了geturl==wapper,所以当我们运行geturl的时候就相当于在执行wapper()这样的一个操作;如果这里实在记不住,就这样。咱上面不是有一个闭包函数吗?你就把geturl=timer(geturl)中的geturl(执行函数的返回结果)当做上面g(函数调用的返回结果),然后在分别再执行了下"g"或者"geturl”这个对象。

如果被装饰者有位置参数的话,我们需要在wapper函数中加上对应的位置参数用来接收,如果长度是不固定的话还可以用*args和**kwargs

六、有参装饰器

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

#一个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

有参函数必备知识:

套路,通过上面无参装饰器,我们得出了geturl=timer(geturl)这个等式。回到有参装饰器,我们又会有什么样子的等式呢?首先@auth("file")是一个装饰器也就是一个函数,所以我们定义了一个auth(auth_type)这个函数,而这个函数返回的是什么呢?没有错就是第二层函数;到了这里我们就会发现@auth("file")其实就是@auth_deco,现在我们知道了现在装饰器其实就是auth_deco,那剩下的还不知道怎么写吗?

整理公式,auth('file')-----------(return)> auth_deco----->@auth_deco ->home=auth_deco(home)

如果记不住?如果实在是记不住,其实就可以这样理解,有参装饰器无非就是在无参装饰器上面加了一层(三层),然后在第一层返回了第二层的函数,而到了第二层就和我们普通用的装饰器是一毛一样了

七、模块导入

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 ** 调用。

详谈Python高阶函数与函数装饰器(推荐)

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

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

以上这篇详谈Python高阶函数与函数装饰器(推荐)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python判断一个集合是否包含了另外一个集合中所有项的方法
Jun 30 Python
python使用clear方法清除字典内全部数据实例
Jul 11 Python
Python中将变量按行写入txt文本中的方法
Apr 03 Python
Python WSGI的深入理解
Aug 01 Python
python3使用matplotlib绘制散点图
Mar 19 Python
python实现支付宝转账接口
May 07 Python
python3下pygame如何实现显示中文
Jan 11 Python
python计算二维矩形IOU实例
Jan 18 Python
python GUI库图形界面开发之PyQt5拖放控件实例详解
Feb 25 Python
django admin后管定制-显示字段的实例
Mar 11 Python
新建文件时Pycharm中自动设置头部模板信息的方法
Apr 17 Python
Python函数参数定义及传递方式解析
Jun 10 Python
深入浅出学习python装饰器
Sep 29 #Python
Python连接phoenix的方法示例
Sep 29 #Python
Python矩阵常见运算操作实例总结
Sep 29 #Python
利用python求解物理学中的双弹簧质能系统详解
Sep 29 #Python
python中装饰器级连的使用方法示例
Sep 29 #Python
python中map()函数的使用方法示例
Sep 29 #Python
python中reduce()函数的使用方法示例
Sep 29 #Python
You might like
写一个用户在线显示的程序
2006/10/09 PHP
PHP内存缓存Memcached类实例
2014/12/08 PHP
php实现比较全的数据库操作类
2015/06/18 PHP
JavaScript自动设置IFrame高度的小例子
2013/06/08 Javascript
jquery自动切换tabs选项卡的具体实现
2013/12/24 Javascript
JQuery表格拖动调整列宽效果(自己动手写的)
2014/09/01 Javascript
基于Bootstrap+jQuery.validate实现Form表单验证
2014/12/16 Javascript
Javascript aop(面向切面编程)之around(环绕)分析
2015/05/01 Javascript
Node.js中使用socket创建私聊和公聊聊天室
2015/11/19 Javascript
动态加载js、css的实例代码
2016/05/26 Javascript
微信小程序 教程之WXML
2016/10/18 Javascript
微信小程序 devtool隐藏的秘密
2017/01/21 Javascript
Angular 2父子组件数据传递之局部变量获取子组件其他成员
2017/07/04 Javascript
ActiveX控件的使用-js实现打印超市小票功能代码详解
2017/11/22 Javascript
利用hasOwnProperty给数组去重的面试题分享
2018/11/05 Javascript
mui js控制开关状态、修改switch开关的值方法
2019/09/03 Javascript
jQuery实现简单飞机大战
2020/07/05 jQuery
vue移动端写的拖拽功能示例代码
2020/09/09 Javascript
python中精确输出JSON浮点数的方法
2014/04/18 Python
详解Python当中的字符串和编码
2015/04/25 Python
浅谈python中的实例方法、类方法和静态方法
2017/02/17 Python
机器学习之KNN算法原理及Python实现方法详解
2018/07/09 Python
python批量从es取数据的方法(文档数超过10000)
2018/12/27 Python
Numpy将二维数组添加到空数组的实现
2019/12/05 Python
Python 如何展开嵌套的序列
2020/08/01 Python
Python使用Turtle模块绘制国旗的方法示例
2021/02/28 Python
纽约家具、家居装饰和地毯店:ABC Carpet & Home
2017/06/21 全球购物
定制iPhone和Macbook保护壳:Slick Case
2018/11/21 全球购物
实习求职信
2013/12/01 职场文书
安全标准化实施方案
2014/02/20 职场文书
2014优秀党员事迹材料
2014/08/14 职场文书
2015小学教师德育工作总结
2015/05/12 职场文书
党员转正意见怎么写
2015/06/03 职场文书
教师节主题班会教案
2015/08/17 职场文书
导游词之南京莫愁湖公园
2019/11/13 职场文书
Python编写车票订购系统 Python实现快递收费系统
2022/08/14 Python