def lazy_print(*args):
def pr():
print(args)
return pr

当我们调用lazy_print()时,返回的并不是求和结果,而是求和函数:

>>> p = lazy_print(1,2,3,4,5)
>>> p
<function lazy_print.<locals>.pr at 0x000000000364ED90>

调用函数p时,才真正计算求和的结果:

>>> p()
(1, 2, 3, 4, 5)

.....

一个函数可以返回一个计算结果,也可以返回一个函数。

返回一个函数时,牢记该函数并未执行,返回函数中不要引用任何可能会变化的变量。

首先定义一个装饰器:

def log(fun):
def wrapper():
print('This is wrapper!')
return fun()
return wrapper

观察上面的log,因为它是一个decorator,所以接受一个函数作为参数,并返回一个函数。我们要借助Python的@语法,把decorator置于函数的定义处:

@log
def now():
print('This is now!')

调用now()函数,不仅会运行now()函数本身,还会在运行now()函数前打印一行日志:

>>> now()
This is wrapper!
This is now!

@log放到now()函数的定义处,相当于执行了语句:

now = log(now)

由于log()是一个decorator,返回一个函数,所以,原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即在log()函数中返回的wrapper()函数。

因为我们讲了函数也是对象,它有__name__等属性,但你去看经过decorator装饰之后的函数,它们的__name__已经从原来的'now'变成了'wrapper'

>>> now.__name__
'wrapper'

因为返回的那个wrapper()函数名字就是'wrapper',所以,需要把原始函数的__name__等属性复制到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。

不需要编写wrapper.__name__ = func.__name__这样的代码,Python内置的functools.wraps就是干这个事的,所以,一个完整的decorator的写法如下:

 
import functools

def log(fun):
@functools.wraps(fun)
def wrapper():
print('This is wrapper!')
return fun()
return wrapper
 

import functools是导入functools模块。模块的概念稍候讲解。现在,只需记住在定义wrapper()的前面加上@functools.wraps(func)即可。

>>> now.__name__
'now'

一.装饰器

1.函数装饰圈的定义

函数装饰器:一种装饰函数的函数

2.个人理解两层函数装饰器

两层函数装饰器个人觉得他其实就是把需要装饰的函数名丢入形参,然后用一个嵌套的函数对其头尾进行添加程序,但是不能减少他的程序内容,他的原来程序不变只能增不能减少,然后返回装饰好的子函数,再全局定义一个变量名与要装饰的函数名相同名字,并且将装饰后的函数调用赋予改变量.

1.简单的例子(无参函数)

#有个函数f1
def f1():
print('nick machachong')
#我们要对齐装饰使其输出打印内容上下都加了'-'线
def f1_deco(f1): #第一步吧变量丢进去
def wrapper():
#print(50*'-') #我们加的内容
#print('nick machachong') #这个等同于f1()
#print(50*'-') #我们要加的内容
#这是我们需要的打印效果 pritn(50*'-')
f1()
print(50*'-')
return wrapper f1 = f1_deco(f1) #前面的f1是变量f1,函数本是f1并未发生变化只是一个变量名接受了f1
f1() ​```
--------------------------------------------------
nick machachong
--------------------------------------------------
​```
#另外种写法
@f1_deco
def f1():
print('nick machachong')
f1()
​```
--------------------------------------------------
nick machachong
--------------------------------------------------
​```

2.关于有参函数

#如函数
def sum(x,y):
print(x+y)
#我们要对齐装饰使其输出打印内容上下都加了'-'线
def sum_deco(sum):
def wrapper(x,y):
print("-")
sum(x,y)
return sum
sum() = sum_deco(sum)
sum(x,y) #多个值
def sb(x,y,z=2,b=1):
print(x,y,z,b)
#把他进行封装
def sb_deco(sb):
def wrapper(*args,**kwargs):
print('nick machachong')
sb(*args,**kwargs)
return wrapper
sb = sb_deco(sb)
sb(1,23,2,3)

3.对于有返回值的函数式

