当你装饰一个方法时,它还没有绑定到类,因此还没有im_class
属性。我正在寻找一种方法来获取装饰器内部类的信息。我试过这个:
import types
def decorator(method):
def set_signal(self, name, value):
print name
if name == 'im_class':
print "I got the class"
method.__setattr__ = types.MethodType(set_signal, method)
return method
class Test(object):
@decorator
def bar(self, foo):
print foo
但它没有打印任何东西。
我可以想象这样做:
class Test(object):
@decorator(klass=Test)
def bar(self, foo):
print foo
但如果我能避免它,它就会成为我的一天。
答案 0 :(得分:3)
__setattr__
仅在明确object.attribute =
作业时调用;构建类不使用属性赋值,而是构建字典(Test.__dict__
)。
要访问该课程,您有几个不同的选择:
改为使用类装饰器;在构建它之后它将被传递给完成的类,你可以通过在类中替换它们(装饰)来装饰该类的各个方法。您可以使用函数装饰器和类装饰器的组合来标记要装饰的方法:
def methoddecoratormarker(func):
func._decorate_me = True
return func
def realmethoddecorator(func):
# do something with func.
# Note: it is still an unbound function here, not a method!
return func
def classdecorator(klass):
for name, item in klass.__dict__.iteritems():
if getattr(item, '_decorate_me', False):
klass.__dict__[name] = realmethoddecorator(item)
当然,您可以使用元类而不是类装饰器来实现相同目的。
作弊,并使用sys._getframe()
从调用框架中检索类:
import sys
def methoddecorator(func):
callingframe = sys._getframe(1)
classname = callingframe.f_code.co_name
请注意,您可以检索的只是该类的名称;班级本身目前仍在建设中。您可以将项目添加到callingframe.f_locals
(映射),它们将成为新类对象的一部分。
调用方法时访问self
。 self
毕竟是对实例的引用,self.__class__
至少是函数定义的原始类的子类。
答案 1 :(得分:1)
我的严格答案是:这是不可能的,因为在执行装饰器时类仍然不存在。
答案越长,取决于您的确切要求。正如我所写,如果它尚不存在,则无法访问该类。一种解决方案是,将装饰的方法标记为稍后“转换”。然后使用元类或类装饰器在创建类之后应用修改。
另一种选择涉及一些魔法。在implements
中查找zope.interfaces
方法的实现。它可以访问刚刚解析过的有关类的信息。不知道它对你的用例是否足够。
答案 2 :(得分:0)
您可能需要查看descriptors。它们允许您实现访问属性时使用的__get__
,并且可以根据对象及其类型返回不同的内容。
答案 3 :(得分:0)
使用方法装饰器为有趣的方法添加一些标记属性,并使用迭代方法的元类,查找标记属性,并执行逻辑。元类代码在创建类时运行,因此它引用了新创建的类。
class MyMeta(object):
def __new__(...):
...
cls = ...
... iterate over dir(cls), find methods having .is_decorated, act on them
return cls
def decorator(f):
f.is_decorated = True
return f
class MyBase(object):
__metaclass__ = MyMeta
class MyClass(MyBase):
@decorator
def bar(self, foo):
print foo
如果您担心MyClass
的程序员忘记使用MyBase
,您可以通过对调用程序堆栈帧的全局变量进行操作来强制设置decorator
中的元类(sys._getframe()
{1}})。