Declaring setters with getters

I've come up with a relatively unobtrusive pattern for defining setters. Given the following definition: def propset(prop): assert isinstance(prop, property) def helper(func): return property(prop.__get__, func, func, prop.__doc__) return helper we can declare getters and setters as follows: class C(object): _encoding = None @property def encoding(self): return self._encoding @propset(encoding) def encoding(self, value=None): if value is not None: unicode("0", value) # Test it self._encoding = value c = C() print(c.encoding) c.encoding = "ascii" print(c.encoding) try: c.encoding = "invalid" # Fails except: pass print(c.encoding) I'd like to make this a standard built-in, in the hope the debate on how to declare settable properties. I'd also like to change property so that the doc string defaults to the doc string of the getter. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

Guido> I've come up with a relatively unobtrusive pattern for defining Guido> setters. Given the following definition: ... I'm a pretty naive user of properties, so can you explain to me how what you propose is better than class C(object): def __init__(self): self._encoding = None def get_encoding(self): return self._encoding def set_encoding(self, value): if value is not None: unicode("0", value) # Test it self._encoding = value encoding = property(get_encoding, set_encoding) ? Guido> I'd also like to change property so that the doc string defaults Guido> to the doc string of the getter. How about defaulting it to the first argument to property() which has a doc string? Skip

On 10/31/07, skip@pobox.com <skip@pobox.com> wrote:
Guido> I've come up with a relatively unobtrusive pattern for defining Guido> setters. Given the following definition:
...
I'm a pretty naive user of properties, so can you explain to me how what you propose is better than
class C(object): def __init__(self): self._encoding = None
def get_encoding(self): return self._encoding
def set_encoding(self, value): if value is not None: unicode("0", value) # Test it self._encoding = value encoding = property(get_encoding, set_encoding)
?
Mostly so it's easy to add a setter to a property that originally had only a getter. For read-only properties, using @property def name(self): return <whatever> is preferred over def get_name(self): return <whatever> name = property(get_name)
Guido> I'd also like to change property so that the doc string defaults Guido> to the doc string of the getter.
How about defaulting it to the first argument to property() which has a doc string?
I considered that briefly, but decided against it -- it could lead to odd results if the getter has no docstring and the setter's docstring is about setting only. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

Guido van Rossum schrieb:
I'd like to make this a standard built-in, in the hope the debate on how to declare settable properties.
+1. I'm waiting for the obligatory cry for @propdel though :)
I'd also like to change property so that the doc string defaults to the doc string of the getter.
I may be misunderstanding, but isn't that what patch #1434038 already implemented in 2.5? Georg -- Thus spake the Lord: Thou shalt indent with four spaces. No more, no less. Four shall be the number of spaces thou shalt indent, and the number of thy indenting shall be four. Eight shalt thou not indent, nor either indent thou two, excepting that thou then proceed to four. Tabs are right out.

