Python 实例方法的装饰器可以访问该类吗?

Python 实例方法的装饰器可以访问该类吗?,python,decorator,Python,Decorator,我有一些大致如下的东西。基本上,我需要从定义中用于实例方法的装饰器访问实例方法的类 def装饰器(视图): #执行需要view类的操作 print view.im_类 返回视图 类ModelA(对象): @装饰师 def a_方法(自身): #做点什么 通过 代码如下所示: AttributeError:“函数”对象没有属性“im\U类” 我发现了类似的问题/答案-和-但这些问题/答案依赖于一种变通方法,即通过获取第一个参数在运行时获取实例。在我的例子中,我将根据从其类中收集的信息调用该方法,

我有一些大致如下的东西。基本上,我需要从定义中用于实例方法的装饰器访问实例方法的类

def装饰器(视图):
#执行需要view类的操作
print view.im_类
返回视图
类ModelA(对象):
@装饰师
def a_方法(自身):
#做点什么
通过
代码如下所示:

AttributeError:“函数”对象没有属性“im\U类”


我发现了类似的问题/答案-和-但这些问题/答案依赖于一种变通方法,即通过获取第一个参数在运行时获取实例。在我的例子中,我将根据从其类中收集的信息调用该方法,因此我迫不及待地等待调用的到来。

您将有权访问在装饰方法中调用该方法的对象的类,装饰程序应该返回该类。像这样:

def decorator(method):
    # do something that requires view's class
    def decorated(self, *args, **kwargs):
        print 'My class is %s' % self.__class__
        method(self, *args, **kwargs)
    return decorated
使用ModelA类,以下是它的作用:

>>> obj = ModelA()
>>> obj.a_method()
My class is <class '__main__.ModelA'>
obj=ModelA() >>>obj.a_方法() 我的班级是
问题是,当调用decorator时,类还不存在。试试这个:

def loud_decorator(func):
    print("Now decorating %s" % func)
    def decorated(*args, **kwargs):
        print("Now calling %s with %s,%s" % (func, args, kwargs))
        return func(*args, **kwargs)
    return decorated

class Foo(object):
    class __metaclass__(type):
        def __new__(cls, name, bases, dict_):
            print("Creating class %s%s with attributes %s" % (name, bases, dict_))
            return type.__new__(cls, name, bases, dict_)

    @loud_decorator
    def hello(self, msg):
        print("Hello %s" % msg)

Foo().hello()
此程序将输出:

Now decorating <function hello at 0xb74d35dc>
Creating class Foo(<type 'object'>,) with attributes {'__module__': '__main__', '__metaclass__': <class '__main__.__metaclass__'>, 'hello': <function decorated at 0xb74d356c>}
Now calling <function hello at 0xb74d35dc> with (<__main__.Foo object at 0xb74ea1ac>, 'World'),{}
Hello World
正在装修
正在创建具有属性{“uuuuuu模块”的类Foo(,):''uuuuu main','uuuu元类:,'hello':}
现在调用(,'World'),{}
你好,世界

正如您所看到的,您必须找到一种不同的方法来做您想做的事情。

正如Ants所指出的,您无法从类中获得对该类的引用。但是,如果您想区分不同的类(而不是操作实际的类类型对象),可以为每个类传递一个字符串。您还可以使用类样式的装饰器将任何其他参数传递给装饰器

class Decorator(object):
    def __init__(self,decoratee_enclosing_class):
        self.decoratee_enclosing_class = decoratee_enclosing_class
    def __call__(self,original_func):
        def new_function(*args,**kwargs):
            print 'decorating function in ',self.decoratee_enclosing_class
            original_func(*args,**kwargs)
        return new_function


class Bar(object):
    @Decorator('Bar')
    def foo(self):
        print 'in foo'

class Baz(object):
    @Decorator('Baz')
    def foo(self):
        print 'in foo'

print 'before instantiating Bar()'
b = Bar()
print 'calling b.foo()'
b.foo()
印刷品:

before instantiating Bar()
calling b.foo()
decorating function in  Bar
in foo

另外,

如果您使用的是Python2.6或更高版本,则可以使用类装饰器,可能类似这样的东西(警告:未测试的代码)

