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")