函数名的运⽤, 第⼀类对象

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

  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-装饰器初识,闭包的更多相关文章

  1. Python装饰器与闭包

    闭包是Python装饰器的基础.要理解闭包,先要了解Python中的变量作用域规则. 变量作用域规则 首先,在函数中是能访问全局变量的: >>> a = 'global var' & ...

  2. python 装饰器-初识

    一.装饰器的形成过程 1.函数无参数,无返回值 import time def f1(): # 无参数,无返回值 time.sleep(1) print("Hello, World!&quo ...

  3. python装饰器执行顺序

    . python 装饰器 1) 2层装饰器 def decorator(func): # TODO def wrapper(*args, **kwargs): # TODO func(*args, * ...

  4. python 全栈开发,Day11(函数名应用,闭包,装饰器初识,带参数以及带返回值的装饰器)

    一.函数名应用 函数名是什么?函数名是函数的名字,本质:变量,特殊的变量. 函数名(),执行此函数. python 规范写法 1. #后面加一个空格,再写内容,就没有波浪线了. 2.一行代码写完,下面 ...

  5. python 装饰器、内部函数、闭包简单理解

    python内部函数.闭包共同之处在于都是以函数作为参数传递到函数,不同之处在于返回与调用有所区别. 1.python内部函数 python内部函数示例: def test(*args): def a ...

  6. 《流畅的Python》第三部分 把函数视作对象 【一等函数】【使用一等函数实现设计模式】【函数装饰器和闭包】

    第三部分 第5章 一等函数 一等对象 在运行时创建 能赋值给变量或数据结构中的元素 能作为参数传递给函数 能作为函数的返回结果 在Python中,所有函数都是一等对象 函数是对象 函数本身是 func ...

  7. Python装饰器,Python闭包

    可参考:https://www.cnblogs.com/lianyingteng/p/7743876.html suqare(5)等价于power(2)(5):cube(5)等价于power(3)(5 ...

  8. python函数下篇装饰器和闭包,外加作用域

    装饰器和闭包的基础概念 装饰器是一种设计模式能实现代码重用,经常用于查日志,性能测试,事务处理等,抽离函数大量不必的功能. 装饰器:1.装饰器本身是一个函数,用于装饰其它函数:2.功能:增强被装饰函数 ...

  9. 关于python装饰器

    关于python装饰器,不是系统的介绍,只是说一下某些问题 1 首先了解变量作用于非常重要 2 其次要了解闭包 def logger(func): def inner(*args, **kwargs) ...

随机推荐

  1. python 字典(dictionary)一些方法

    1.python 字典(Dictionary) keys() 函数以列表返回一个字典所有的键. keys()语法: dict.keys() 2.setdefault()方法 python字典setde ...

  2. C#socket客户端自己输入消息发送到服务端通信实现通信

    一,服务端代码 using System; using System.Collections.Generic; using System.Linq; using System.Text; using ...

  3. Vue证明题

    看来我需要对我的vue能力做一个证明了~~ 最近辞职了,又逢病重,找工作的时候发现对vue要求蛮高的,说会不行,还必须要有过vue的项目. 我这种半路出家的哪里来的vue的项目,公司又不是那种一线互联 ...

  4. java中垃圾收集的方法有哪些?

    java中垃圾收集的方法有哪些? 一.引用计数算法(Reference Counting) 介绍:给对象添加一个引用计数器,每当一个地方引用它时,数据器加1:当引用失效时,计数器减1:计数器为0的即可 ...

  5. [几何]计算不规则多边形的面积、中心、重心(Android,转)

    转自:[几何]计算不规则多边形的面积.中心.重心 最近项目用到:在不规则多边形的中心点加一个图标.(e.g: xx地区发生暴雪,暴雪区域是多边形,给多边形中心加一个暴雪的图标) 之前的设计是,计算不规 ...

  6. 使用Python+Qt时解决QTreeWidget中的内容超出边界后自动隐藏的问题

    问题: 默认情况下,内容超出边界后会自动省略,以...代替,而且无法出现水平滚动条 解决方法: 把VerticalScrollBar和HorizontalScrollBar的值都设为ScrollBar ...

  7. word--->pdf资料转载..

    https://blog.csdn.net/dsn727455218/article/details/80667927

  8. Java 基础 - 自动装箱,valueOf装箱,new -->使用 == 和 equals比较

    总结 关于equals 比较  记住:equals方法比较的是真正的值 两个包装类比较,比较的是包装的基本数据类型的值 基本数据类型和包装类型比较时,会先把基本数据类型包装后再比较 (但是因为equa ...

  9. 后端优化(2)—— BA与图优化

  10. 使用Intent实现Activity之间传值与跳转(转)

    转:http://blog.csdn.net/cjjky/article/details/6337447 在一个Android的应用程序中,很少只存在一个Activity,一般都有多个Activity ...