GVKun编程网logo

Python装饰器,自我混淆

462

以上就是给各位分享Python装饰器,自我混淆,同时本文还将给你拓展PYTHON-有参装饰器,无参装饰器,语法糖、python之装饰器(类装饰器,函数装饰器)、python装饰器、Python装饰器-

以上就是给各位分享Python装饰器,自我混淆,同时本文还将给你拓展PYTHON-有参装饰器,无参装饰器,语法糖、python之装饰器(类装饰器,函数装饰器)、python装饰器、Python装饰器-装饰流程,执行顺序等相关知识,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!

本文目录一览:

Python装饰器,自我混淆

Python装饰器,自我混淆

我是Python装饰器的新手(哇,很棒的功能!),并且我很难使以下内容起作用,因为self参数混杂在一起。

#this is the decoratorclass cacher(object):    def __init__(self, f):        self.f = f        self.cache = {}    def __call__(self, *args):        fname = self.f.__name__        if (fname not in self.cache):            self.cache[fname] = self.f(self,*args)        else:            print "using cache"        return self.cache[fname]class Session(p.Session):    def __init__(self, user, passw):        self.pl = p.Session(user, passw)    @cacher    def get_something(self):        print "get_something called with self = %s "% self        return self.pl.get_something()s = Session(u,p)s.get_something()

运行此命令时,我得到:

get_something called with self = <__main__.cacher object at 0x020870F0> Traceback:...AttributeError: ''cacher'' object has no attribute ''pl''

我做的那条线 self.cache[fname] = self.f(self,*args)

问题 -显然,问题在于self缓存器对象而不是Session实例,而该实例实际上没有pl属性。但是我找不到解决方法。

我已经考虑过但不能使用的解决方案
-我想到使decorator类返回一个函数而不是一个值(如本文的2.1节),以便self在正确的上下文中进行评估,但这是不可能的因为我的装饰器是作为一个类实现的,并且使用了内置
__call__方法。然后,我认为 不要
为装饰器使用类,这样就不需要__call__方法,但是我不能这样做,因为我需要在装饰器调用之间保持状态(即,跟踪self.cache属性中的内容) 。

问题 -因此,除了使用全局cache字典变量(我没有尝试过,但假定可以使用)之外,还有其他方法可以使装饰器起作用吗?

答案1

小编典典

像这样使用描述符协议:

import functoolsclass cacher(object):    def __init__(self, f):        self.f = f        self.cache = {}    def __call__(self, *args):        fname = self.f.__name__        if (fname not in self.cache):            self.cache[fname] = self.f(self,*args)        else:            print "using cache"        return self.cache[fname]    def __get__(self, instance, instancetype):        """Implement the descriptor protocol to make decorating instance         method possible.        """        # Return a partial function with the first argument is the instance         #   of the class decorated.        return functools.partial(self.__call__, instance)

编辑:

如何运作?

在装饰器中使用描述符协议将使我们能够以正确的实例作为自身来访问装饰的方法,也许一些代码可以提供更好的帮助:

现在,当我们要做的时候:

class Session(p.Session):    ...    @cacher    def get_something(self):        print "get_something called with self = %s "% self        return self.pl.get_something()

相当于:

class Session(p.Session):    ...    def get_something(self):        print "get_something called with self = %s "% self        return self.pl.get_something()    get_something = cacher(get_something)

因此,现在get_something是cacher的实例。因此,当我们调用方法get_something时,它将被转换为此(由于描述符协议):

session = Session()session.get_something  #  <==> session.get_something.__get__(get_something, session, <type ..>)# N.B: get_something is an instance of cacher class.

并且因为:

session.get_something.__get__(get_something, session, <type ..>)# returnget_something.__call__(session, ...) # the partial function.

所以

session.get_something(*args)# <==>get_something.__call__(session, *args)

希望这会解释它是如何工作的:)

PYTHON-有参装饰器,无参装饰器,语法糖

PYTHON-有参装饰器,无参装饰器,语法糖

装饰器
装饰器就是闭包函数的一种应用场景

一 为何要用装饰器
#开放封闭原则:对修改封闭,对扩展开放

二 什么是装饰器
装饰器他人的器具,本身可以是任意可调用对象,被装饰者也可以是任意可调用对象。
强调装饰器的原则:
1 不修改被装饰对象的源代码
2 不修改被装饰对象的调用方式
装饰器的目标:在遵循1和2的前提下,为被装饰对象添加上新功能

