影响sys._getframe的装饰器(1)

时间:2014-03-20 18:05:11

标签: python python-2.x python-decorators

a.py

import d

d.funcme('blah')

d.py

import sys
import Errors
def argcheck(in_=(), out=(type(None),)):
    def _argcheck(function):
        # do something here
        def __argcheck(*args, **kw):
            print '+++++++++ checking types before calling the func'
            # do something here
            res = function(*args, **kw)
            return res
        return __argcheck
    return _argcheck

@argcheck((str))       <-----
def funcme(name):
    try:
        f = sys._getframe(1)
    except ValueError, err:
        raise Errors.UserError(err)     # stack too deep
        filename, lineno = f.f_globals['__name__'], f.f_lineno
    print filename, lineno

没有argcheck装饰器的OUTPUT(注释掉@argcheck((str))):

$ python a.py  
__main__ 3  

使用argcheck装饰器输出:

$ python a.py  
+++++++++ checking types before calling the func  
defines 9  

问题:

  1. 装饰师做了什么,以便改变_getframe的值?

  2. 如何保存信息以便捕获原始信息,即__main__ 3而不是定义9?

2 个答案:

答案 0 :(得分:2)

问题是你的funcme()函数假设它是直接调用而不是通过其他东西间接调用 - 比如装饰器。这可以通过更改其调用顺序并添加一个带有默认值的附加depth关键字参数来修复,该默认值将传递给_sys._getframe()。有了这个脚手架,装饰器就可以覆盖默认值。无论是否已应用装饰器,以下内容都将打印相同的内容:

 1 import sys
 2 import Errors
 3 def argcheck(in_=(), out=(type(None),)):
 4     def _argcheck(function):
 5         # do something here
 6         def __argcheck(*args, **kw):
 7             print '+++++++++ checking types before calling the func'
 8             # do something here
 9             res = function(*args, depth=2, **kw)  # override default depth
10             return res
11         return __argcheck
12     return _argcheck
13
14 @argcheck((str))
15 def funcme(name, depth=1):  # added keyword arg with default value
16     try:
17         f = sys._getframe(depth)   # explicitly pass stack depth wanted
18     except ValueError, err:
19         raise Errors.UserError(err)     # stack too deep
20
21     filename, lineno = f.f_globals['__name__'], f.f_lineno
22     print filename, lineno

答案 1 :(得分:1)

装饰者基本上是语法糖。这样:

@argcheck((str))
def funcme(name):

与此相同:

funcme = argcheck(str)(funcme)

现在您可以看到装饰器更改调用堆栈的原因。

我不确定如何在任意情况下解决这个问题,但如果您事先知道装饰器的某些内容,您可以补偿代码。您也可以查看functools.wraps,也许这会提供一些可能有帮助的线索。