Python类方法装饰器与自我参数?

Mar*_*ark 125 python

如何将类字段作为参数传递给类方法上的装饰器?我想做的是:

class Client(object):
    def __init__(self, url):
        self.url = url

    @check_authorization("some_attr", self.url)
    def get(self):
        do_work()
Run Code Online (Sandbox Code Playgroud)

它抱怨自我不存在将self.url传递给装饰者.有没有解决的办法?

li.*_*idm 171

是.而不是在类定义时传入instance属性,而是在运行时检查它:

def check_authorization(f):
    def wrapper(*args):
        print args[0].url
        return f(*args)
    return wrapper

class Client(object):
    def __init__(self, url):
        self.url = url

    @check_authorization
    def get(self):
        print 'get'

>>> Client('http://www.google.com').get()
http://www.google.com
get
Run Code Online (Sandbox Code Playgroud)

装饰器拦截方法参数; 第一个参数是实例,因此它从中读取属性.您可以将属性名称作为字符串传递给装饰器,getattr如果您不想对属性名称进行硬编码,请使用:

def check_authorization(attribute):
    def _check_authorization(f):
        def wrapper(self, *args):
            print getattr(self, attribute)
            return f(self, *args)
        return wrapper
    return _check_authorization
Run Code Online (Sandbox Code Playgroud)


小智 33

from re import search
from functools import wraps

def is_match(_lambda, pattern):
    def wrapper(f):
        @wraps(f)
        def wrapped(self, *f_args, **f_kwargs):
            if callable(_lambda) and search(pattern, (_lambda(self) or '')): 
                f(self, *f_args, **f_kwargs)
        return wrapped
    return wrapper

class MyTest(object):

    def __init__(self):
        self.name = 'foo'
        self.surname = 'bar'

    @is_match(lambda x: x.name, 'foo')
    @is_match(lambda x: x.surname, 'foo')
    def my_rule(self):
        print 'my_rule : ok'

    @is_match(lambda x: x.name, 'foo')
    @is_match(lambda x: x.surname, 'bar')
    def my_rule2(self):
        print 'my_rule2 : ok'



test = MyTest()
test.my_rule()
test.my_rule2()
Run Code Online (Sandbox Code Playgroud)

ouput:my_rule2:好的

  • 非常感谢!这应该是公认的答案.我正在试图找出类方法的装饰器. (2认同)

max*_*ywb 28

更简洁的示例可能如下:

#/usr/bin/env python3
from functools import wraps

def wrapper(method):
    @wraps(method)
    def _impl(self, *method_args, **method_kwargs):
        method_output = method(self, *method_args, **method_kwargs)
        return method_output + "!"
    return _impl

class Foo:
    @wrapper
    def bar(self, word):
        return word

f = Foo()
result = f.bar("kitty")
print(result)
Run Code Online (Sandbox Code Playgroud)

哪个会打印:

kitty!
Run Code Online (Sandbox Code Playgroud)

  • IMO,这优于 /sf/answers/821184591/。它演示了内部函数“_impl”如何访问“self”以出于任何目的操纵该“self”。我需要构建一个简单的方法装饰器,它在类中方法的_子集_上增加“self.id”,并且仅在类中应用了“@”装饰语法的那些方法。与 /sf/answers/3942607791/ 相比,该语法糖将其转发给未来的自己,后者放弃了该糖并要求我深入了解 __init__` 方法。 (3认同)

小智 11

我知道这个问题已经很老了,但是之前没有提出过以下解决方法。这里的问题是您无法self在类块中访问,但可以在类方法中访问。

让我们创建一个虚拟装饰器来重复某个函数几次。

import functools
def repeat(num_rep):
    def decorator_repeat(func):
        @functools.wraps(func)
        def wrapper_repeat(*args, **kwargs):
            for _ in range(num_rep):
                value = func(*args, **kwargs)
            return 
        return wrapper_repeat
    return decorator_repeat
Run Code Online (Sandbox Code Playgroud)
class A:
    def __init__(self, times, name):
        self.times = times
        self.name = name
    
    def get_name(self):
        @repeat(num_rep=self.times)
        def _get_name():
            print(f'Hi {self.name}')
        _get_name()
Run Code Online (Sandbox Code Playgroud)


小智 10

另一种选择是放弃语法糖并在__init__类的 中进行装饰。

def countdown(number):
    def countdown_decorator(func):
        def func_wrapper():
            for index in reversed(range(1, number+1)):
                print(index)
            func()
        return func_wrapper
    return countdown_decorator

class MySuperClass():
    def __init__(self, number):
        self.number = number
        self.do_thing = countdown(number)(self.do_thing)
    
    def do_thing(self):
        print('im doing stuff!')


myclass = MySuperClass(3)

myclass.do_thing()
Run Code Online (Sandbox Code Playgroud)

哪个会打印

3
2
1
im doing stuff!
Run Code Online (Sandbox Code Playgroud)


小智 7

我知道这是一个老问题,但这个解决方案还没有被提及,希望它即使在 8 年后的今天也能对某人有所帮助。

那么,用包装纸包裹起来怎么样?假设我们不能更改装饰器,也不能装饰init中的那些方法(它们可能是 @property 装饰的或其他什么)。总是有可能创建自定义的、特定于类的装饰器,该装饰器将捕获 self 并随后调用原始装饰器,并将运行时属性传递给它。

这是一个工作示例(f-strings 需要 python 3.6):

import functools

# imagine this is at some different place and cannot be changed
def check_authorization(some_attr, url):
        def decorator(func):
                @functools.wraps(func)
                def wrapper(*args, **kwargs):
                        print(f"checking authorization for '{url}'...")
                        return func(*args, **kwargs)
                return wrapper
        return decorator

# another dummy function to make the example work
def do_work():
        print("work is done...")

###################
# wrapped wrapper #
###################
def custom_check_authorization(some_attr):
        def decorator(func):
                # assuming this will be used only on this particular class
                @functools.wraps(func)
                def wrapper(self, *args, **kwargs):
                        # get url
                        url = self.url
                        # decorate function with original decorator, pass url
                        return check_authorization(some_attr, url)(func)(self, *args, **kwargs)
                return wrapper
        return decorator
        
#############################
# original example, updated #
#############################
class Client(object):
        def __init__(self, url):
                self.url = url
    
        @custom_check_authorization("some_attr")
        def get(self):
                do_work()

# create object
client = Client(r"/sf/ask/821179551/")

# call decorated function
client.get()
Run Code Online (Sandbox Code Playgroud)

输出:

checking authorisation for '/sf/ask/821179551/'...
work is done...
Run Code Online (Sandbox Code Playgroud)


Jul*_*ian 5

你不能。有没有self在类体内,因为没有实例存在。您需要传递它,例如,str包含属性名称以在实例上查找,然后返回的函数可以执行该操作,或者完全使用不同的方法。