#有返回值
def sb(x,y,z=2,b=1):
return x,y,z,b
#对于返回值乘以3
def sb_deco(sb):
def wrapper(*args,**kwargs):
res = sb(*args,**kwargs)
res = list(res)
for a in range(len(res)):
res[a] = 3*res[a]
res = tuple(res)
return res
return wrapper
sb = sb_deco(sb)
print(sb(2,3,4,5))

4.装饰圈模板

def sb(*args,**kwargs):
pass
#装饰器模板
def sb_deco(sb):
def wrapper(*args,**kwargs):
#sb(*args,**kwargs)
res = sb(*args,**kwargs) #赋值的时候已经开始调用了所有没必要在写一步调用
return res
return wrapper
sb = sb_deco(sb)
sb(1,23,2,3,12,312,312,3,123)

三.对于三层装饰器理解

#比如说一个函数为,我们对齐装饰,打印内容前后上下加'-'
def sb():
print('i am sb') #装饰
def sb_deco(sb):
def wrapper():
print('-'*5)
sb()
print('-'*5)
return wrapper
#只时候我们加条件要区分是你输入还是我输入的 # 只时候我们加条件要区分是你输入还是我输入的, 我那边聪明肯定不是SB
def sb_deco(sb):
def wrapper():
if user == 'you':
print('-' * 5)
sb()
print('-' * 5)
elif user == 'i':
print('-' * 5)
sb()
print('这是不可能的')
print('-' * 5)
return wrapper
#这时候我们导入的参数多了个user
def user(user):
def sb_deco(sb):
def wrapper():
if user == 'you':
print('-' * 5)
sb()
print('-' * 5)
elif user == 'i':
print('-' * 5)
sb()
print('这是不可能的')
print('-' * 5)
return wrapper
return sb_deco @user('you') #其中@user('you') 相当于a =user('you') sb = a(sb)
def sb():
print('i am sb')
sb()
#他是对于输入的值的一层装饰,判断他是拿来的

Python 装饰器为什么要双层嵌套

如果没有嵌套,实际上装饰器返回的要么是原函数的定义,要么根本不是函数,也就是说函数根本没有被装饰。

即使碰巧得到了想要的结果也是装饰器在定义的阶段便运行的,这其实是不应该发生的,因为这意味着不调用函数,照样会有输出,而且效果不能在函数被调用时复现。

代码说明如下

如果直接一个单层的装饰器
def log(func):                                         #
print('call %s():' % func.__name__) #
return func() #3
@log                                                   #
def now(): #
    print('2015-3-25') now()

此时#4运行时就会运行#2的print,并且装饰器要求#3  return一个函数,而不是函数的值(正确写法是returnfunc)

因此#3的语法错误,会报错'NoneType' object is not callable

3.如果改正为不要括号的写法

def log(func):                                         #
print('call %s():' % func.__name__) #
return func #3

,则不会报错,但是会在@log时直接执行装饰器的内容(规则1),即执行print,此时装饰器返回的还是原函数a(规则3),所以之后调用函数a就无法触发装饰器

3,python的设计者为什么这么设计?

为什么不把单层的装饰器的执行顺序等价为现在的双层装饰器的执行顺序

一是两层结构可以传递被装饰的函数a的参数

二是不改变函数a和函数log原来的结构,因为log本身就是一个函数,他与其他函数有相同的执行顺序

@log

def now():

pass

完全等价于now=log(now)

解释:

装饰器等于修改了原函数定义,返回的仍然是函数定义!而不是单纯的返回的函数运行结果。

此时再调用被装饰函数实际执行的是已经被装饰过的函数(即可以理解为调用具有装饰器的函数,该函数的实际定义已经不是你看到的定义,而是被装饰器修改后的)

一次定义后即可重复使用,就像普通定义函数一般。

个人以为如果用装饰器,又不嵌套的写法是完全有悖装饰器初衷的,这时大可不必用装饰器,还不如直接函数调用来的实在。

装饰器是什么,有什么功能,能用在什么业务场景?

概念:

1.装饰器的实现是由闭包支撑的;

