A better python property decorator
I've inherited some python code that contains a rather cryptic decorator. This decorator sets properties in classes all over the project. The problem is that this I have traced my debugging problems to this decorator. Seems it "fubars" all debuggers I've tried and trying to speed up the code with psyco breaks everthing. (Seems psyco and this decorator dont play nice). I think it would be best to change it.
def Property(function):
"""A开发者_如何学Gollow 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)
Used like so:
class A(object):
@Property
def prop():
def fget(self):
return self.__prop
def fset(self, value):
self.__prop = value
... ect
The errors I get say the problems are because of sys.settrace. (Perhaps this is abuse of settrace ?)
My question: Is the same decorator achievable without sys.settrace. If not I'm in for some heavy rewrites.
The same thing? No. You can't do what that decorator does without magic like sys.settrace. (It technically doesn't have to be sys.settrace, but using something else -- like bytecode rewriting -- wouldn't be an improvement.) You can make it a lot simpler by doing, for example:
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
In Python 2.6 and later you can use a slightly different decorator, though:
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)
And you would use it like so:
class Foo(object):
@Property
class myprop(object):
"Property docstring"
def fget(self):
return 'fget'
def fset(self, x):
pass
def fdel(self):
pass
However, a more idiomatic way of doing this in Python 2.6 and later is like so:
class Foo(object):
@property
def myprop(self):
"Property docstring"
return 'fget'
@myprop.setter
def myprop(self, x):
pass
@myprop.deleter
def myprop(self):
pass
It might be possible; it appears that this abuse of settrace
is being used to catch the decorated function just before it returns for the purpose of pilfering it's local variables...
Unfortunately, the main approaches I can think of at the moment involve hacking the bytecode :-(.
However, you could replace it with one that requires every so-decorated function to call another special function rather than simply returning, or you could modify it to call sys.gettrace
rather than throwing out whatever trace function might be active, and restore the same trace function afterward.
精彩评论