更好的 Python 属性装饰器

3 投票
2 回答
5592 浏览
提问于 2025-04-15 21:26

我接手了一些Python代码,其中有一个看起来很复杂的装饰器。这个装饰器在整个项目中设置了很多类的属性。问题是,我发现我的调试问题都和这个装饰器有关。似乎它会搞乱我尝试过的所有调试工具,而用psyco来加速代码又会让一切都崩溃。(看起来psyco和这个装饰器不太合得来)。我觉得最好还是改一下这个装饰器。

def Property(function):
    """Allow readable properties"""
    keys = 'fget', 'fset', 'fdel'
    func_locals = {'doc':function.__doc__}
    def probeFunc(frame, event, arg):
        if event == 'return':
            locals = frame.f_locals
            func_locals.update(dict((k,locals.get(k)) for k in keys))
            sys.settrace(None)
        return probeFunc
    sys.settrace(probeFunc)
    function()
    return property(**func_locals)

用法如下:

class A(object):
    @Property
    def prop():
        def fget(self):
            return self.__prop
        def fset(self, value):
            self.__prop = value
    ... ect

我收到的错误信息显示问题出在sys.settrace上。(也许这是对settrace的滥用?)

我的问题是:有没有办法实现同样的装饰器而不使用sys.settrace?如果不行的话,我就得重写很多代码了。

2 个回答

2

可能是有可能的;看起来有人在滥用settrace这个功能,目的是在被装饰的函数返回之前抓住它,以便偷取它的局部变量……

不幸的是,我现在能想到的主要方法都是要对字节码进行一些黑客操作 :-(。

不过,你可以换成一个需要每个被装饰的函数调用另一个特殊函数,而不是简单地返回,或者你可以修改它,让它调用sys.gettrace,而不是直接丢掉可能正在使用的任何跟踪函数,然后再恢复原来的跟踪函数。

8

这两个东西是一样的吗?不是的。你不能像那个装饰器那样工作,除非用一些特别的手段,比如 sys.settrace。其实不一定非得用 sys.settrace,换成其他方法,比如字节码重写,也不会有什么好处。你可以通过更简单的方法来实现,比如:

def Property(f):  
    fget, fset, fdel = f()
    fdoc = f.__doc__
    return property(fget, fset, fdel, fdoc)

class Foo(object):
    @Property
    def myprop():
        "Property docstring"
        def fget(self):  
            return 'fget' 
        def fset(self, x):
            pass
        def fdel(self):
            pass
        return fget, fset, fdel

在 Python 2.6 及之后的版本中,你可以使用一个稍微不同的装饰器:

def Property(cls):
    fget = cls.__dict__.get('fget')
    fset = cls.__dict__.get('fset')
    fdel = cls.__dict__.get('fdel')
    fdoc = cls.__doc__
    return property(fget, fset, fdel, fdoc)

你可以这样使用它:

class Foo(object):
    @Property
    class myprop(object):
        "Property docstring"
        def fget(self):
            return 'fget'
        def fset(self, x):
            pass
        def fdel(self):
            pass

不过,在 Python 2.6 及之后的版本中,更常见的做法是这样:

class Foo(object):
    @property
    def myprop(self):
        "Property docstring"
        return 'fget'
    @myprop.setter
    def myprop(self, x):
            pass
    @myprop.deleter
    def myprop(self):
            pass

撰写回答