方法装饰器通过添加“use_class”属性将方法标记为感兴趣的方法-函数和方法也是对象,因此您可以向它们附加额外的元数据

在类被创建之后,类装饰器将遍历所有的方法,并对已标记的方法执行任何需要的操作


如果您希望所有方法都受到影响,那么您可以省去方法装饰器,而只使用类装饰器。

正如其他人所指出的,在调用装饰器时还没有创建类但是,可以使用decorator参数注释函数对象,然后在元类的
\uuuuu new\uuuu
方法中重新修饰函数。您需要直接访问函数的
\uuu dict\uuuu
属性,至少对我来说,
func.foo=1
导致了一个AttributeError。

要做的是创建一个临时缓存,它存储在方法上,然后使用其他东西(事实是Flask将使用
register
类方法注册类)来实际包装该方法

您可以重用此模式,这次使用元类,以便在导入时包装该方法

def route(rule, **options):
    """A decorator that is used to define custom routes for methods in
    FlaskView subclasses. The format is exactly the same as Flask's
    `@app.route` decorator.
    """

    def decorator(f):
        # Put the rule cache on the method itself instead of globally
        if not hasattr(f, '_rule_cache') or f._rule_cache is None:
            f._rule_cache = {f.__name__: [(rule, options)]}
        elif not f.__name__ in f._rule_cache:
            f._rule_cache[f.__name__] = [(rule, options)]
        else:
            f._rule_cache[f.__name__].append((rule, options))

        return f

    return decorator
在实际类上(可以使用元类执行相同的操作):


来源:

这里有一个简单的例子:

def mod_bar(cls):
    # returns modified class

    def decorate(fcn):
        # returns decorated function

        def new_fcn(self):
            print self.start_str
            print fcn(self)
            print self.end_str

        return new_fcn

    cls.bar = decorate(cls.bar)
    return cls

@mod_bar
class Test(object):
    def __init__(self):
        self.start_str = "starting dec"
        self.end_str = "ending dec" 

    def bar(self):
        return "bar"
输出为:

>>> import Test
>>> a = Test()
>>> a.bar()
starting dec
bar
ending dec

这是一个古老的问题,但金星人遇到了