三 装饰器的使用
1. 无参装饰器
def 装饰器名字(func):
def wrapper(*args,**kwargs):
#func是被装饰的对象,这里可以写执行前的功能
res=func(*args,**kwargs)
# 这里可以写执行后的功能
return res
return wrapper

@装饰器名字 #被装饰的函数名字 = 装饰器名字(被装饰的函数内存地址)
def 被装饰的函数名字():
pass

2. 有参装饰器
def 装饰器名字(参数1,参数2,...):
def outter(func):
def wrapper(*args,**kwargs):
res=func(*args,**kwargs)
return res
return wrapper
return outter
@装饰器名字(值1,值2,...) #@outter
def 被装饰的函数名字():
pass


3. 语法糖:在被装饰对象正上方单独一行写
无参:@装饰器的名字
有参:@装饰器的名字(arg1,arg2,...)

@timmer # index=timmer(index)
def index():
pass

@auth2(engine=''file'') # @名字 # index=名字(index)
def index():
pass


叠加多个装饰器
@deco1
@deco2
@deco3
def index():
pass
解释语法的顺序:自下而上,deco3=》deco2=》deco1
调用index()时才开始触发装饰器内部闭包函数的执行,闭包函数执行顺序是:自上而条,
deco1.wrapper1=>deco2.wrapper2=>deco3.wrapper3


