Python基础--函数进阶与装饰器
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python基础--函数进阶与装饰器相关的知识,希望对你有一定的参考价值。
函数作用域
python函数运行的时候,会创建自己的scope,即作用域(或者说函数有自己的namespace,即命名空间)。执行函数时,如果在函数体中遇到了变量名,python首先会在该函数的namespace内寻找该变量。如果找不到就会跳出函数在全局找(如果是嵌套函数就会找上一级函数,依次往上找,然后是在内建中找,在找不到就会报错)。
首先定义一个简单的函数
def test():
a = 1
print(1)
test()
如果在外面打印a会怎样那?
效果
说明a的作用域存在于函数内
如果在外面定义一个a 函数内部是可以使用的
在函数内部可以修改a的值,但是无法改变外面的值
其实函数没有改变外面的值,只是在函数内部重新创建了一个和外部变量一样的值
把地址打印出来可以证明
a = 1
def test():
a = 2
print(a)
print(id(a))
test()
print(a)
print(id(a))
地址是不同的
如果在函数内使用的外部变量,那么就是直接使用的外部变量(不会创建新的变量)
全局变量是在整个py文件中声明,全局范围内都可以访问
局部变量是在某个函数中声明的,只能在该函数中调用它,如果试图在超出范围的地方调用,就会报错。
函数可以调用全局变量,(就是直接使用的全局变量)
如果局部变量和全局变量一样,那么可以在函数内部对全局变量修改,(仅限于函数内部,对外部不起作用),这种情况其实是在内部重新创建的临时变量只不过和全局变量一样,本质上没有对全局变量产生任何影响。
函数基础可以参考
http://blog.51cto.com/linuxubuntu/2085265
函数的作用域也叫局部作用域,它的范围是整个函数,出了函数不起作用
作用域链
name = "Jack"
def f1():
name = "Mike"
def f2():
name = "Tom"
print(name)
f2()
print(name)
f1()
print(name)
#执行过程为:调用f1()函数,定义name 和 f2()函数,
#执行f2()函数,打印f2()中的name
#然后打印f1()中的name,最后打印全局变量的name
Python中有作用域链,
变量会由内到外找,先去自己作用域去找,
自己没有再去上级去找,直到找不到报错
例如:
把内部变量去掉后
name = "Jack"
def f1():
#name = "Mike"
def f2():
#name = "Tom"
print(name)
f2()
print(name)
f1()
print(name)
打印地址
可以看出全部是全局变量的地址
类似
def f1():
name = "Jack"
def f2():
print(name)
f2()
f1()
叫做嵌套函数,就是一个函数里面有一个或多个函数
函数可以使用外部变量但是无法修改外部变量,外部变量也无法修改函数内部的变量。
如果想在外部对函数内部的数据修改要怎么做,可以在内部返回函数地址,进行修改
函数内部的函数在修改完数据后直接返回地址,这样内存中的数据不会被回收,达到在外部调用修改的目的
正常是这样
def f1():
b = 1
def f2(b):
b += 1
print(b)
f2(b)
f1()
f1()
f1()
f1()
f1()
对代码修改后:
def f1():
def f2(b):
b += 1
return b
return f2
print(f1())
print(f1()(1))
可以看到调用f1()得到的是一个函数地址
可以把f1()赋值给一个变量,然后执行
这就是简单的闭包
闭包就是你调用了一个函数A,这个函数A返回了一个函数B给你。这个返回的函数B就叫做闭包。你在调用函数A的时候传递的参数就是自由变量。
这里面调用f1的时候就产生了一个闭包——f2,并且该闭包持有自由变量——b,因此这也意味着,当函数f1的生命周期结束之后,b这个变量依然存在,因为它被闭包引用了,所以不会被回收。
装饰器
装饰器本质上是一个Python函数,就是在不改变原来函数的情况下新增功能。
例如:
冬天为了御寒穿上棉衣(基本需求),为了加强保暖效果可以带上帽子防风眼镜手套等配置(新增需求)
#给下面函数加一项输入的功能
def f1():
print(‘f1‘)
def f2():
print(‘f2‘)
def f3():
print(‘f3‘)
第一种方式:
每个函数里加入同样的代码,效率太差
第二种方式:
将新功能封装成函数在每个函数下调用,效率提高了,但是违反开放封闭原则
开放封闭原则,其核心的思想是:
软件实体应该是可扩展,而不可修改的。也就是说,对扩展是开放的,而对修改是封闭的。
因此,开放封闭原则主要体现在两个方面:
对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。
对修改封闭,意味着类一旦设计完成,就可以独立完成其工作,而不要对类进行任何修改。
简单的说就是
封闭:已实现的功能代码块不做修改
开放:对现有代码进行扩展
在python 中一切皆对象,所以在Python中,函数本身也是对象,在全局域,函数对象被函数名引用着,因此,完全可以用其他变量名引用这个函数对象,相当于改个名字。
比如:
def test():
print("test")
调用时可以直接调用
test()
也可fu=test
fun()
这样调用
def test():
print("test")
fu = test
fu()
既然函数名可以赋值给变量,那么可不可以把赋值过得变量当做参数传递给函数那,答案是可以的
把代码改一下
def msg(fun):
fun()
inputword = input("请输入信息:")
print("输出信息为:",inputword)
def f1():
print(‘f1‘)
msg(f1)
调用后正常输出f1函数的功能
这样功能可以加上了,不过改变了调用方式
上面调用的是f1()这里改为msg(f1),
既然把函数赋值给变量可以加个括号可以运行,那么能不能把msg的地址重新赋值给变量那?通过上面的闭包我们可以知道是可以的
把代码改一下
def msg(fun):
def func():
fun()
inputword = input("请输入信息:")
print("输出信息为:",inputword)
return func
def f1():
print(‘f1‘)
f1 = msg(f1)
f1()#这样调用后就可以正常执行,其实这里执行的是func函数
这样就实现的功能并且不用改变调用方式
这样就实现了装饰器,不过在python中用语法糖@来实现实现方式为:
def msg(fun):#在调用f1时执行过程为 msg(fun)相当于msg(f1) fun相当于f1
def func():
fun()
inputword = input("请输入信息:")
print("输出信息为:",inputword)
return funcbr/>@msg
def f1():
print(‘f1‘)
#f1 = msg(f1)
f1()#这样调用后就可以正常执行,其实这里执行的是func函数
打印出地址
可以看出func的地址和f1的地址一样
如果被装饰的函数有参数怎么办那?那就在里面的函数加上参数
def msg(fun):
def func(arg):
fun(arg)
inputword = input("请输入信息:")
print("输出信息为:",inputword)
return funcbr/>@msg
def f1(arg):
print(arg)
f1(‘hello world‘)
如果是多个参数就要用到*args and **kwargs
def dec(fun):
def warpper(*args, *kwargs):
fun(args, **kwargs)
print("通用装饰器案例")
return warpperbr/>@dec
def f1(arg1,arg2):
print(arg1,arg2)
f1([1,2,3],{‘a‘:"A",‘b‘:"b"})
以上是关于Python基础--函数进阶与装饰器的主要内容,如果未能解决你的问题,请参考以下文章