2.装饰器本质上是⼀个python函数,它可以在让其他函数在不需要做任何代码的变动的前提下增加额外的功能;

3.装饰器的返回值也是⼀个函数的对象,它经常用于有切面需求的场景,实现路由传参,flask的路由传参依赖于装饰器,浏览器通过url访问到装饰器的路由,从而访问视图函数获得返回的HTML页面;

应用场景:

1.可以在外层函数加上时间计算函数,计算函数运行时间;

2.计算函数运行次数;

3.可以用在框架的路由传参上;

4.插入日志,作为函数的运行日志;

5.事务处理,可以让函数实现事务的一致性,让函数要么一起运行成功,要么一起运行失败;

6.缓存,实现缓存处理;

7.权限的校验,在函数外层套上权限校验的代码,实现权限校验;

写出一个单例的装饰器(使一个本来不是单例类的类变成单例类))

def set_func(func):  
    __singleton = None  
  
    def call_func(*args, **kwargs):  
        nonlocal __singleton  
        if not __singleton:  
            __singleton = func(*args, **kwargs)  
            return __singleton  
        else:  
            return __singleton  
    return call_func  
 
 
@set_func  
class Std(object):  
  
    def __init__(self, name, age):  
        self.name = name  
        self.age = age  
  
  
s2 = Std('jack',18)  
print(id(s2),s2.name,s2.age)  
  
s1 = Std('leo',23)  
print(id(s1),s1.name,s1.age)  
运行结果:

139727292442832 jack 18  
139727292442832 jack 18  
登录判断装饰器:

之前做过的一个用flask框架实现的移动app项目,里面大量用到是否已经登录的判断,如果这个业务逻辑大量重复地写在视图函数,代码的复用性很差,因此我将登录判断封装成装饰器,然后用这个装饰器装饰每一个需要验证是否登录的视图函数,代码如下:

def login_required(view_func):  
    """自定义装饰器判断用户是否登录"""  
 
    @wraps(view_func)  
    def wrapper(*args, **kwargs):  
        """具体实现判断用户是否登录的逻辑"""  
        user_id = session.get('user_id')  
        if not user_id:  
            return jsonify(errno=RET.SESSIONERR, errmsg='用户未登录')  
        else:  
            g.user_id = user_id  
            return view_func(*args, **kwargs)  
  
    return wrapper  
    总结:装饰器是python三大神器(迭代器,生成器,装饰器)中比较难理解的,但是它的本质实际上就是闭包,我们在闭包函数或者类外面封装了一套逻辑,因此可以增强函数的功能,增加权限校验,事务一致性,缓存等功能,这就是装饰器,使漂亮的姑娘(函数)变得更加漂亮。

参考自:

https://www.cnblogs.com/dylan-wu/p/6705149.html?utm_source=itdadao&utm_medium=referral

https://cloud.tencent.com/developer/article/1471212

https://blog.csdn.net/zhengyajun_email/article/details/86715467

https://blog.csdn.net/max_weil/article/details/80063551

分享

  •  
  •  
  •  
扫描二维码