wrapper.__name__=func.__name__ (函数名字赋值)
wrapper.__doc__=func.__doc__ (注释的内用赋值#)



python之装饰器(类装饰器,函数装饰器)

python之装饰器(类装饰器,函数装饰器)

@decorator这个语法相当于 执行 func = decorator(func),为func函数装饰并返回
<1> 类装饰器

## 装饰器
#类装饰器
class Decorator:
    def __init__(self,func):
        ## 赋值函数
        self.func = func

    def __call__(self, *args, **kwargs):
        ## 一call顶万物
        print(''假设这里开始执行其他的代码'')
        self.other_func()
        self.func(*args, **kwargs)
        self.other_func2()
        print(''假设这里在执行完这个函数后,执行了其他的代码'')
        return
    def other_func(self):
        print(''这是一个打酱油的函数'')

    @staticmethod
    def other_func2():
        print(''这是一个吃瓜的函数'')

## 测试一下
@Decorator
def test_a(a,b):
    print(f''print a :{a}'')
    print(f''print b :{b}'')
    c = a+b
    print(c)
    return c

test_a(1,2)

执行结果:

 

<2>函数装饰器

函数装饰器还是比较多的,相对来说

def zsq_func(func):
    print(''这是一个函数装饰器'')
    def wrapper(*args,**kwargs):
        try:
            print(''开始前,执行的代码'')
            func(*args,**kwargs)
            print(''结束了,执行的代码'')
            return
        except:
            print(''出错了的处理'')

    print(''执行装饰器函数前可以执行其他代码'')
    return wrapper

## 测试一下
@zsq_func
def func(a,b):
    print(f''print a :{a}'')
    print(f''print b :{b}'')
    c = a+b
    print(c)
    return c
func(1,2)

结果如下:

 

python装饰器

python装饰器

1. 简介

​ Python中的装饰器是一种可以装饰其它对象的工具。类似于装饰模式,实际是对原有对象进行功能上的增强(原有对象可以是对象、函数、类)。其使用像java 的注解。

​ 该工具本质上是一个可调用的对象(callable),所以装饰器一般可以由函数、类来实现。装饰器本身需要接受一个被装饰的对象作为参数,该参数通常为函数、方法、类等对象。装饰器需要返回一个对象,该对象可以是 经过处理的原参数对象、一个包装且类似原参数的对象;或者返回一个不相干内容。

​ 基于@语法和函数闭包,将原函数封装在闭包中,然后将函数赋值为一个新的函数(内置函数),执行函数时再在内层函数中执行闭包中的原函数。可以在你改变函数内部代码和调用的前提下,实现在函数执行和执行拓展功能。

​ 适用于多个函数系统统一在执行前后定义一些功能。

2. 简单使用

  1. 基于类实现装饰器,对函数进行增强
# 简单装饰器。 基于类实现装饰器,对函数进行增强
class wrap(object):
    def __init__(self):
        pass

    def __call__(self, obj):
        print("======1")
        print(obj)
        print("======2")
        return obj


@wrap()  # 等价于 foo = wrap(foo)
def foo():
    print('hello decorator!')


print(foo())

结果:

======1
<function foo at 0x0000021fdbB9EF70>
======2
hello decorator!
None
  1. 基于函数实现装饰器,对函数进行增强
# 简单装饰器。 基于函数实现装饰器,对函数进行增强
def wrap(obj):
    print("======1")
    print(obj)
    print("======2")
    return obj


@wrap  # 等价于 foo = wrap(foo)
def foo():
    print('hello decorator!')


print(foo())  

结果:

======1
<function foo at 0x000002171B1F1F70>
======2
hello decorator!
None
  1. 给被装饰的对象添加一个name 属性, 并且值设置为 张三
# 装饰器-给被装饰的对象添加一个name 属性, 并且值设置为 张三
def wrap(obj):
    obj.name = '张三'
    return obj


@wrap
class User:
    def __int__(self):
        pass


print(User.name)  # => hello decorator!
print(User)

结果:

张三
<class '__main__.User'>
  1. 函数装饰器: 对传入的对象进行增强,增强后返回新的对象
# 函数装饰器: 对传入的对象进行增强,增强后返回新的对象
def outer(func):
    def inner():
        print("hello inner")
        func()

    return inner


@outer  # foo = outer(foo)
def foo():
    print('hello foo')


print(foo.__name__)
foo()  # => hello foo

结果:

inner
hello inner
hello foo
  1. 对象方法装饰器。和普通的函数装饰器的区别是多了一个默认的self 参数
# desc: 对象方法装饰器。和普通的函数装饰器的区别是多了一个默认的self 参数

def outer(obj):
    def inner(self):
        print('hello inner')
        obj(self)

    return inner


class Zoo(object):
    def __init__(self):
        pass

    @outer  # => zoo = outer(zoo)
    def zoo(self):
        print('hello zoo')


zoo = Zoo()
print(zoo.zoo.__name__)
zoo.zoo()

结果:

inner
hello inner
hello zoo
  1. 类方法装饰器。 相当于对类进行增强
# desc: 类方法装饰器。 相当于对类进行增强

def outer(clss):
    class Inner(object):
        def __init__(self):
            self.clss = clss()

        def __getattr__(self, attr):
            return getattr(self.clss, attr)

    return Inner


@outer  # Zoo = outer(Zoo)
class Zoo(object):
    def __init__(self):
        pass

    def say(self):
        print('Hello World!')


zoo = Zoo()
print(zoo.__class__)
zoo.say()

结果:

<class '__main__.outer.<locals>.Inner'>
Hello World!
  1. 特殊应用的装饰器,比如,类静态属性装饰器。 如下代码可以看出ratio 会作为一个属性,而不是方法
class Foo(object):
    def __init__(self, height, weigth):
        self.height = height
        self.weigth = weigth

    @property
    def ratio(self):
        return self.height / self.weigth


foo = Foo(176, 120)
print(foo.ratio)

自己实现:

class Prop(object):
    def __init__(self, fget):
        self.fget = fget

    def __get__(self, instance, owner):
        return self.fget(instance)


class Foo(object):
    def __init__(self, height, weigth):
        self.height = height
        self.weigth = weigth

    @Prop
    def ratio(self):
        return self.height / self.weigth


foo = Foo(176, 120)
print(foo.ratio)
  1. 装饰带参数和返回值的对象
# desc: 装饰带参数和返回值的对象

# def outer(func):  # 函数装饰器
#     def inner(x, y):
#         print('hello inner')
#         return func(x, y)
#
#     return inner


def outer(func):  # 函数装饰器
    def inner(*args, **kwargs):
        print('hello inner')
        return func(*args, **kwargs)

    return inner


@outer
def add(x, y):
    return x + y


print(add(1, 4))

结果:

hello inner
5
  1. 装饰器本身带参数
# desc: 装饰器本身带参数

url_mapping = {}


def route(url):
    def decorator(func):  # 函数装饰器
        url_mapping[url] = func
        return func

    return decorator


@route('/home')
def home():
    print(1)
    pass


@route('/index')
def index():
    print(2)
    pass


print(url_mapping)

结果:

{'/home': <function home at 0x000001B16D480D30>, '/index': <function index at 0x000001B16D48F040>}

Python装饰器-装饰流程,执行顺序

Python装饰器-装饰流程,执行顺序

最近看到一个关于Flask的CTF(RealWorld CTF 2018 web题bookhub)文章 其中的一个trick是装饰器的顺序问题,就想写篇博客回顾下装饰器~

首先强烈推荐很久之前看的一篇博文 (翻译)理解PYTHON中的装饰器 关于什么是装饰器看这篇文章就好了~ 这里主要想写关于多个装饰器的执行流程

装饰顺序

示例代码

# import pdb;pdb.set_trace()

def functionOne(function_to_decorate):
    print("functionOne初始化")
  
    def wrapperOne():
    	pass
    return wrapperOne

def functionTwo(function_to_decorate):
    print("functionTwo初始化")
  
    def wrapperTwo():
    	pass
    return wrapperTwo

@functionOne
@functionTwo
def testFunction():
    pass

# 输出结果
functionTwo初始化
functionOne初始化

从上面我们能得知:装饰顺序,就近装饰

然后我们利用下面的代码进行一步探究 如下我们得知:执行这段代码,相当于: 首先,将testFunction函数打包给wrapperTwo,由于没有调用,functionTwo整体返回了wrapperTwo,而没有执行 然后,functionOne将wrapperTwo作为参数,打包成wrapperOne

# import pdb;pdb.set_trace()

def functionOne(function_to_decorate):
    print("functionOne初始化")
  
    def wrapperOne():
    	print("第一处"+function_to_decorate.__name__)
    	function_to_decorate()
    return wrapperOne

def functionTwo(function_to_decorate):
    print("functionTwo初始化")
  
    def wrapperTwo():
    	print("第二处"+function_to_decorate.__name__)
    	function_to_decorate()
    return wrapperTwo

@functionOne
@functionTwo
def testFunction():
    print(''index'')

testFunction()

#输出结果
functionTwo初始化
functionOne初始化
第一处wrapperTwo
第二处testFunction
index

执行顺序

从上面的第二段代码我们已经能看出部分执行顺序了 就是它会优先执行我们打包好的wrapperOne,因为从起始的testFunction,wrapperTwo都已经打包在wrapperOne 可以说成执行顺序,就远执行 我们继续执行下面的代码:

# import pdb;pdb.set_trace()

def functionOne(function_to_decorate):
    print("functionOne初始化")
  
    def wrapperOne():
    	print("第一处"+function_to_decorate.__name__)
    	function_to_decorate()
    	print("wrapperOne")
    return wrapperOne

def functionTwo(function_to_decorate):
    print("functionTwo初始化")
  
    def wrapperTwo():
    	print("第二处"+function_to_decorate.__name__)
    	function_to_decorate()
    	print("wrapperTwo")
    return wrapperTwo

@functionOne
@functionTwo
def testFunction():
    print(''index'')

testFunction()

# 输出结果
functionTwo初始化
functionOne初始化
第一处wrapperTwo
第二处testFunction
index
wrapperTwo
wrapperOne

这个执行顺序可能也困扰了很多人,现在我们从输出结果看 对照代码,就很容易清楚了,执行到wrapperOne中的function_to_decorate时 其实相当于跳转到了函数wrapperTwo,然后执行wrapperTwo

Flask @login_require

从上面的几个例子我们应该大概了解了,多个装饰器进行装饰以及执行的顺序 我们来看这道CTF题目,我们首先需要知道的是Flask中路由就是一个装饰

from flask import Flask

app = Flask(__name__)
app.debug = True

# import pdb;pdb.set_trace()

# 为了更好的控制输出,自定义了loginRequire装饰器
def loginRequire(function_to_decorate):
    print("loginRequire初始化")
  
    def wrapperTwo():
    	print("loginRequire装饰成功")
    	print(function_to_decorate.__name__)
    	return function_to_decorate()
    return wrapperTwo

@loginRequire
@app.route(''/up'')
def up():
    return "装饰路由放在上面!"

@app.route(''/down'')
@loginRequire
def down():
    return "装饰路由放在下面!"

if __name__ == ''__main__'':
    app.run()

# 分别访问两个url输出结果
loginRequire初始化
loginRequire初始化
 * Debugger is active!
 * Debugger PIN: 244-957-971
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [24/Aug/2018 19:01:30] "GET /up HTTP/1.1" 200 -
loginRequire装饰成功
down
127.0.0.1 - - [24/Aug/2018 19:01:35] "GET /down HTTP/1.1" 200 -

从输出结果我们能清楚的看到up的装饰,并没有执行装饰器 如果按照我们上面的分析,无论在上面还是下面都会执行的啊??只是顺序不同罢了~ 我们利用pdb来一步步调试查看哪里的问题,部分log如下:

> c:\users\bayi\desktop\test\256.py(17)<module>()
-> @loginRequire
(Pdb) s
> c:\users\bayi\desktop\test\256.py(18)<module>()
-> @app.route(''/up'')
(Pdb) s
> c:\users\bayi\.virtualenvs\test-gq7eoxbq\lib\site-packages\flask\app.py(1252)route()-><function Fla...at 0x0376F978>
-> return decorator
(Pdb) s
--Call--
> c:\users\bayi\.virtualenvs\test-gq7eoxbq\lib\site-packages\flask\app.py(1248)decorator()
-> def decorator(f):
(Pdb) f
<function up at 0x0376F9C0>
(Pdb) s
> c:\users\bayi\.virtualenvs\test-gq7eoxbq\lib\site-packages\flask\app.py(1249)decorator()
-> endpoint = options.pop(''endpoint'', None)
(Pdb) s
> c:\users\bayi\.virtualenvs\test-gq7eoxbq\lib\site-packages\flask\app.py(1250)decorator()
-> self.add_url_rule(rule, endpoint, f, **options)
(Pdb) f
<function up at 0x0376F9C0>
#===================================================================================#
		上方up 下方down
#===================================================================================#
> c:\users\bayi\desktop\test\256.py(22)<module>()
-> @app.route(''/down'')
(Pdb) s
> c:\users\bayi\desktop\test\256.py(23)<module>()
-> @loginRequire
(Pdb) s
--Call--
> c:\users\bayi\desktop\test\256.py(6)loginRequire()
-> def loginRequire(function_to_decorate):
(Pdb) s
> c:\users\bayi\desktop\test\256.py(7)loginRequire()
-> print("loginRequire初始化")
(Pdb) s
loginRequire初始化
> c:\users\bayi\desktop\test\256.py(9)loginRequire()
-> def wrapperTwo():
(Pdb) s
> c:\users\bayi\desktop\test\256.py(13)loginRequire()
-> return wrapperTwo
(Pdb) s
--Return--
> c:\users\bayi\desktop\test\256.py(13)loginRequire()-><function log...at 0x0071C468>
-> return wrapperTwo
(Pdb) s
--Call--
> c:\users\bayi\.virtualenvs\test-gq7eoxbq\lib\site-packages\flask\app.py(1248)decorator()
-> def decorator(f):
(Pdb) f
<function loginRequire.<locals>.wrapperTwo at 0x0071C468>

从上面的执行流程,打印出不断出现的f,我们能看出,两个顺序的f值不同 在up中,f=up() 在down中,f=wrapperTwo() 这点符合预期,装饰位置不同,然而在执行Flask源码 add_url_rule时 如上面log所示,直接添加了f的值

> c:\users\bayi\.virtualenvs\test-gq7eoxbq\lib\site-packages\flask\app.py(1249)decorator()
-> endpoint = options.pop(''endpoint'', None)
(Pdb) s
> c:\users\bayi\.virtualenvs\test-gq7eoxbq\lib\site-packages\flask\app.py(1250)decorator()
-> self.add_url_rule(rule, endpoint, f, **options)
(Pdb) f
<function up at 0x0376F9C0>

也就是添加路由的时候会选择丢失外层的路由,只装饰route下方的函数 在add_url_rule中,有这段注释:

Basically this example::

    @app.route(''/'')
    def index():
    	pass

Is equivalent to the following::

    def index():
    	pass
    app.add_url_rule(''/'', ''index'', index)

关于Python装饰器,自我混淆的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于PYTHON-有参装饰器,无参装饰器,语法糖、python之装饰器(类装饰器,函数装饰器)、python装饰器、Python装饰器-装饰流程,执行顺序等相关知识的信息别忘了在本站进行查找喔。

本文标签: