string-valued fget/fset/fdel for properties

I was wondering if there would be any interest for adding a special case for properties with string-valued fget/fset/fdel: - if fget is a string, then the getter returns the value of the member variable with the given name. - if fset is a string, then the setter sets the value of the member variable with the given name. - if fdel is a string, then the deleter deletes the member variable with the given name. I.e., the following groups would be functionally equivalant: property(fget='_foo') property(fget=lambda self: self._foo) property(fget=lambda self: getattr(self, '_foo')) property(fset='_foo') property(fset=lambda self, value: setattr(self, '_foo', value)) property(fdel='_foo') property(fdel=lambda self: delattr(self, '_foo')) This change has 2 advantages: 1. It's easier to read. (In my opinion, anyway; what do other people think?) 2. It's faster: for properties whose fget/fset/fdel are strings, we can avoid a function call (since the changes are implemented in c). Preliminary tests indicate that this results in approximately a 3x speedup for a tight loop of attribute lookups. (It's unclear how much of a speed increase you'd get in actual code, though.) and one disadvantage (that I can think of): - It's one more special case to document/know. This change shouldn't break any existing code, because there's currently no reason to use string-valued fget/fset/fdel. Does this change seem useful to other people? Do the advantages outweigh the disadvantage? Or are there other disadvantage that I neglected to notice? If this seems like a useful addition, I'd be happy to work on making a patch that includes test cases & doc changes. -Edward

