码迷,mamicode.com
首页 > 编程语言 > 详细

python-装饰器初识,闭包

时间:2018-06-14 18:15:32      阅读:178      评论:0      收藏:0      [点我收藏+]

标签:修改   内部函数   water   art   closure   argument   fun   col   mod   

 函数名的运?, 第?类对象

?. 函数名的运?.
  函数名是?个变量, 但它是?个特殊的变量, 与括号配合可以执?函数的变量

  1. 函数名的内存地址

def func():
  print("呵呵")
print(func)
结果:
<function func at 0x1101e4ea0>

  2. 函数名可以赋值给其他变量

def func():
  print("呵呵")
print(func)
a = func # 把函数当成?个变量赋值给另?个变量
a() # 函数调? func()

  3. 函数名可以当做容器类的元素

def func1():
    print("呵呵")
def func2():
    print("呵呵")
def func3():
    print("呵呵")
def func4():
    print("呵呵")lst = [func1, func2, func3]
for i in lst:
    i()

 4. 函数名可以当做函数的参数 

def func():
    print("吃了么")
def func2(fn):
    print("我是func2")
    fn() # 执?传递过来的fn
    print("我是func2")
func2(func) # 把函数func当成参数传递给func2的参数fn.

 5. 函数名可以作为函数的返回值

def func_1():
    print("这?是函数1")
    def func_2():
        print("这?是函数2")
    print("这?是函数1")
    return func_2
fn = func_1() # 执?函数1. 函数1返回的是函数2, 这时fn指向的就是上?函数2
fn() # 执?上?返回的函数        

  

二. 闭包

  什么是闭包? 闭包就是内层函数, 对外层函数(非全局)的变量的引?. 叫闭包 

def func1():
  name = "alex"
  def func2():
  print(name) # 闭包
  func2()
func1()
结果:
alex

我们可以使?__closure__来检测函数是否是闭包. 使?函数名.__closure__返回cell就是 闭包. 返回None就不是闭包

def func1():
    name = "alex"
    def func2():
        print(name) # 闭包
    func2()
    print(func2.__closure__) # (<cell at 0x10c2e20a8: str object at
0x10c3fc650>,)
func1()    

  问题, 如何在函数外边调?内部函数呢?

def outer():
    name = "alex"
    # 内部函数
    def inner():
        print(name)
    return inner
fn = outer() # 访问外部函数, 获取到内部函数的函数地址
fn() # 访问内部函数    

  那如果多层嵌套呢? 很简单, 只需要?层?层的往外层返回就?了

def func1():
    def func2():
        def func3():
            print("嘿嘿")
        return func3
    return func2
func1()()()        

  由它我们可以引出闭包的好处. 由于我们在外界可以访问内部函数. 那这个时候内部函
数访问的时间和时机就不?定了, 因为在外部, 我可以选择在任意的时间去访问内部函数.
个时候. 想?想. 我们之前说过, 如果?个函数执?完毕. 则这个函数中的变量以及局部命名
空间中的内容都将会被销毁. 在闭包中. 如果变量被销毁了. 那内部函数将不能正常执?.
. python规定. 如果你在内部函数中访问了外层函数中的变量. 那么这个变量将不会消亡.
将会常驻在内存中. 也就是说. 使?闭包, 可以保证外层函数中的变量在内存中常驻. 这样做
有什么好处呢? 非常?的好处. 我们来看?个关于爬?的代码:

from urllib.request import urlopen
def but():
    content = urlopen("http://www.xiaohua100.cn/index.html").read()
    def get_content():
        return content
    return get_content
fn = but() # 这个时候就开始加载校花100的内容
# 后?需要?到这??的内容就不需要在执??常耗时的?络连接操作了
content = fn() # 获取内容
print(content)
content2 = fn() # 重新获取内容
print(content2)    

  综上, 闭包的作?就是让?个变量能够常驻内存. 供后?的程序使?.

 

. 装饰器初识

  在说装饰器之前啊. 我们先说?个软件设计的原则: 开闭原则, ?被成为开放封闭原则,
你的代码对功能的扩展是开放的, 你的程序对修改源代码是封闭的. 这样的软件设计思路可以
更好的维护和开发.
  开放: 对功能扩展开放
  封闭: 对修改代码封闭

接下来我们来看装饰器. ?先我们先模拟?下女娲造?

def create_people():
    print("?娲很厉害. 捏个泥?吹??就成了?了")
create_people()

  ok! 很简单. 但是现在问题来了. 上古时期啊. 天?很不. 这时候呢?旱三年. 女娲再去
造?啊就很困难了. 因为啥呢? 没?. 也就是说. 女娲想造?必须得先和泥. 浇点??才能造
?

def create_people():
    print("浇?") # 添加了个浇?功能
    print("?娲很厉害. 捏个泥?吹??就成了?了")
create_people()

  搞定. 但是, 我们来想想. 是不是违背了我们最开始的那个约定"开闭原则", 我们是添加了
新的功能. 对添加功能开放. 但是修改了源代码啊. 这个就不好了. 因为开闭原则对修改是封
闭的. 那怎么办. 我们可以这样做.

def create_people():
    # print("浇?") # 添加了个浇?功能, 不符合开闭原则了
    print("?娲很厉害. 捏个泥?吹??就成了?了")
def warter():
    print("先浇?")
    create_people() # 造?
# create_people() # 这个就不?了.
warter() # 访问浇?就好了

  现在问题?来了. 你这个函数写好了. 但是由于你添加了功能. 重新创建了个函数. 在这之
前访问过这个函数的?就必须要修改代码来访问新的函数water() 这也要修改代码. 这个也不
. 依然违背开闭原则. ?且. 如果你这个函数被?量的?访问过. 你让他们所有?都去改.
你就要倒霉了. 不?死你就??了.
  那怎么办才能既不修改原代码, ?能添加新功能呢? 这个时候我们就需要?个装饰器了.
饰器的作?就是在不修改原有代码的基础上, 给函数扩展功能

def create_people():
    # print("浇?") # 添加了个浇?功能, 不符合开闭原则了
    print("?娲很厉害. 捏个泥?吹??就成了?了")
def warter(fn):
    def inner():
        print("浇浇?")
        fn()
        print("施肥")
    return inner
# # create_people() # 这个就不?了.
# warter() # 访问浇?就好了
func = warter(create_people)
func() # 有?问了. 下游访问的不依然是func么, 不还是要改么?
create_people = warter(create_people)
create_people() # 这回就好了吧,    

  说?下执?流程:

技术分享图片

1. ?先访问warter(create_people)

2. 把你的?标函数传递给warter的形参fn. 那么后?如果执?了fn意味着执?了你的?
标函数create_people
3. warter()执?就?句话. 返回inner函数. 这个时候. 程序认为warter()函数执?完. 那么
前?的create_people函数名被重新覆盖成inner函数
4. 执?create_people函数. 实际上执?的是inner函数. ?inner中访问的恰恰使我们最开
始传递进去的原始的create_people函数

 

结论: 我们使?warter函数把create_people给包装了?下. 在不修改create_people的前提下.
完成了对create_people函数的功能添加

 

这是?个装饰器的雏形. 接下来我们观察?下代码. 很不好理解. 所以呢. 我们可以使?语法
糖来简化我们的代码

def warter(fn):
    def inner():
        print("浇浇?")
        fn()
        print("施肥")
    return inner
@warter # 相当于 create_people = warter(create_people)
def create_people():
print("?娲很厉害. 捏个泥?吹??就成了?了")
create_people()
结果:
浇浇?
?娲很厉害. 捏个泥?吹??就成了?了
施肥    

  我们发现, 代码运?的结果是?样的. 所谓的语法糖语法: @装饰器
类似的操作在我们?活中还有很多. 比?说. 约?约.

