Python函数中使用@

稍提一下的基础

funfun()的区别

以一段代码为例:

def fun():print('fun')return Nonea = fun() #fun函数并将返回值给a
print('a的值为',a)
b = fun #将fun函数地址赋给b
b() #调用b,b和fun指向的地址相同
print('b的值为',b)
'''输出
fun
a的值为 None
fun
b的值为 <function fun at 0x00000248E1EBE0D0>
'''

根据输出可以看出,a=fun()是将函数fun的返回值(None)赋给a,而b=fun是将函数的地址赋给b,如果调用函数,需要b()

类似的,其他内置函数也可以通过这种方法,相当于起了一个同名的函数

>>>a = abs
>>>a(-1)
1

除此之外,原来的函数名也被覆盖为其他函数,例如

def fun():print('fun')
abs = fun
abs() #输出fun

综上,可以理解为fun,abs在不带括号时为变量,该变量包含了函数在内容的地址

返回函数

参考链接:https://www.liaoxuefeng.com/wiki/1016959663602400/1017434209254976

以廖老师的教程为例

def lazy_sum(*args):def sum():ax = 0for n in args:ax = ax + nreturn axreturn sum
>>> f = lazy_sum(1, 3, 5, 7, 9)
>>> f
<function lazy_sum.<locals>.sum at 0x101c6ed90>
>>>f()
25

在单步调试中可以发现,当读到def sum():时,解释器会直接跳到return sumsum函数的地址返回给f,因此f()即为执行sum() (不是非常准确,但先如此理解)

如果对返回函数还是有些不理解的话,可以假设lazy_sum()的返回值改为1

def lazy_sum(*args):def sum():ax = 0for n in args:ax = ax + nreturn axreturn 1f = lazy_sum(1,3,5,7,9)
print(f)#Q输出1print(f())#报错'int' object is not callable

此时无论lazy_sum()的参数如何修改,都会把1赋给f,而1()是不可调用的,因此会报错

:star:返回函数中的闭包问题也要了解一下,内嵌函数可以访问外层函数的变量

参数的嵌套调用

仍然上述例子,此时将lazy_sum()改为空函数,内嵌的sum()需要参数:

def lazy_sum():def sum(*args):ax = 0for n in args:ax = ax + nreturn axreturn sumf = lazy_sum()(1,3,5,7,9)
print(f)#输出25

按照运算的优先级,可以理解为:

  1. 执行 lazy_sum(),返回sum;
  2. 执行sum(1,3,5,7,9),返回25;
  3. 25赋给f

如果有了以上基础,再来看@的用法就会觉得很容易了

@的使用

如果需要具体理解装饰器,可以参考廖老师的博客,本文仅介绍@的执行流程

本文参考了 Python @函数装饰器及用法(超级详细),Python中的注解“@”

不带参数的单一使用(一个@修饰)

def spamrun(fn):def sayspam():print("spam,spam,spam")fn()return sayspam
@spamrun
def useful():print('useful')useful()
'''
输出:
spam,spam,spam
useful
'''

修饰效果相当于useful = spamrun(useful),具体步骤如下:

  1. 在初始化时,解释器读到@spamrun,此时将下方的useful作为参数传入到spamrun
  2. spamrun(useful)中,由于是返回函数,直接将sayspam()的内存地址赋给useful
  3. 执行useful(),此时useful指向了sayspam,因此打印spam,spam,spam。然后执行fn(),此时的fn才指向原来useful()的地址,开始执行print('useful')

执行流程可以在下图了解一下,可以理解为经过@后,useful已经不直接指向函数useful()的地址了,而是sayspam。再调用useful()时,执行sayspam(),由于fn保存原先useful()函数的地址,因此可以执行useful()的功能,即可以打印出'useful'。如果‘使坏’把fn()去掉的话,相当于useful()再也不会执行了

一般情况下,使用@时不会改变函数原先的执行逻辑,而只是增加功能,因此成为装饰器,如廖老师教程中可以使原函数打印日志

def log(func):def wrapper(*args, **kw):print('call %s():' % func.__name__)return func(*args, **kw)return wrapper
@log
def now():print('2015-3-25')now()
'''
call now():
2015-3-25
'''

不带参数的多次使用(两个@)

def spamrun(fn):def sayspam():print("spam,spam,spam")fn()return sayspamdef spamrun1(fn):def sayspam1():print("spam1,spam1,spam1")fn()return sayspam1@spamrun
@spamrun1
def useful():print('useful')useful()
'''
spam,spam,spam
spam1,spam1,spam1
useful
'''

修饰效果相当于useful = spamrun(spamrun1(useful))

叠加使用时,装饰器的调用顺序和声明顺序是相反的,可以理解成是一个递归的过程。

  1. 遇到@spamrun,开始向下寻找def 函数名
  2. 结果第二行也是一个@。@spamrun1继续向下找
  3. 遇到了def useful,执行useful = spamrun1(useful)
  4. 回归。@spamrun1返回useful@spamrun,执行useful=spamrun(useful)

带参数的单次使用

以廖老师教程中的举例,简化一些,先不考虑*args,**kw,因为涉及到返回函数的闭包问题

def log(text):def decorator(func):def wrapper():print('%s %s():' % (text, func.__name__))return func()return wrapperreturn decorator@log('execute')
def now():print('2015-3-25')now()

修饰效果相当于now=log('execute')(now)

​ 1. 解释器读到@log('execute'),先执行了log('execute'),返回函数decorator

​ 2. 将now作为decorator(func)的形参,返回warpper

3. 将`warpper`的内存地址赋给变量`now`

