python基础三

  • 2019 年 10 月 6 日
  • 笔记

一、揭开函数名的真实面目

我们创建一个函数给的命名实际是代表一个内存地址,加上括号才会运行函数中的函数体,如果不加括号,可以自己试一下,输出的是一个内存地址,这样看来,他实际上他和一个变量差不多,只不过函数代表了一个功能,因此函数也可以作为参数传入函数,也可以作为返回值返回。

二、函数嵌套(函数中的函数)

def zhangsan():

def lisi():

print("我是lisi")

print("我是zhangsan")

lisi()

zhangsan()

当我们在zhangsan函数中没有调用lisi时,我是lisi是不会输出的。只有在调用的时候才会输出。以上只是2级嵌套,你可以像列表一样,多次嵌套。

注意:在嵌套函数中我们内部需要修改外部的变量时不能使用关键字global,

global影响的是函数外的全局变量。需要使用nonlocal(python3中),例如:

age =111

def zhangsan():

age = 18

def lisi():

nonlocal age

age +=1

print("lisi的年龄为",age)

print("zhangsan的年龄为",age)

lisi()

zhangsan()

print("age:",age)

我们运行一下会发现,age并没有影响到函数(zhangsan外)的age。如果换成global,就会影响到全局变量。

三、闭包

例如一个简单的闭包:

def zhangsan():

age = 18

def lisi():

print(age)

return lisi

闭包的概念:首先它是一个嵌套函数,其次在内部函数中引用了外部函数的变量,我们称这是一个闭包。

其次我们为什么要以返回值的形式返回lisi,我们可以想一下,如果我们只想用到lisi这个函数,如果我们用二中的方法,每次调用zhangsan,那么每次都要走一遍zhangsan,这显然不是我们想要的,因此,我们如果只想调用lisi可以这样写:wangwu = zhangsan()#此时wangwu代表的就是lisi的内存地址,但并没有去执行它。wangwu()#这样就实现调用lisi这个内部函数,并且可以引用到zhangsan中age这个变量。

四、装饰器

4.1普通装饰器

首先,思考一个问题,我们进入一个网页,再点击个人信息,会提醒需要登录才能查看,那么需要我们登录的页面很多,每次都调用很麻烦,所有就有了装饰器。

它需要遵循一个原则:开放封闭原则

开放:就是说要有利于扩展新的功能,比如更新的时候。

封闭:在扩展新功能的时候,不能修改原来的代码。

例如:

def login():

print("登录")

def func():

print("点击个人信息")

A写好登录,B写好个人信息,C想要调用个人信息他不会去想着调用登录,B需要将这两个功能结合起来,但是不能修改原来的功能,于是就有了:

def login(func):

print("登录")

def log():

func()

return log


def func():

print("点击个人信息")

func = login(func)

f()

用闭包的知识可以解决这个问题,func这个方法就叫做装饰器。但是一想要加上func = login(func)这样我们就调用的不是原来的函数名了,于是乎就有了:

def login(func):

print("登录")

def log():

func()

return log

@login

def func():

print("点击个人信息")

func()

利用在该函数上方添加一个@加函数名代替func = login(func)。我们称它为语法糖,嗯,真甜。

现在调用func就相当于调用login了,但是当func函数有返回值的时候,我们按照上述写法就获取不到了,因此修改成:

def login(func):

print("登录")

def log():

ret = func()

return ret

return log

@login

def func():

print("点击个人信息")

return "ok"

print(func())

总结:当我们执行到func()的时候,会跳到@login,@login相当于执行func = login(func),此时就执行login函数了。执行后的结果返回给func,此时func得到的值就是ok的内存地址,再回到func(),便会输出ok。

问题来了:

当我们新添加的功能,也就是func函数需要一个参数时候在装饰器函数中如何添加?下一个功能也可能需要这个装饰器需要二个参数该怎么办?也是就我们的参数是动态的,那就添加动态参数掰,将login函数修改成:

def login(func):

print("登录")

def log(*args,**kwargs):

ret = func(*args,**kwargs)

return ret

return log

此时无论添加几个都不会受到影响。

4.2带参数的装饰器

首先如果我们添加一个参数@login(True),当有参数时,我们将@和login(True)拆开看,login(True)得到返回值log函数的内存地址也就是@log,显然我们的func放在哪里呢?显然会报错,于是我们可以在外再嵌套一层变成:

def log_out(F):

def login(func):

print("登录")

def log(*args,**kwargs):

if F==True:

ret = func(*args,**kwargs)

return ret

else:

return "NO"

return log

return login

@log_out(True)

def func():

print("点击个人信息")

return "ok"

print(func())

这样再按照之前的理解方式就明白了,log_out(True)表示login也就是@login,

我们只是在原来的基础上添加了一个开关,也就是说当装饰器带参数时,就需要三层嵌套了。

4.3 一个函数使用多个装饰器

def zhangsan1(fun1):

def lisi1(*args,**kwargs):

print("执行被装饰函数前111111")

fun1(*args,**kwargs)

print("执行被装饰函数后111111")

return lisi1

def zhangsan2(fun2):

def lisi2(*args,**kwargs):

print("执行被装饰函数前222222")

fun2(*args,**kwargs)

print("执行被装饰函数后222222")

return lisi2

@zhangsan1

@zhangsan2 #zhangsan3 = zhangsan2(zhangsan3 )

def zhangsan3():

print("我是被装饰的函数333333")

zhangsan3()

执行的结果:

执行被装饰函数前111111

执行被装饰函数前222222

我是被装饰的函数333333

执行被装饰函数后222222

执行被装饰函数后111111

执行顺序:首先当调用zhangsan3的时候,先执行@zhangsan2,@zhangsan2等同于zhangsan3 = zhangsan2(zhangsan3)也就是返回了lisi2,然后执行@zhangsan1,@zhangsan1等同于zhangsan3 = zhangsan1(lisi2)注意,带入的函数变成了lisi2,而不是zhangsan3,也就返回lisi1,接下来再加上一个括号执行,也就是执行lisi1中的第一句。#执行被装饰函数前111111,接着fun1()也就是lisi2(),就会输出#执行被装饰函数前222222,回到zhangsan2后,带入的参数又变成了zhangsan3,所以输出了#我是被装饰的函数333333,接着输出执行被装饰函数后222222,然后再回到最初调用的地方输出#执行被装饰函数后111111。。。。

可能也不知道我再说撒,。。。。。。。你也可以暂时记住这个顺序,

你也可以使用pycharm打上断点,看一遍执行顺序就会明白。

写的快吐血,溜了。