Why bother with the getattr() example?
property(fset='_foo') property(fset=lambda self, value: setattr(self, '_foo', value))
Also of course (and IMO more readable): def _set_foo(self, value): self._foo = value property(fset=_set_foo)
property(fdel='_foo') property(fdel=lambda self: delattr(self, '_foo'))
(And similar here.)
Only if you're used to the new syntax. Otherwise it could mean a costly excursion into the docs.
Which makes me wonder if this argument has much value.
and one disadvantage (that I can think of):
- It's one more special case to document/know.
Right. It feels like a hack.
This change shouldn't break any existing code, because there's currently no reason to use string-valued fget/fset/fdel.
Correct.
It feels somewhat un-Pythonic to me: a special case that just happens to be useful to some folks. I want to be very careful in adding too many of those to the language, because it makes it harder to learn and makes it feel full of surprises for casual users. (I'm trying hard to avoid using the word "Perl" here. :-) I'm curious about the use case that makes you feel the need for speed. I would expect most properties not to simply redirect to another attribute, but to add at least *some* checking or other calculation. I'd be more in favor if you used a separate "renamed" property: foo = renamed("_foo") being a shortcut for def _get_foo(self): return self._foo def _set_foo(self, value): self._foo = value def _del_foo(self): del self._foo foo = property(_get_foo, _set_foo, _del_foo) but I've got a suspicion you want to combine some string argument (most likely for fget) with some function argument. --Guido van Rossum (home page: http://www.python.org/~guido/)

Guido:
I suspect he's thinking of cases where you only want to wrap special behaviour around *some* of the accessors, e.g. you want writing to a property to be mediated by a function, but reading it can just be a normal attribute access. Currently, you're forced to pay the price of a function call for both reading and writing in this case. Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | A citizen of NewZealandCorp, a | Christchurch, New Zealand | wholly-owned subsidiary of USA Inc. | greg@cosc.canterbury.ac.nz +--------------------------------------+

Guido van Rossum wrote:
To me it seems like the "obvious" behavior for a string fget/fset/fdel, but if it's not universally obvious than you're proably right that it's a bad idea to add it.
but I've got a suspicion you want to combine some string argument (most likely for fget) with some function argument.
Yes, the idea was that some properties only redirect on read, or only on write; and that the syntax could be made "cleaner" for those cases.
The primary motivation was actually to make the code "easier to read"; the speed boost was an added bonus. (Though not a trivial one -- I do have a good number of fairly tight loops that access properties.) The use case that inspired the idea is defining read-only properties for immutable objects. But I guess I would be better off going with wrapper functions that create the read-only properties for me (like <http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/157768>). Thanks for the feedback! -Edward

Hi everyone. My first post to the list, even thought I have been reading it for a long time now. 8) Edward Loper wrote:
Actually, this would introduce a nice feature: allow to easily subclass the functions that are part of the property, without the need to re-create the property in the subclass. class C(object): def get_foo(self): return 'C.foo' c = property('get_foo') class D(object): def get_foo(self): return 'D.foo' In the current behaviour, D would have to recreate the property, which can be cumbersome if you're only interested in overwriting one of the property's methods (which is the common case in my experience). But I don't agree with Edward that property should accept strings. I think they should just accept functions as of now, but don't store the actual function object, just it's name, and delay the name lookup until it is actually needed. What you guys think? Regards, Nicodemus.

Nicodemus <nicodemus@esss.com.br>:
Now *that* would be useful (it's slightly different from the original proposal, as I understood it). I wrote a function recently to create properties that work like that, and I'm finding it very useful. It would be great to have it as a standard feature, either as a part of the existing 'property' object, or an alternative one.
No! If all that's being used is the name, then just pass the name. Anything else would be pointless and confusing. Plus it would allow the new behaviour to coexist with the current one: if it's a function, call it, and if it's a string, use it as a method name to look up. Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | A citizen of NewZealandCorp, a | Christchurch, New Zealand | wholly-owned subsidiary of USA Inc. | greg@cosc.canterbury.ac.nz +--------------------------------------+

[Greg Ewing]
This alternate possibility is yet another argument against Edward's proposal. :-) But I think it can be done without using string literals: a metaclass could scan a class definition for new methods that override functions used by properties defined in base classes, and automatically create a new property. If you only want this behavior for selected properties, you can use a different class instead of 'property'. You could then also do away with the metaclass, but you'd be back at Nicodemus's proposal, and that seems to incur too much overhead (we could use heavy caching, but it would be a bit hairy). Anyway, all of this can be implemented easily by subclassign property or by defining your own descriptor class -- there's no magic, just define __get__ and __set__ (and __delete__ and __doc__, to be complete). So maybe somebody should implement this for themselves and find out how often they really use it. --Guido van Rossum (home page: http://www.python.org/~guido/)

Guido:
So maybe somebody should implement this for themselves and find out how often they really use it.
As I said, I have implemented something very similar to this and I'm making extensive use of it in my current project, which is a re-working of my Python GUI library. The world will get a chance to see it soon, I hope... Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | A citizen of NewZealandCorp, a | Christchurch, New Zealand | wholly-owned subsidiary of USA Inc. | greg@cosc.canterbury.ac.nz +--------------------------------------+

Guido van Rossum wrote:
I think the overhead is very small, unless I'm overlooking something. The only extra overhead that I see is the extra lookup every time the property is accessed, which is the same as calling a method. But I agree that this difference could be significant for some applications.
Actually, I already did it. 8) The class accepts functions just like property does, but keeps only the names of the functions, and uses getattr in __get__ and __set__ to access the actual functions (nothing magical, as you pointed it out). I use it quite often, and the biggest advantage is that when you *do* need to overwrite one of the property's methods, you don't have to change anything in the base class: you just overwrite the method in the derived class and that's it. So as a rule, I always use this property instead of the built-in, but that's for other reasons besides easy subclassing. Regards, Nicodemus.

I just thought of another small benefit - the property definition can precede the definitions of the methods which implement it, e.g. class C(object): c = property('get_foo') def get_foo(self): ... which is a more natural order to write things in. Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | A citizen of NewZealandCorp, a | Christchurch, New Zealand | wholly-owned subsidiary of USA Inc. | greg@cosc.canterbury.ac.nz +--------------------------------------+

OTOH I hate seeing name references inside string quotes, because it complicates reference checking by tools like PyChecker (which would have to be told about the meaning of the arguments to property to check this kind of forward references). --Guido van Rossum (home page: http://www.python.org/~guido/)

Guido:
OTOH I hate seeing name references inside string quotes, because it complicates reference checking by tools like PyChecker
Oh, dear... you're going to like some of the other tricks I'm pulling in PyGUI even less, then... Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | A citizen of NewZealandCorp, a | Christchurch, New Zealand | wholly-owned subsidiary of USA Inc. | greg@cosc.canterbury.ac.nz +--------------------------------------+

In article <200311250038.hAP0cLc03924@oma.cosc.canterbury.ac.nz>, Greg Ewing <greg@cosc.canterbury.ac.nz> wrote:
Name references inside string quotes are also a standard part of PyObjC (used to represent an objective-C "selector" i.e. a method name that has not yet been bound to an object type). -- David Eppstein http://www.ics.uci.edu/~eppstein/ Univ. of California, Irvine, School of Information & Computer Science

On 25 nov 2003, at 3:12, David Eppstein wrote:
That's an implementation detail, and the name references are references to *Objective-C* identifiers which are not always valid Python identifiers (it's highly unlikely that 'foo:bar:' will ever be a valid Python indentifier, while it is a valid Objective-C method name) Ronald

Why bother with the getattr() example?
property(fset='_foo') property(fset=lambda self, value: setattr(self, '_foo', value))
Also of course (and IMO more readable): def _set_foo(self, value): self._foo = value property(fset=_set_foo)
property(fdel='_foo') property(fdel=lambda self: delattr(self, '_foo'))
(And similar here.)
Only if you're used to the new syntax. Otherwise it could mean a costly excursion into the docs.
Which makes me wonder if this argument has much value.
and one disadvantage (that I can think of):
- It's one more special case to document/know.
Right. It feels like a hack.
This change shouldn't break any existing code, because there's currently no reason to use string-valued fget/fset/fdel.
Correct.
It feels somewhat un-Pythonic to me: a special case that just happens to be useful to some folks. I want to be very careful in adding too many of those to the language, because it makes it harder to learn and makes it feel full of surprises for casual users. (I'm trying hard to avoid using the word "Perl" here. :-) I'm curious about the use case that makes you feel the need for speed. I would expect most properties not to simply redirect to another attribute, but to add at least *some* checking or other calculation. I'd be more in favor if you used a separate "renamed" property: foo = renamed("_foo") being a shortcut for def _get_foo(self): return self._foo def _set_foo(self, value): self._foo = value def _del_foo(self): del self._foo foo = property(_get_foo, _set_foo, _del_foo) but I've got a suspicion you want to combine some string argument (most likely for fget) with some function argument. --Guido van Rossum (home page: http://www.python.org/~guido/)

Guido:
I suspect he's thinking of cases where you only want to wrap special behaviour around *some* of the accessors, e.g. you want writing to a property to be mediated by a function, but reading it can just be a normal attribute access. Currently, you're forced to pay the price of a function call for both reading and writing in this case. Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | A citizen of NewZealandCorp, a | Christchurch, New Zealand | wholly-owned subsidiary of USA Inc. | greg@cosc.canterbury.ac.nz +--------------------------------------+

Guido van Rossum wrote:
To me it seems like the "obvious" behavior for a string fget/fset/fdel, but if it's not universally obvious than you're proably right that it's a bad idea to add it.
but I've got a suspicion you want to combine some string argument (most likely for fget) with some function argument.
Yes, the idea was that some properties only redirect on read, or only on write; and that the syntax could be made "cleaner" for those cases.
The primary motivation was actually to make the code "easier to read"; the speed boost was an added bonus. (Though not a trivial one -- I do have a good number of fairly tight loops that access properties.) The use case that inspired the idea is defining read-only properties for immutable objects. But I guess I would be better off going with wrapper functions that create the read-only properties for me (like <http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/157768>). Thanks for the feedback! -Edward

Hi everyone. My first post to the list, even thought I have been reading it for a long time now. 8) Edward Loper wrote:
Actually, this would introduce a nice feature: allow to easily subclass the functions that are part of the property, without the need to re-create the property in the subclass. class C(object): def get_foo(self): return 'C.foo' c = property('get_foo') class D(object): def get_foo(self): return 'D.foo' In the current behaviour, D would have to recreate the property, which can be cumbersome if you're only interested in overwriting one of the property's methods (which is the common case in my experience). But I don't agree with Edward that property should accept strings. I think they should just accept functions as of now, but don't store the actual function object, just it's name, and delay the name lookup until it is actually needed. What you guys think? Regards, Nicodemus.

Nicodemus <nicodemus@esss.com.br>:
Now *that* would be useful (it's slightly different from the original proposal, as I understood it). I wrote a function recently to create properties that work like that, and I'm finding it very useful. It would be great to have it as a standard feature, either as a part of the existing 'property' object, or an alternative one.
No! If all that's being used is the name, then just pass the name. Anything else would be pointless and confusing. Plus it would allow the new behaviour to coexist with the current one: if it's a function, call it, and if it's a string, use it as a method name to look up. Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | A citizen of NewZealandCorp, a | Christchurch, New Zealand | wholly-owned subsidiary of USA Inc. | greg@cosc.canterbury.ac.nz +--------------------------------------+

[Greg Ewing]
This alternate possibility is yet another argument against Edward's proposal. :-) But I think it can be done without using string literals: a metaclass could scan a class definition for new methods that override functions used by properties defined in base classes, and automatically create a new property. If you only want this behavior for selected properties, you can use a different class instead of 'property'. You could then also do away with the metaclass, but you'd be back at Nicodemus's proposal, and that seems to incur too much overhead (we could use heavy caching, but it would be a bit hairy). Anyway, all of this can be implemented easily by subclassign property or by defining your own descriptor class -- there's no magic, just define __get__ and __set__ (and __delete__ and __doc__, to be complete). So maybe somebody should implement this for themselves and find out how often they really use it. --Guido van Rossum (home page: http://www.python.org/~guido/)

Guido:
So maybe somebody should implement this for themselves and find out how often they really use it.
As I said, I have implemented something very similar to this and I'm making extensive use of it in my current project, which is a re-working of my Python GUI library. The world will get a chance to see it soon, I hope... Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | A citizen of NewZealandCorp, a | Christchurch, New Zealand | wholly-owned subsidiary of USA Inc. | greg@cosc.canterbury.ac.nz +--------------------------------------+

Guido van Rossum wrote:
I think the overhead is very small, unless I'm overlooking something. The only extra overhead that I see is the extra lookup every time the property is accessed, which is the same as calling a method. But I agree that this difference could be significant for some applications.
Actually, I already did it. 8) The class accepts functions just like property does, but keeps only the names of the functions, and uses getattr in __get__ and __set__ to access the actual functions (nothing magical, as you pointed it out). I use it quite often, and the biggest advantage is that when you *do* need to overwrite one of the property's methods, you don't have to change anything in the base class: you just overwrite the method in the derived class and that's it. So as a rule, I always use this property instead of the built-in, but that's for other reasons besides easy subclassing. Regards, Nicodemus.

I just thought of another small benefit - the property definition can precede the definitions of the methods which implement it, e.g. class C(object): c = property('get_foo') def get_foo(self): ... which is a more natural order to write things in. Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | A citizen of NewZealandCorp, a | Christchurch, New Zealand | wholly-owned subsidiary of USA Inc. | greg@cosc.canterbury.ac.nz +--------------------------------------+

OTOH I hate seeing name references inside string quotes, because it complicates reference checking by tools like PyChecker (which would have to be told about the meaning of the arguments to property to check this kind of forward references). --Guido van Rossum (home page: http://www.python.org/~guido/)

Guido:
OTOH I hate seeing name references inside string quotes, because it complicates reference checking by tools like PyChecker
Oh, dear... you're going to like some of the other tricks I'm pulling in PyGUI even less, then... Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | A citizen of NewZealandCorp, a | Christchurch, New Zealand | wholly-owned subsidiary of USA Inc. | greg@cosc.canterbury.ac.nz +--------------------------------------+

In article <200311250038.hAP0cLc03924@oma.cosc.canterbury.ac.nz>, Greg Ewing <greg@cosc.canterbury.ac.nz> wrote:
Name references inside string quotes are also a standard part of PyObjC (used to represent an objective-C "selector" i.e. a method name that has not yet been bound to an object type). -- David Eppstein http://www.ics.uci.edu/~eppstein/ Univ. of California, Irvine, School of Information & Computer Science

On 25 nov 2003, at 3:12, David Eppstein wrote:
That's an implementation detail, and the name references are references to *Objective-C* identifiers which are not always valid Python identifiers (it's highly unlikely that 'foo:bar:' will ever be a valid Python indentifier, while it is a valid Objective-C method name) Ronald
participants (6)
-
David Eppstein
-
Edward Loper
-
Greg Ewing
-
Guido van Rossum
-
Nicodemus
-
Ronald Oussoren