此时调用now(),先执行完print(...),然后return func()。注意此处是带括号的,因此执行了真正的now函数,最终return的为None

带参数的多次调用可以将之前的情况组合即可

总结

  1. @行不带参数
@XXX
def funXXX():

会被解释成funXXX = XXX(funXXX)

  1. 如果@那行中带参数,则被解释成funXXX = XXX(@行的参数)(funXXX)
  2. 要深刻理解返回函数以及funfun()的区别
  3. 函数的内存地址,函数变量,函数的名称的区别。默认情况下,函数变量指向函数的内存地址,但也可以被改变

初学Python,学识短浅,希望多多交流

matlab的syms无法在函数中使用_Python函数中使用@相关推荐

  1. python中函数的定义_Python函数是什么_如何定义和调用函数?

    函数是什么? 本节Python培训教程是要大家认识一个"老朋友"--函数. 与数学中的函数不同,在Python中,函数不是看上去冰冷无聊的规则和公式,而是实打实的.有自己作用的代码 ...

  2. python中函数的调用_python函数的调用、函数中变量的使用之详解

    '''函数的工作原理:函数内部的变量都是临时的, 当你的函数返回以后,返回值可以被赋予一个变量. 这里是创建了一个新变量,用来存放函数的返回值. ''' def secret_formula(star ...

  3. python函数参数定义_python函数定义中的5种参数

    python函数参数定义 Python函数定义中的5种参数类型: (5 Types of Arguments in Python Function Definition:) default argum ...

  4. python函数修饰器_Python函数装饰器指南

    Python 具有强大的功能和富有表现力的语法.我最喜欢的装饰之一.在设计模式的上下文中,装饰器动态更改方法或类功能,而不必直接使用子类.当您需要扩展功能,但不想修改原函数时,这是理想的选择.我们可以 ...

  5. if python 判断函数返回值_Python函数的返回值和作用域

    函数的返回值和作用域 1.返回值 def guess(x): if x > 3: return "> 3" else: return "<= 3&quo ...

  6. python average函数详解_python 函数详解

    函数函数是代码的一种组织形式 函数应该能完成一项特定的工作,而且一般一个函数只完成一项工作 有些语言,分函数和过程两个概念,通俗解释是,有返回结果的是函数,无返回结果的叫过程,python不加以区分 ...

  7. python 函数参数传递机制_Python函数参数传递机制(超级详细)

    Python中,函数参数由实参传递给形参的过程,是由参数传递机制来控制的.通过学习<Python函数值传递和引用传递>一节我们知道,根据实际参数的类型不同,函数参数的传递方式分为值传递和引 ...

  8. python封装一个函数并调用_python - 函数的封装与调用

    一.函数的定义,函数名,函数体以及函数的调用 1.函数的定义语法: def 函数名(): 函数体 2.函数名的定义与变量名命名一样 3.函数的封装与调用 #函数的封装 defyue():print(' ...

  9. python闭包函数的必要条件_Python 函数 functions、闭包 closure

    一般我们都知道,函数只有在调用的时候才会被执行 但是我们在用 def 定义一个函数时,编译器做了什么?或者什么都没做? 实际上,当用 def 定义一个函数时,就创建了一个函数对象,该对象封装了函数体, ...

最新文章

  1. Unity中那些事半功倍的好插件
  2. maven热部署插件-jetty
  3. matlab求kcf算法响应图_剖析KCF
  4. 鸟哥的Linux私房菜(基础篇)-第四章、安装 CentOS 5.x 与多重开机小技巧(三.1. 本练习机的规划--尤其是分割参数)
  5. JavaFX:TouchGesture内存泄漏?
  6. Promise使用,return的运用,解决回调地狱
  7. 防止linux系统文件被误删除,Linux系统防止误删除文件
  8. SteveY对Amazon和Google平台的吐槽
  9. 清华大学发布《人工智能芯片技术白皮书(2018)》
  10. Linux基础入门之VM和centos的安装使用
  11. C#【高级篇】 IntPtr是什么?怎么用?
  12. [培训-DSP快速入门-1]:DSP概述(基本框架、CPU, GPU, FPGA比较,常见型号)
  13. excel怎么设置自动计算_机械设计工程师辅助计算Excel表格,自动进行选型计算...
  14. 联想硬盘保护系统安装
  15. Xmarks Hosts
  16. 【独行秀才】macOS Big Sur 11.5 Beta 1(20G5023d)原版镜像
  17. 高等数学:第十一章 无穷级数(1)常数项技术的概念、性质、审敛法、幂级数
  18. node+express 搭建商城项目(2-建立 Mysql链接 完成注册账号接口)
  19. 修改菜单项字体的颜色和大小
  20. MS中Perl脚本实现原子随机掺杂(或生成空位)

热门文章

  1. DSGN:基于深度立体几何网络的3D目标检测(香港大学提出)
  2. 双线性插值(Bilinear Interpolation)
  3. Linux(64位)下OpenBabel 2.4.1、python2.7和Ipython实战(二)
  4. 面向生信分析的高性 RStudio 服务器
  5. Nature子刊:宏基因组组装基因组实现谱系解析
  6. mSystems:青大苏晓泉阐述微生物组的Beta多样性-从全局比对到局部比对
  7. NC:电缆细菌减少水稻种植土壤中的甲烷排放
  8. 华为年薪200万招募的“天才少年”,一句话让我陷入了深思
  9. 香港中文大学Center for Gut Microbiota Research招聘启事
  10. QIIME 2用户文档. 19使用q2-vsearch聚类OTUs(2019.7)