嗨我有类似下面的东西.基本上我需要从定义中的实例方法使用的装饰器访问实例方法的类.
def decorator(view):
# do something that requires view's class
print view.im_class
return view
class ModelA(object):
@decorator
def a_method(self):
# do some stuff
pass
Run Code Online (Sandbox Code Playgroud)
代码原样给出
AttributeError: 'function' object has no attribute 'im_class'
我发现类似的问题/答案 - Python装饰器让函数忘记它属于一个类和Python装饰器中的Get类 - 但这些依赖于一种解决方法,它通过抢夺第一个参数在运行时抓取实例.在我的情况下,我将基于从其类中收集的信息调用该方法,因此我不能等待来电.
谢谢.
Dav*_*rby 63
如果您使用的是Python 2.6或更高版本,则可以使用类装饰器,可能是这样的(警告:未经测试的代码).
def class_decorator(cls):
for name, method in cls.__dict__.iteritems():
if hasattr(method, "use_class"):
# do something with the method and class
print name, cls
return cls
def method_decorator(view):
# mark the method as something that requires view's class
view.use_class = True
return view
@class_decorator
class ModelA(object):
@method_decorator
def a_method(self):
# do some stuff
pass
Run Code Online (Sandbox Code Playgroud)
方法装饰器通过添加"use_class"属性将方法标记为感兴趣的方法 - 函数和方法也是对象,因此您可以将附加元数据附加到它们.
在创建类之后,类装饰器将遍历所有方法,并对已标记的方法执行任何操作.
如果您希望所有方法都受到影响,那么您可以省略方法装饰器并使用类装饰器.
tyr*_*ion 25
从 python 3.6 开始,你可以用object.__set_name__一种非常简单的方式来完成这个。该文档指出__set_name__“在创建拥有类所有者时调用”。下面是一个例子:
class class_decorator:
def __init__(self, fn):
self.fn = fn
def __set_name__(self, owner, name):
# do something with owner, i.e.
print(f"decorating {self.fn} and using {owner}")
self.fn.class_name = owner.__name__
# then replace ourself with the original method
setattr(owner, name, self.fn)
Run Code Online (Sandbox Code Playgroud)
请注意,它在类创建时被调用:
>>> class A:
... @class_decorator
... def hello(self, x=42):
... return x
...
decorating <function A.hello at 0x7f9bedf66bf8> and using <class '__main__.A'>
>>> A.hello
<function __main__.A.hello(self, x=42)>
>>> A.hello.class_name
'A'
>>> a = A()
>>> a.hello()
42
Run Code Online (Sandbox Code Playgroud)
如果您想了解更多关于类是如何创建的,特别是在什么时候__set_name__被调用,您可以参考关于“创建类对象”的文档。
Mar*_*ser 15
正如其他人所指出的那样,在调用装饰器时尚未创建类.但是,可以使用装饰器参数注释函数对象,然后在元类的__new__方法中重新装饰该函数.你需要直接访问函数的__dict__属性,至少对我来说,func.foo = 1导致了AttributeError.
问题是,当调用装饰器时,该类还不存在。尝试这个:
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()
Run Code Online (Sandbox Code Playgroud)
该程序将输出:
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
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,您将必须找出一种不同的方法来完成您想要的事情。
小智 6
正如马克所说:
此代码显示了使用自动后处理可能如何工作:
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__)
Run Code Online (Sandbox Code Playgroud)
输出结果:
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}
Run Code Online (Sandbox Code Playgroud)
请注意,在此示例中:
希望这可以帮助
正如 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()
Run Code Online (Sandbox Code Playgroud)
印刷:
before instantiating Bar()
calling b.foo()
decorating function in Bar
in foo
Run Code Online (Sandbox Code Playgroud)