On 10/31/07, Georg Brandl <g.brandl@gmx.net> wrote:
Guido van Rossum schrieb:
I'd like to make this a standard built-in, in the hope the debate on how to declare settable properties.
+1. I'm waiting for the obligatory cry for @propdel though :)
propdel: just say no.
I'd also like to change property so that the doc string defaults to the doc string of the getter.
I may be misunderstanding, but isn't that what patch #1434038 already implemented in 2.5?
D'oh, you're right. I tested this with 2.4 (which is the default Python at Google). +1 on this proposal then. :-) -- --Guido van Rossum (home page: http://www.python.org/~guido/)

Guido van Rossum wrote:
I'd like to make this a standard built-in, in the hope the debate on how to declare settable properties.
+0.75 It's still not as good as the C# way to create a property but it's so simple and obvious that it's ingenious again. Every other implementation would either require support from the parser or some metaclass magic. Your KISS approach rocks! :) Christian

Guido van Rossum wrote:
@property def encoding(self): return self._encoding
@propset(encoding) def encoding(self, value=None): if value is not None: unicode("0", value) # Test it self._encoding = value
That's reasonably nice, although I'm not sure about the name "propset" -- it sounds like a repair kit for an aircraft. Maybe something like "setproperty" would be better. But there's something that bothers me about the whole propery mechanism -- there's no straightforward way for a property accessor to call the inherited version from a base class. Wrapping property accessors up in decorators makes this worse. At least when the accessors are declared as separate functions you have a chance of grabbing an inherited one and calling it. But if they're buried inside property descriptors with no other reference to them, it's a lot harder to get hold of one. -- Greg

On 10/31/07, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Guido van Rossum wrote:
@property def encoding(self): return self._encoding
@propset(encoding) def encoding(self, value=None): if value is not None: unicode("0", value) # Test it self._encoding = value
That's reasonably nice, although I'm not sure about the name "propset" -- it sounds like a repair kit for an aircraft. Maybe something like "setproperty" would be better.
I'm open for a naming contest.
But there's something that bothers me about the whole propery mechanism -- there's no straightforward way for a property accessor to call the inherited version from a base class.
Isn't there? You can just use super() ISTR, though I haven't had the need myself and don't recall the exact syntax. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

At 12:06 PM 11/1/2007 +1300, Greg Ewing wrote:
Guido van Rossum wrote:
Isn't there? You can just use super() ISTR,
That may work, now that I come to think about it.
However, super() is not a complete solution, because it doesn't let you choose which inherited method to call in a multiple inheritance situation.
Er, TheRightBase.propname.fget(self)?

On 10/31/07, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Guido van Rossum wrote:
Isn't there? You can just use super() ISTR,
That may work, now that I come to think about it.
However, super() is not a complete solution, because it doesn't let you choose which inherited method to call in a multiple inheritance situation.
Yes, that's the point of super(). if you want more control, you can just get the property object out of the class you want -- super() doesn't have supernatural powers, all it does is pick the class for you. :-) -- --Guido van Rossum (home page: http://www.python.org/~guido/)

Guido van Rossum wrote:
if you want more control, you can just get the property object out of the class you want
You're quite right. I was thinking that getting the property would trigger the access machinery, but of course you're getting it from the class rather than the instance, so that doesn't happen. -- Greg

On Thu, Nov 01, 2007, Greg Ewing wrote:
But there's something that bothers me about the whole propery mechanism -- there's no straightforward way for a property accessor to call the inherited version from a base class.
Wrapping property accessors up in decorators makes this worse. At least when the accessors are declared as separate functions you have a chance of grabbing an inherited one and calling it. But if they're buried inside property descriptors with no other reference to them, it's a lot harder to get hold of one.
That's why the property delegates to a normal method. (Kudos to Martelli for the idea.) -- Aahz (aahz@pythoncraft.com) <*> http://www.pythoncraft.com/ "Typing is cheap. Thinking is expensive." --Roy Smith

To follow up, I now have a patch. It's pretty straightforward. This implements the kind of syntax that I believe won over most folks in the end: @property def foo(self): ... @foo.setter def foo(self, value=None): ... There are also .getter and .deleter descriptors. This includes the hack that if you specify a setter but no deleter, the setter is called without a value argument when attempting to delete something. If the setter isn't ready for this, a TypeError will be raised, pretty much just as if no deleter was provided (just with a somewhat worse error message :-). I intend to check this into 2.6 and 3.0 unless there is a huge cry of dismay. Docs will be left to volunteers as always. --Guido On Oct 31, 2007 9:08 AM, Guido van Rossum <guido@python.org> wrote:
I've come up with a relatively unobtrusive pattern for defining setters. Given the following definition:
def propset(prop): assert isinstance(prop, property) def helper(func): return property(prop.__get__, func, func, prop.__doc__) return helper
we can declare getters and setters as follows:
class C(object):
_encoding = None
@property def encoding(self): return self._encoding
@propset(encoding) def encoding(self, value=None): if value is not None: unicode("0", value) # Test it self._encoding = value
c = C() print(c.encoding) c.encoding = "ascii" print(c.encoding) try: c.encoding = "invalid" # Fails except: pass print(c.encoding)
I'd like to make this a standard built-in, in the hope the debate on how to declare settable properties.
I'd also like to change property so that the doc string defaults to the doc string of the getter.
-- --Guido van Rossum (home page: http://www.python.org/~guido/)
-- --Guido van Rossum (home page: http://www.python.org/~guido/)

D'oh. I forgot to point to the patch. It's here: http://bugs.python.org/issue1416 On Nov 9, 2007 10:00 PM, Guido van Rossum <guido@python.org> wrote:
To follow up, I now have a patch. It's pretty straightforward.
This implements the kind of syntax that I believe won over most folks in the end:
@property def foo(self): ...
@foo.setter def foo(self, value=None): ...
There are also .getter and .deleter descriptors. This includes the hack that if you specify a setter but no deleter, the setter is called without a value argument when attempting to delete something. If the setter isn't ready for this, a TypeError will be raised, pretty much just as if no deleter was provided (just with a somewhat worse error message :-).
I intend to check this into 2.6 and 3.0 unless there is a huge cry of dismay. Docs will be left to volunteers as always.
--Guido
On Oct 31, 2007 9:08 AM, Guido van Rossum <guido@python.org> wrote:
I've come up with a relatively unobtrusive pattern for defining setters. Given the following definition:
def propset(prop): assert isinstance(prop, property) def helper(func): return property(prop.__get__, func, func, prop.__doc__) return helper
we can declare getters and setters as follows:
class C(object):
_encoding = None
@property def encoding(self): return self._encoding
@propset(encoding) def encoding(self, value=None): if value is not None: unicode("0", value) # Test it self._encoding = value
c = C() print(c.encoding) c.encoding = "ascii" print(c.encoding) try: c.encoding = "invalid" # Fails except: pass print(c.encoding)
I'd like to make this a standard built-in, in the hope the debate on how to declare settable properties.
I'd also like to change property so that the doc string defaults to the doc string of the getter.
-- --Guido van Rossum (home page: http://www.python.org/~guido/)
-- --Guido van Rossum (home page: http://www.python.org/~guido/)
-- --Guido van Rossum (home page: http://www.python.org/~guido/)

Unless I get negative feedback really soon I plan to submit this later today. I've tweaked the patch slightly to be smarter about replacing the setter and the deleter together if they are the same object. On Nov 9, 2007 10:03 PM, Guido van Rossum <guido@python.org> wrote:
D'oh. I forgot to point to the patch. It's here: http://bugs.python.org/issue1416
On Nov 9, 2007 10:00 PM, Guido van Rossum <guido@python.org> wrote:
To follow up, I now have a patch. It's pretty straightforward.
This implements the kind of syntax that I believe won over most folks in the end:
@property def foo(self): ...
@foo.setter def foo(self, value=None): ...
There are also .getter and .deleter descriptors. This includes the hack that if you specify a setter but no deleter, the setter is called without a value argument when attempting to delete something. If the setter isn't ready for this, a TypeError will be raised, pretty much just as if no deleter was provided (just with a somewhat worse error message :-).
I intend to check this into 2.6 and 3.0 unless there is a huge cry of dismay. Docs will be left to volunteers as always.
--Guido
On Oct 31, 2007 9:08 AM, Guido van Rossum <guido@python.org> wrote:
I've come up with a relatively unobtrusive pattern for defining setters. Given the following definition:
def propset(prop): assert isinstance(prop, property) def helper(func): return property(prop.__get__, func, func, prop.__doc__) return helper
we can declare getters and setters as follows:
class C(object):
_encoding = None
@property def encoding(self): return self._encoding
@propset(encoding) def encoding(self, value=None): if value is not None: unicode("0", value) # Test it self._encoding = value
c = C() print(c.encoding) c.encoding = "ascii" print(c.encoding) try: c.encoding = "invalid" # Fails except: pass print(c.encoding)
I'd like to make this a standard built-in, in the hope the debate on how to declare settable properties.
I'd also like to change property so that the doc string defaults to the doc string of the getter.
-- --Guido van Rossum (home page: http://www.python.org/~guido/)
-- --Guido van Rossum (home page: http://www.python.org/~guido/)
--
--Guido van Rossum (home page: http://www.python.org/~guido/)
-- --Guido van Rossum (home page: http://www.python.org/~guido/)

On Nov 10, 2007 11:31 AM, Guido van Rossum <guido@python.org> wrote:
Unless I get negative feedback really soon I plan to submit this later today. I've tweaked the patch slightly to be smarter about replacing the setter and the deleter together if they are the same object.
Definitely +1 on the basic patch. Could you explain briefly the advantage of the "hack" that merges the set and del methods? Looking at the patch, I get a little nervous about this:: @foo.setter def foo(self, value=None): if value is None: del self._foo else: self._foo = abs(value) That means that ``c.foo = None`` is equivalent to ``del c.foo`` right? STeVe -- I'm not *in*-sane. Indeed, I am so far *out* of sane that you appear a tiny blip on the distant coast of sanity. --- Bucky Katt, Get Fuzzy

On Nov 10, 2007 11:09 AM, Steven Bethard <steven.bethard@gmail.com> wrote:
On Nov 10, 2007 11:31 AM, Guido van Rossum <guido@python.org> wrote:
Unless I get negative feedback really soon I plan to submit this later today. I've tweaked the patch slightly to be smarter about replacing the setter and the deleter together if they are the same object.
Definitely +1 on the basic patch.
Could you explain briefly the advantage of the "hack" that merges the set and del methods? Looking at the patch, I get a little nervous about this::
@foo.setter def foo(self, value=None): if value is None: del self._foo else: self._foo = abs(value)
That means that ``c.foo = None`` is equivalent to ``del c.foo`` right?
Which is sometimes convenient. But thinking about this some more I think that if I *wanted* to use the same method as setter and deleter, I could just write @foo.setter @foo.deleter def foo(self, value=None): ... So I'm withdrawing the hacks, making the code and semantics much simpler. See propset3.diff in http://bugs.python.org/issue1416 . -- --Guido van Rossum (home page: http://www.python.org/~guido/)

Guido van Rossum wrote:
Which is sometimes convenient. But thinking about this some more I think that if I *wanted* to use the same method as setter and deleter, I could just write
@foo.setter @foo.deleter def foo(self, value=None): ...
So I'm withdrawing the hacks, making the code and semantics much simpler.
I like the new way better than the implicit magic of your former patch. (*) I've reviewed your patch and I found a minor typo caused by copy and paste. Good work Guido! Christian (*) The buzz words 'implicit' and 'magic' are used in this posting to make Guido's non-pythonic-code-sense tingle. *scnr* :]

On Nov 10, 2007 1:43 PM, Christian Heimes <lists@cheimes.de> wrote:
Good work Guido!
With sich a ringing endorsement, I've submitted this to the 2.6 trunk and the py3k branch. -- --Guido van Rossum (home page: http://www.python.org/~guido/)
participants (8)
-
Aahz
-
Christian Heimes
-
Georg Brandl
-
Greg Ewing
-
Guido van Rossum
-
Phillip J. Eby
-
skip@pobox.com
-
Steven Bethard