Python property decorators and set functions
I have some questions regarding the following code:
1 class Test(object):
2 def __init__(self):
3 print "Object instance created."
4 self._x = raw_input("Initial value of x = ")
5 print "Initial value of x set."
6
7 def Property(func):
8 return property(**func())
9
10 @Property
11 def x():
12 def fget(self):
13 print 'Getting x'
14 return self._x
15 def fset(self, val):
16 print 'Setting x'
17 self._x = val
18 def fdel(self):
19 print 'Deleting x'
20 del self._x
21 doc = "A test case"
22 return locals()
- Why is a function needed
Property()
? - Why can't I just
return locals()
and then use@property
as a decorator directly?
When I do this, I get the error: x takes no arguments, one of them (presumably "i"). I know python has an option @x.setter
, however I am forced to use 2.4 regularly so this is not an option for me. Even then @x.setter
, it still seems less elegant than defining just one block.
Is there a way to define it all in one block with @property
?
source to share
You cannot use property
as a decorator directly for the code you posted as it is not intended to be used in this way and it will not work.
If used as a decorator, property
converts the function to a getter; if used as a function, you can pass to receiver, setter, deleter and doc.
locals()
It returns all locales, so you will have a dictionary with fget
, fset
, fdel
, doc
, property
and __init__
that will lead to the removal property
because it was passed too many arguments.
Personally, I like the style @x.setter
and @x.deleter
since I have no extra function names in the class namespace.
If you need to use 2.4 regularly, just roll your own (or steal the latest from 2.6 like me;):
class property(object):
"2.6 properties for 2.5-"
def __init__(self, fget=None, fset=None, fdel=None, doc=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
self.__doc__ = doc or fget.__doc__
def __call__(self, func):
self.fget = func
if not self.__doc__:
self.__doc__ = fget.__doc__
def __get__(self, obj, objtype=None):
if obj is None:
return self
if self.fget is None:
raise AttributeError("unreadable attribute")
return self.fget(obj)
def __set__(self, obj, value):
if self.fset is None:
raise AttributeError("can't set attribute")
self.fset(obj, value)
def __delete__(self, obj):
if self.fdel is None:
raise AttributeError("can't delete attribute")
self.fdel(obj)
def setter(self, func):
self.fset = func
return self
def deleter(self, func):
self.fdel = func
return self
source to share
You can do it all in one block: without using @property
, defining and instantiating a class with methods __get__()
, __set__()
and __delete__()
. See Implementing Descriptors for more details :
class Test(object):
def __init__(self):
print "Object instance created."
self._x = raw_input("Initial value of x = ")
print "Initial value of x set."
class x(object):
def __get__(self, instance, owner):
print 'Getting x'
return instance._x
def __set__(self, instance, value):
print 'Setting x'
instance._x = value
def __delete__(self, instance):
print 'Deleting x'
del instance._x
__doc__ = "A test case"
x = x()
property()
is a shortcut for writing the above, and a method property()
in your example class is a shortcut for writing functions separately and passing them to property()
; instead, you write a function that defines the functions and then returns them where they are passed to property()
.
The reason you can't use @property
is because decorators decorate a single object. So you need a container, like a class, and so you can just write the handle directly at this point.
source to share