3

我有一个 python 类,例如:

class Book(models.Model):
    enabled     = models.BooleanField(default=False)
    full_title  = models.CharField(max_length=256)
    alias       = models.CharField(max_length=64)
    author      = models.CharField(max_length=64)
    status      = models.CharField(max_length=64)
    @serializable
    def pretty_status(self):
        return [b for a, b in BOOK_STATUS_CHOICES if a == self.status][0]

方法pretty_status@serializable 修饰

发现类中具有某种装饰的方法的最简单和最有效的方法是什么?(在上面的例子中给出:pretty_status)。

编辑: 还请注意,有问题的装饰器是自定义/可修改的。

4

3 回答 3

2

一般来说,你不能。装饰器只是应用可调用对象的语法糖。在您的情况下,装饰器语法转换为:

def pretty_status(self):
    return [b for a, b in BOOK_STATUS_CHOICES if a == self.status][0]
pretty_status = serializable(pretty_status)

也就是说,pretty_status被任何serializable()返回所取代。它返回的可能是任何东西。

现在,如果serializable返回的内容本身已被修饰,并且您使用的是 Python 3.2 或更高版本,那么您可以查看新方法上functools.wraps()是否有.__wrapped__属性;.pretty_status它是对原始包装函数的引用。

在早期版本的 Python 上,您也可以轻松地自己执行此操作:

def serializable(func):
    def wrapper(*args, **kw):
        # ...

    wrapper.__wrapped__ = func

    return wrapper

您可以向该包装函数添加任意数量的属性,包括您自己选择的自定义属性:

def serializable(func):
    def wrapper(*args, **kw):
        # ...

    wrapper._serializable = True

    return wrapper

然后测试该属性:

if getattr(method, '_serializable', False):
    print "Method decorated with the @serializable decorator"

您可以做的最后一件事是测试该包装函数;它会有一个.__name__你可以测试的属性。该名称可能不是唯一的,但它是一个开始。

在上面的示例装饰器中,调用了包装函数wrapper,因此pretty_status.__name__ == 'wrapper'为 True。

于 2013-06-26T13:16:25.067 回答
2

如果您无法控制装饰器的作用,那么通常您无法识别装饰方法。

但是,由于您可以修改serializable,因此您可以向包装函数添加一个属性,以后可以使用该属性来识别序列化方法:

import inspect
def serializable(func):
    def wrapper(self):
        pass
    wrapper.serialized = True
    return wrapper

class Book:
    @serializable
    def pretty_status(self):
        pass
    def foo(self):
        pass


for name, member in inspect.getmembers(Book, inspect.ismethod):
    if getattr(member, 'serialized', False):
        print(name, member)

产量

('pretty_status', <unbound method Book.wrapper>)
于 2013-06-26T13:19:39.603 回答
0

您无法直接发现它们,但您可以用一些标志标记装饰方法。

import functools
def serializable(func):
    functools.wraps(func)
    def wrapper(*args, **kw):
        # ...

    wrapper._serializable = True
    return wrapper

然后您可以制作元类,例如分析_serializable属性的存在或不存在。

或者您可以在装饰器中收集所有包装的方法

import functools
DECORATED = {}
def serializable(func):
    functools.wraps(func)
    def wrapper(*args, **kw):
        # ...

    DECORATED[func.__name__] = wrapper
    return wrapper
于 2013-06-26T13:19:02.377 回答