用于Python装饰器的装饰函数

5

从2.4版本开始(对于类是2.6),Python允许您使用另一个函数来修饰函数:

def d(func): return func

@d
def test(first): pass

这是一种方便的语法糖。你可以使用装饰器完成各种有趣的事情,而不会弄乱代码。然而,如果你想找到被装饰的原始函数,你必须通过一些麻烦的方式(比如Cls.method.__func__.__closure__[0].cell_contents或者更糟)。
我发现自己希望有一个更好的方法,并发现python-dev上有一些讨论,关于在由装饰器返回的[新]函数中添加一个名为__decorated__的变量。然而,似乎没有任何进展。
作为一个爱冒险的人,我已经有了大约4年的Python编程经验,我想看看在Python编译器源码中实现__decorated__会怎样。老实说,我从未深入了解过C语言,所以我的前几个小时都是在试图理解底层C代码的工作原理。那么,首先,了解我需要改变/添加哪些内容以实现__decorator__的最佳资源是什么? 其次,如果装饰器返回一个新函数,那么__decorated__将只返回原始的已装饰函数。然而,如果装饰器返回原始函数,那么应该发生什么呢?我想到了三个选项(第三个是我最喜欢的):
  1. 不添加 __decorator__
  2. 添加__decorator__但将其设置为None。
  3. 添加__decorator__并将其设置为原始函数。
所以如果真的发生了这种情况,你认为哪个选项最好?
更新:
有人提醒我了我忽略的一种情况。当装饰器既不返回原始函数也不返回包裹原始函数的函数时会发生什么?此时没有任何东西保留对原始函数的引用,它将被垃圾回收。(感谢Oddthinking!)
在这种情况下,我认为我仍然会选择第三个选项。装饰器返回的对象将获得一个名为__decorated__的名称,该名称引用原始函数。这意味着它不会被垃圾回收。
对我来说,从类定义中返回的函数因为你装饰它而完全消失似乎很奇怪。在我看来,这更加需要为每个装饰器应用一个__decorated__属性。然而,更可能的是我的直觉有误,当前的行为是大多数人所期望的。有任何想法吗?
附言:这是我先前更一般的问题的延伸。我还通过单独发布了第一部分的更多信息。

所以你正在考虑下一步,即装饰函数如何知道它被装饰了?https://dev59.com/NHA75IYBdhLWcg3wxsNn :) - mykhal
这个在@property中怎么用?也就是说,装饰器根本不需要返回函数。 - habnabit
@Aaron,我想你会将__decorated__添加到返回的任何内容中,无论是函数还是其他。 - Eric Snow
请注意,从Python 3.2开始,functools.wraps会自动添加一个指向原始函数的__wrapped__属性(http://docs.python.org/dev/library/functools#functools.update_wrapper)。 - ncoghlan
2个回答

2

不管是否这是一个好主意,独立于任何讨论之外,我会选择选项#3,因为它最一致:它总是显示函数已被修饰的属性存在,并且访问属性值始终返回一个函数,所以您不必将其与 None 进行测试。

您还应该考虑这一点:您会提出关于手动装饰的建议吗?例如:

def test(first): pass
test = d(test)

关于问题的第一部分,我没有仔细查看Python解释器源代码,所以我无法指向任何特别有用的东西。


我认为你需要手动添加__decorated__属性到新函数中,这会更加丑陋。 - Eric Snow

1

我觉得可以自由地使用一些单下划线的“私有”属性,比如_mydecor

可能的多装饰器解决方案:

def decorate(decoration): 
    def do_decor(func): 
        if hasattr(func, '_mydecor'): 
            func._mydecor.add(decoration) 
        else: 
            func._mydecor = set([decoration]) 
        return func 
    return do_decor 

def isDecorated(func, decoration): 
    return (decoration in getattr(func, '_mydecor', set())) 

@decorate('red') 
@decorate('green') 
def orangefunc(): pass 

print isDecorated(orangefunc, 'green') # -> True 
print isDecorated(orangefunc, 'blue')  # -> False 

如果您控制装饰器,那么这将有效。我想当您无法控制它时,您可以包装装饰器来完成此操作。 - Eric Snow

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接