确定文件的位置--浏览文件夹对话框folderBrowserDialog的更多相关文章

  1. MFC 如何创建浏览文件夹的对话框

    如何创建浏览文件夹的对话框 如何创建浏览文件夹的对话框 CString CXXXXDlg::GetOpenfolderPath() { BROWSEINFO bi; ZeroMemory(&b ...

  2. MFC中打开选择文件夹对话框,并将选中的文件夹地址显示在编辑框中

    一般用于选择你要将文件保存到那个目录下,此程序还包含新建文件夹功能 BROWSEINFO bi; ZeroMemory(&bi, sizeof(BROWSEINFO));  //指定存放文件的 ...

  3. winform 实现选择文件和选择文件夹对话框

    //选择文件,点击[浏览],选择文件 private void button1_Click(object sender, EventArgs e) { OpenFileDialog openFileD ...

  4. c++选择文件夹对话框

    1,目的 提供一个对话框供用户选择一个文件夹路径. 2,原理&实现 先贴上这个工具类的源码: 在你的程序中使用静态方法 CSelectFolderDlg::Show() 就能显示一个选择文件夹 ...

  5. CFileDialog(文件夹对话框类)和CFontDialog(字体设置对话框类)的使用学习

    CFileDialog(文件夹对话框类) 主要用于文件的保存,另存,打开,关闭等功能 功能“另存为”的实现: void CTXTDlg::OnFileSaveas() { LPCTSTR szFilt ...

  6. MAC在Finder栏显示所浏览文件夹路径的方法

    我们在使用MAC时,Finder栏默认只显示当前浏览的文件夹名称,而没有显示访问路径,这个问题该怎么解决呢? 操作步骤: 打开“终端”(应用程序->实用工具),输入以下两条命令: default ...

  7. Delphi 弹出Windows风格的选择文件夹对话框, 还可以新建文件夹

    Delphi 弹出Windows风格的选择文件夹对话框, 还可以新建文件夹     unit Unit2; interface uses  Windows, Messages, SysUtils, V ...

  8. CodeSmith使用总结--下拉列表和文件夹对话框属性

    上一篇有点短了,因为实在没有什么可说的,这一篇会多一点.O(∩_∩)O~ 一.下拉列表 关于如何在CodeSmith中创建一个下拉列表的属性框其实很简单,是要使用C#中的枚举就行了,看操作. 首先定义 ...

  9. java 选择文件夹对话框

    java swing 选择文件夹对话框 import java.io.File; import javax.swing.JFileChooser; public class Test2 { publi ...

随机推荐

  1. windows10 Sqlserver卸载 指定账户不存在

    在windows卸载程序时,有时会出现因提示“指定的账户不存在”而无法删除,如下: 这时时候要先选择要删除的项目,进行修复后再进行删除就可以正常删除了

  2. DNS named.conf文件详解

    配置文件: /etc/named.conf /在NAMED.CONF         配置文件中使用//和/* */来进行注释, options { /*OPTIONS选项用来定义一些影响整个DNS服 ...

  3. DG日志不应用,GAP,主备切换解决思路与办法

    环境ORACLE 10G OS WINDOWS 对于DG故障解决思路,DG日志切换不进行应用,DG出现GAP解决方法,DG主备库切换, 当DG出现故障时,第一时间检测alert日志,服务器OS日志,网 ...

  4. 统计的一个小题目python实现

    最近面试碰到的一个题目,业余时间用python实现的. 拿到数据,先用sort 命令排序,也可再进一步去重复 sort -k 1,2  data.txt |uniq  > data.new # ...

  5. zend studio 13.6.1 安装+破解+汉化

    zend studio 13.6.1 X64 安装+破解+汉化+补丁 一.下载相关文件 1.官网原版下载 : http://downloads.zend.com/studio-eclipse/13.6 ...

  6. <The Art of Readable Code> 笔记一

    第1章  代码应易理解 (Code should be easy to understand) 基本原则:好的代码,能够减少 “别人” 理解它的时间. “别人” 不仅指的是 “其它人”,也可能是 “以 ...

  7. iOS 本地通知 操作

    iOS 本地通知 操作 1:配置通知:然后退出程序: UILocalNotification *localNotif = [[UILocalNotification alloc] init]; loc ...

  8. VMWare Workstation 15 serial number

    Serial number:YZ718-4REEQ-08DHQ-JNYQC-ZQRD0 该Key仅供体验,支持正版,从我做起. 点击此处购买正版

  9. java线程-java多线程之可见性

    可见性:一个线程对共享变量值的修改,能够及时呗其他线程看到. 共享变量:如果一个变量在多个线程的内存中都存在副本,那么这个变量就是这几个线程的共享变量. java内存模型(JMM) 描述了java程序 ...

  10. 强制关机后导致VBOX(4.2.16 r86992)的虚拟机不可使用问题的解决MEMO

    上周六晚上由于有急事,就强制关机,导致今天晚上用VirtualBox(4.2.16 r86992)时,虚拟机上写着不可使用. 显示异常Message如下: D:\tinderbox\win-4.2\s ...