它似乎具有修饰方法的能力,并在这样做的同时允许您访问类和方法。 注意:调用setattr(ob,wrapped.\uu name\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

不管怎样。。。下面的示例已完成,应该运行

import sys
from functools import wraps
import venusian

def logged(wrapped):
    def callback(scanner, name, ob):
        @wraps(wrapped)
        def decorated(self, *args, **kwargs):
            print 'you called method', wrapped.__name__, 'on class', ob.__name__
            return wrapped(self, *args, **kwargs)
        print 'decorating', '%s.%s' % (ob.__name__, wrapped.__name__)
        setattr(ob, wrapped.__name__, decorated)
    venusian.attach(wrapped, callback)
    return wrapped

class Foo(object):
    @logged
    def bar(self):
        print 'bar'

scanner = venusian.Scanner()
scanner.scan(sys.modules[__name__])

if __name__ == '__main__':
    t = Foo()
    t.bar()
正如马克所言:

  • 任何装饰器都是在类构建之前调用的,因此装饰器不知道
  • 我们可以标记这些方法,并在以后进行任何必要的后期处理
  • 我们有两个后处理选项:在类定义结束时自动执行,或者在应用程序运行之前的某个位置执行。我更喜欢使用基类的第一种方法,但也可以使用第二种方法
  • 此代码显示了如何使用自动后处理:

    def expose(**kw):
        "Note that using **kw you can tag the function with any parameters"
        def wrap(func):
            name = func.func_name
            assert not name.startswith('_'), "Only public methods can be exposed"
    
            meta = func.__meta__ = kw
            meta['exposed'] = True
            return func
    
        return wrap
    
    class Exposable(object):
        "Base class to expose instance methods"
        _exposable_ = None  # Not necessary, just for pylint
    
        class __metaclass__(type):
            def __new__(cls, name, bases, state):
                methods = state['_exposed_'] = dict()
    
                # inherit bases exposed methods
                for base in bases:
                    methods.update(getattr(base, '_exposed_', {}))
    
                for name, member in state.items():
                    meta = getattr(member, '__meta__', None)
                    if meta is not None:
                        print "Found", name, meta
                        methods[name] = member
                return type.__new__(cls, name, bases, state)
    
    class Foo(Exposable):
        @expose(any='parameter will go', inside='__meta__ func attribute')
        def foo(self):
            pass
    
    class Bar(Exposable):
        @expose(hide=True, help='the great bar function')
        def bar(self):
            pass
    
    class Buzz(Bar):
        @expose(hello=False, msg='overriding bar function')
        def bar(self):
            pass
    
    class Fizz(Foo):
        @expose(msg='adding a bar function')
        def bar(self):
            pass
    
    print('-' * 20)
    print("showing exposed methods")
    print("Foo: %s" % Foo._exposed_)
    print("Bar: %s" % Bar._exposed_)
    print("Buzz: %s" % Buzz._exposed_)
    print("Fizz: %s" % Fizz._exposed_)
    
    print('-' * 20)
    print('examine bar functions')
    print("Bar.bar: %s" % Bar.bar.__meta__)
    print("Buzz.bar: %s" % Buzz.bar.__meta__)
    print("Fizz.bar: %s" % Fizz.bar.__meta__)
    
    产量:

    Found foo {'inside': '__meta__ func attribute', 'any': 'parameter will go', 'exposed': True}
    Found bar {'hide': True, 'help': 'the great bar function', 'exposed': True}
    Found bar {'msg': 'overriding bar function', 'hello': False, 'exposed': True}
    Found bar {'msg': 'adding a bar function', 'exposed': True}
    --------------------
    showing exposed methods
    Foo: {'foo': <function foo at 0x7f7da3abb398>}
    Bar: {'bar': <function bar at 0x7f7da3abb140>}
    Buzz: {'bar': <function bar at 0x7f7da3abb0c8>}
    Fizz: {'foo': <function foo at 0x7f7da3abb398>, 'bar': <function bar at 0x7f7da3abb488>}
    --------------------
    examine bar functions
    Bar.bar: {'hide': True, 'help': 'the great bar function', 'exposed': True}
    Buzz.bar: {'msg': 'overriding bar function', 'hello': False, 'exposed': True}
    Fizz.bar: {'msg': 'adding a bar function', 'exposed': True}
    
    找到foo{'inside':'\uuuu meta\uuuu func属性','any':'parameter will go','exposed':True}
    找到条{'hide':True,'help':'the great bar function','exposed':True}
    找到条{'msg':'overriding bar function','hello':False',exposed':True}
    找到条{'msg':'adding a bar function','exposed':True}
    --------------------
    展示暴露的方法
    Foo:{'Foo':}
    酒吧:{'Bar':}
    巴斯:{'bar':}
    嘶嘶声:{'foo':,'bar':}
    --------------------
    检查条函数
    Bar.Bar:{'hide':True,'help':'the great Bar function','exposed':True}
    Buzz.bar:{'msg':'overriding bar function','hello':False,'exposed':True}
    Fizz.bar:{'msg':'adding a bar function','exposed':True}
    
    请注意,在本例中:

  • 我们可以用任意参数注释任何函数
  • 每个类都有自己的公开方法
  • 我们也可以继承公开的方法
  • 方法可以在公开功能更新时重写

  • 希望这有助于

    函数在decorator代码运行时不知道它是否是定义点上的方法。只有通过类/实例标识符访问它时,它才可能知道它的类/实例。为了克服此限制,您可以通过描述符对象装饰,将实际装饰代码延迟到访问/调用时间:

    class decorated(object):
        def __init__(self, func, type_=None):
            self.func = func
            self.type = type_
    
        def __get__(self, obj, type_=None):
            func = self.func.__get__(obj, type_)
            print('accessed %s.%s' % (type_.__name__, func.__name__))
            return self.__class__(func, type_)
    
        def __call__(self, *args, **kwargs):
            name = '%s.%s' % (self.type.__name__, self.func.__name__)
            print('called %s with args=%s kwargs=%s' % (name, args, kwargs))
            return self.func(*args, **kwargs)
    
    这允许您修饰单个(静态|类)方法:

    现在您可以使用装饰器代码进行内省

    >>> Foo.foo
    accessed Foo.foo
    >>> Foo.bar
    accessed Foo.bar
    >>> Foo.baz
    accessed Foo.baz
    >>> Bar.foo
    accessed Bar.foo
    >>> Bar.bar
    accessed Bar.bar
    >>> Bar.baz
    accessed Bar.baz
    
    …对于更改函数行为:

    >>> Foo().foo(1, 2)
    accessed Foo.foo
    called Foo.foo with args=(1, 2) kwargs={}
    >>> Foo.bar(1, b='bcd')
    accessed Foo.bar
    called Foo.bar with args=(1,) kwargs={'b': 'bcd'}
    >>> Bar.baz(a='abc', b='bcd')
    accessed Bar.baz
    called Bar.baz with args=() kwargs={'a': 'abc', 'b': 'bcd'}
    

    由于Python3.6,您可以使用它以非常简单的方式实现这一点。文档声明
    \uuuu set\u name\uuuu
    是“在创建所属类所有者时调用的”。 这里有一个例子
    class Foo(object):
        @decorated
        def foo(self, a, b):
            pass
    
        @decorated
        @staticmethod
        def bar(a, b):
            pass
    
        @decorated
        @classmethod
        def baz(cls, a, b):
            pass
    
    class Bar(Foo):
        pass
    
    >>> Foo.foo
    accessed Foo.foo
    >>> Foo.bar
    accessed Foo.bar
    >>> Foo.baz
    accessed Foo.baz
    >>> Bar.foo
    accessed Bar.foo
    >>> Bar.bar
    accessed Bar.bar
    >>> Bar.baz
    accessed Bar.baz
    
    >>> Foo().foo(1, 2)
    accessed Foo.foo
    called Foo.foo with args=(1, 2) kwargs={}
    >>> Foo.bar(1, b='bcd')
    accessed Foo.bar
    called Foo.bar with args=(1,) kwargs={'b': 'bcd'}
    >>> Bar.baz(a='abc', b='bcd')
    accessed Bar.baz
    called Bar.baz with args=() kwargs={'a': 'abc', 'b': 'bcd'}
    
    def field(fn):
        """Mark the method as an extra field"""
        fn.is_field = True
        return fn
    
    class MetaEndpoint(type):
        def __new__(cls, name, bases, attrs):
            fields = {}
            for k, v in attrs.items():
                if inspect.isfunction(v) and getattr(k, "is_field", False):
                    fields[k] = v
            for base in bases:
                if hasattr(base, "_fields"):
                    fields.update(base._fields)
            attrs["_fields"] = fields
    
            return type.__new__(cls, name, bases, attrs)
    
    class EndPoint(metaclass=MetaEndpoint):
        pass
    
    
    # Usage
    
    class MyEndPoint(EndPoint):
        @field
        def foo(self):
            return "bar"
    
    e = MyEndPoint()
    e._fields  # {"foo": ...}
    
    import datetime as dt
    import functools
    
    def dec(arg1):
        class Timed(object):
            local_arg = arg1
            def __init__(self, f):
                functools.update_wrapper(self, f)
                self.func = f
    
            def __set_name__(self, owner, name):
                # doing something fancy with owner and name
                print('owner type', owner.my_type())
                print('my arg', self.local_arg)
    
            def __call__(self, *args, **kwargs):
                start = dt.datetime.now()
                ret = self.func(*args, **kwargs)
                time = dt.datetime.now() - start
                ret["time"] = time
                return ret
            
            def __get__(self, instance, owner):
                from functools import partial
                return partial(self.__call__, instance)
        return Timed
    
    class Test(object):
        def __init__(self):
            super(Test, self).__init__()
    
        @classmethod
        def my_type(cls):
            return 'owner'
    
        @dec(arg1='a')
        def decorated(self, *args, **kwargs):
            print(self)
            print(args)
            print(kwargs)
            return dict()
    
        def call_deco(self):
            self.decorated("Hello", world="World")
    
    @dec(arg1='a function')
    def another(*args, **kwargs):
        print(args)
        print(kwargs)
        return dict()
    
    if __name__ == "__main__":
        t = Test()
        ret = t.call_deco()
        another('Ni hao', world="shi jie")