# 2--> 现在啊, 这个?情?较不好, 什么??蛇神都出来了.
# 那怎么办呢? 问问??板. ??板在前?给?家带路
# 这时候我们就需要在约之前啊. 先问问??板了. 所以也要给函数添加?个功能, 这?依然可以使
?装饰器
def wen_jin(fn):
    def inner():
        print("问问??板, ?情怎么样, 质量好不好")
        fn()
        print("?, ??板骗我")
    return inner
@wen_jin
def yue(): # 1--> 约?约函数
    print("约?约")
yue()

  ok, 接下来. 我们来看?下, 我约的话, 我想约个?. 比如约wusir, 这时, 我们要给函数添加
?个参数

# 2--> 现在啊, 这个?情?较不好, 什么??蛇神都出来了.
# 那怎么办呢? 问问??板. ??板在前?给?家带路
# 这时候我们就需要在约之前啊. 先问问??板了. 所以也要给函数添加?个功能, 这?依然可以使
?装饰器
def wen_jin(fn):
    def inner():
        print("问问??板, ?情怎么样, 质量好不好")
        fn()
        print("?, ??板骗我")
    return inner
@wen_jin
def yue(name): # 1--> 约?约函数
    print("约?约", name)
yue("wusir")
结果:
Traceback (most recent call last):
File "/Users/sylar/PycharmProjects/oldboy/fun_2.py", line 131, in
<module>
yue("wusir")
TypeError: inner() takes 0 positional arguments but 1 was given

  程序报错. 分析原因: 我们在外?访问yue()的时候. 实际上访问的是inner函数. ?inner函数
没有参数. 我们给了参数. 这肯定要报错的. 那么该怎么改呢? inner加上参数就好了

def wen_jin(fn):
    def inner(name):
        print("问问??板, ?情怎么样, 质量好不好")
        fn(name)
        print("?, ??板骗我")
    return inner
@wen_jin
def yue(name):
    print("约?约", name)
yue("wusir")

  这样就够了么? 如果我的yue()改成两个参数呢? 你是不是还要改inner. 对了. ?*args
**kwargs来搞定多个参数的问题

def wen_jin(fn):
        def inner(*args, **kwargs): # 接收任意参数
            print("问问??板, ?情怎么样, 质量好不好")
            fn(*args, **kwargs) # 把接收到的内容打散再传递给?标函数
            print("?, ??板骗我")
        return inner
@wen_jin
def yue(name):
    print("约?约", name)
yue("wusir")    

  搞定. 这时 wen_jin()函数就是?个可以处理带参数的函数的装饰器
光有参数还不够. 返回值呢?

def wen_jin(fn):
    def inner(*args, **kwargs):
        print("问问??板, ?情怎么样, 质量好不好")
        ret = fn(*args, **kwargs) # 执??标函数. 获取?标函数的返回值
        print("?, ??板骗我")
        return ret # 把返回值返回给调?者
    return inner
@wen_jin
def yue(name):
    print("约?约", name)
    return "?萝莉" # 函数正常返回
r = yue("wusir") # 这?接收到的返回值是inner返回的. inner的返回值是?标函数的返回值
print(r)

  返回值和参数我们都搞定了. 接下来给出装饰器的完整模型代码(必须记住)

# 装饰器: 对传递进来的函数进?包装. 可以在?标函数之前和之后添加任意的功能.
def wrapper(func):
    def inner(*args, **kwargs):
        ‘‘‘在执??标函数之前要执?的内容‘‘‘
        ret = func(*args, **kwargs)
        ‘‘‘在执??标函数之后要执?的内容‘‘‘
        return ret
    return inner
# @wrapper 相当于 target_func = wrapper(target_func) 语法糖
@wrapper
def target_func():
    print("我是?标函数")
# 调??标函数
target_func()

  

 

 

python-装饰器初识,闭包

标签:修改   内部函数   water   art   closure   argument   fun   col   mod   

原文地址:https://www.cnblogs.com/liuye1990/p/9183237.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!