An alternative way of defining properties (warning: contains creative abuse of the class statement)
The @property.getter and @property.setter decorators are clever, but they have the disadvantage that you end up writing the name of the property no less than 5 times, all of which have to match. Thinking there must be a better way, I came up with this: def Property(name, bases, dict): return property(dict.get('get'), dict.get('set')) which allows you to write class Test: class foo(metaclass = Property): def get(self): print("Getting foo") return self._foo def set(self, x): print("Setting foo to", x) self._foo = x test = Test() test.foo = 42 print(test.foo) Output: Setting foo to 42 Getting foo 42 -- Greg
(Not sure why this is on python-ideas - wouldn't python-list be more appropriate? Keeping it where it is for now though.) On Thu, Jul 2, 2020 at 5:14 PM Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
The @property.getter and @property.setter decorators are clever, but they have the disadvantage that you end up writing the name of the property no less than 5 times, all of which have to match.
Thinking there must be a better way, I came up with this:
def Property(name, bases, dict): return property(dict.get('get'), dict.get('set'))
which allows you to write
class Test:
class foo(metaclass = Property):
def get(self): print("Getting foo") return self._foo
def set(self, x): print("Setting foo to", x) self._foo = x
test = Test() test.foo = 42 print(test.foo)
Output: Setting foo to 42 Getting foo 42
I quite like the use of class blocks as namespaces, but I'm not a fan of metaclassing. A decorated class is way cleaner in the source code. How about this? def Property(ns): return property(*[getattr(ns, k, None) for k in ("fget", "fset", "fdel", "__doc__")]) class Test: @Property class foo: """Docstring for foo""" def fget(self): print("Getting foo") return self._foo def fset(self, x): print("Setting foo to", x) self._foo = x Same output as yours, and it's easy to support fdel, and a docstring. ChrisA
On Thu, Jul 2, 2020 at 9:34 AM Chris Angelico <rosuav@gmail.com> wrote:
(Not sure why this is on python-ideas - wouldn't python-list be more appropriate? Keeping it where it is for now though.)
As someone not familiar with the other lists...why? It's a proposal of an idea that could use some debate. Isn't this the perfect place?
On Thu, Jul 2, 2020 at 7:06 PM Alex Hall <alex.mojaki@gmail.com> wrote:
On Thu, Jul 2, 2020 at 9:34 AM Chris Angelico <rosuav@gmail.com> wrote:
(Not sure why this is on python-ideas - wouldn't python-list be more appropriate? Keeping it where it is for now though.)
As someone not familiar with the other lists...why? It's a proposal of an idea that could use some debate. Isn't this the perfect place?
What's the idea being discussed? AIUI there's no need or request to change the language/stdlib, but maybe I'm misreading. This is a cute showcase of how a class namespace can be used to gather multiple functions together and build a property with them; it already works without any changes. So it's a great thing to discuss, but with no changes being proposed, it's not really an idea for the language. On python-list, there'll be more people, many of whom wouldn't think to hang out here, but could still appreciate (a) this cool way of doing properties, and (b) the more general notion that a decorated class or metaclass can be used for this kind of very powerful namespacing. ChrisA
On Thu, Jul 2, 2020 at 11:33 AM Chris Angelico <rosuav@gmail.com> wrote:
On Thu, Jul 2, 2020 at 7:06 PM Alex Hall <alex.mojaki@gmail.com> wrote:
On Thu, Jul 2, 2020 at 9:34 AM Chris Angelico <rosuav@gmail.com> wrote:
(Not sure why this is on python-ideas - wouldn't python-list be more appropriate? Keeping it where it is for now though.)
As someone not familiar with the other lists...why? It's a proposal of
an idea that could use some debate. Isn't this the perfect place?
What's the idea being discussed? AIUI there's no need or request to change the language/stdlib, but maybe I'm misreading. This is a cute showcase of how a class namespace can be used to gather multiple functions together and build a property with them; it already works without any changes. So it's a great thing to discuss, but with no changes being proposed, it's not really an idea for the language. On python-list, there'll be more people, many of whom wouldn't think to hang out here, but could still appreciate (a) this cool way of doing properties, and (b) the more general notion that a decorated class or metaclass can be used for this kind of very powerful namespacing.
Ah, now I understand. I did automatically assume that Greg was proposing adding this to the stdlib or something, but maybe I only assumed that because it was in the list.
On Thu, Jul 2, 2020 at 7:41 PM Alex Hall <alex.mojaki@gmail.com> wrote:
On Thu, Jul 2, 2020 at 11:33 AM Chris Angelico <rosuav@gmail.com> wrote:
What's the idea being discussed? AIUI there's no need or request to change the language/stdlib, but maybe I'm misreading.
Ah, now I understand. I did automatically assume that Greg was proposing adding this to the stdlib or something, but maybe I only assumed that because it was in the list.
Could be that I'm the one misunderstanding here, but only Greg can answer that question :) ChrisA
On 2/07/20 9:45 pm, Chris Angelico wrote:
On Thu, Jul 2, 2020 at 7:41 PM Alex Hall <alex.mojaki@gmail.com> wrote:
On Thu, Jul 2, 2020 at 11:33 AM Chris Angelico <rosuav@gmail.com> wrote:
What's the idea being discussed? AIUI there's no need or request to change the language/stdlib, but maybe I'm misreading.
Ah, now I understand. I did automatically assume that Greg was proposing adding this to the stdlib or something, but maybe I only assumed that because it was in the list.
Could be that I'm the one misunderstanding here, but only Greg can answer that question :)
I was thinking something like it might become a candidate for stdlib inclusion, if it wasn't universally hated for abusing the class statement. -- Greg
On Thu, Jul 2, 2020 at 8:38 PM Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
On 2/07/20 9:45 pm, Chris Angelico wrote:
On Thu, Jul 2, 2020 at 7:41 PM Alex Hall <alex.mojaki@gmail.com> wrote:
On Thu, Jul 2, 2020 at 11:33 AM Chris Angelico <rosuav@gmail.com> wrote:
What's the idea being discussed? AIUI there's no need or request to change the language/stdlib, but maybe I'm misreading.
Ah, now I understand. I did automatically assume that Greg was proposing adding this to the stdlib or something, but maybe I only assumed that because it was in the list.
Could be that I'm the one misunderstanding here, but only Greg can answer that question :)
I was thinking something like it might become a candidate for stdlib inclusion, if it wasn't universally hated for abusing the class statement.
Let's get concrete then. Suppose that the property constructor recognized a special one-argument form: if it has an fget attribute, it fetches its details from the fget, fset, fdel, and __doc__ attributes of that object. That way, it can be used in three ways: a standard constructor that does all the work with up to four arguments, the decorator form (which is the same as the one-arg form initially, but then you can use its setter decorator), and the class form. class Test: x = property(lambda self: 42, lambda self, val: print("Setting to", val)) @property def y(self): return 42 @y.setter def y(self, val): print("Setting to", val) # the theoretical one @property class z: def fget(self): return 42 def fset(self, val): print("Setting to", val) Since functions won't normally have fset attributes, this should be fairly safe - technically it'd be backward incompatible, but it's highly unlikely to break any real-world code. I don't write a lot of properties with setters, so I can't speak to the value of this, but it is at least plausible. (If you'd prefer to use "get" or "getter" or something in place of "fget", that would work fine. I used the same name as the keyword arg to property itself, but it can be anything.) ChrisA
02.07.20 10:12, Greg Ewing пише:
The @property.getter and @property.setter decorators are clever, but they have the disadvantage that you end up writing the name of the property no less than 5 times, all of which have to match.
5 times? How is it?
Thinking there must be a better way, I came up with this:
def Property(name, bases, dict): return property(dict.get('get'), dict.get('set'))
It has a problem with pickling (it is solvable). The larger problem is with using private (double underscored) variables and super().
On Thu, Jul 02, 2020 at 11:04:40AM +0300, Serhiy Storchaka wrote:
02.07.20 10:12, Greg Ewing пише:
The @property.getter and @property.setter decorators are clever, but they have the disadvantage that you end up writing the name of the property no less than 5 times, all of which have to match.
5 times? How is it?
Perhaps Greg meant to say *up to* rather than "no less". class MyClass: @property def spam(): ... @spam.setter def spam(self, value): ... @spam.deleter def spam(self): ... That makes five by my count, however I hardly ever write a deleter so three is more common, and only occassionally a setter so one is most common :-) -- Steven
On Thu, Jul 2, 2020 at 7:04 PM Steven D'Aprano <steve@pearwood.info> wrote:
That makes five by my count, however I hardly ever write a deleter so three is more common, and only occassionally a setter so one is most common :-)
Well, if you only want a getter, the most common way is fine. But if you then go to add a setter, you have to keep things in sync. ChrisA
On 2/07/20 9:00 pm, Steven D'Aprano wrote:
Perhaps Greg meant to say *up to* rather than "no less".
What I said is true for sufficiently small values of 5. :-) You're right that it depends on how many operations you want. For reading and writing it's 3; if you want deleting as well it's 5. But 3 is still a lot less DRY than 1. -- Greg
Coincidentally, cython has a custom, deprecated syntax for properties that is actually pretty similar, and nice: cdef class Spam: property cheese: "A doc string can go here." def __get__(self): # This is called when the property is read. ... def __set__(self, value): # This is called when the property is written. ... def __del__(self): # This is called when the property is deleted. https://cython.readthedocs.io/en/latest/src/userguide/extension_types.html#p... I don't think the benefits are worth a core python change, but great minds.. Steve On Thu, Jul 2, 2020 at 11:42 AM Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
On 2/07/20 9:00 pm, Steven D'Aprano wrote:
Perhaps Greg meant to say *up to* rather than "no less".
What I said is true for sufficiently small values of 5. :-)
You're right that it depends on how many operations you want. For reading and writing it's 3; if you want deleting as well it's 5.
But 3 is still a lot less DRY than 1.
-- Greg _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/4QVMNK... Code of Conduct: http://python.org/psf/codeofconduct/
On 2/07/20 10:49 pm, Stestagg wrote:
Coincidentally, cython has a custom, deprecated syntax for properties that is actually pretty similar
Not entirely a coincidence -- I invented that syntax for Pyrex, and Cython inherited it. I'm a little disappointed to hear it's been deprecated. :-( -- Greg
Haha, I had no idea! That's great. fyi, judicious use of cython allowed our python team to build python components that far outperformed their java counterparts in some real-time middle-office financial processing jobs for a really large client. So thank you. I can see why using the standard python syntax might be considered better than the more elegant custom version, but yes, it's a bit sad Steve On Thu, Jul 2, 2020 at 12:27 PM Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
On 2/07/20 10:49 pm, Stestagg wrote:
Coincidentally, cython has a custom, deprecated syntax for properties that is actually pretty similar
Not entirely a coincidence -- I invented that syntax for Pyrex, and Cython inherited it.
I'm a little disappointed to hear it's been deprecated. :-(
-- Greg _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/YPSI34... Code of Conduct: http://python.org/psf/codeofconduct/
On 2/07/20 8:04 pm, Serhiy Storchaka wrote:
It has a problem with pickling (it is solvable).
Can you elaborate? The end result is a property object just the same as you would get from using @property or calling property directly. I don't see how it can have any pickling problems beyond what properties already have.
The larger problem is with using private (double underscored) variables and super().
I don't know what you're talking about here. I didn't use any double-underscore names in my example. -- Greg
On Thu, Jul 2, 2020, 6:30 AM Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
On 2/07/20 8:04 pm, Serhiy Storchaka wrote:
It has a problem with pickling (it is solvable).
Can you elaborate? The end result is a property object just the same as you would get from using @property or calling property directly. I don't see how it can have any pickling problems beyond what properties already have.
The larger problem is with using private (double underscored) variables and super().
I don't know what you're talking about here. I didn't use any double-underscore names in my example.
I think what he may have meant is that if you tried accessing a double-underscore property of the outer class from the get/set methods, it won't properly de-mangle. Similarly, if you wanted to overwrite a property by using this property approach in the sub-class, but also call super for the parent's class property getter from within the get/set this wouldn't work!?
My personal feeling: I would love this idea (DRY gets me almost every time) if it weren't for that awful, terrible `class` keyword hanging out there. I wouldn't call using class this way "abuse", exactly, but it could be a potential use for an old idea raised more than once in the past: some kind of submodule or namespace definition statement: class Test: @Property ns foo: # ns a new syntax meaning a namespace, or "submodule", object """Docstring for foo""" def fget(self): print("Getting foo") return self._foo def fset(self, x): print("Setting foo to", x) self._foo = x --- Ricky. "I've never met a Kentucky man who wasn't either thinking about going home or actually going home." - Happy Chandler On Thu, Jul 2, 2020 at 1:46 PM Matthew Einhorn <moiein2000@gmail.com> wrote:
On Thu, Jul 2, 2020, 6:30 AM Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
On 2/07/20 8:04 pm, Serhiy Storchaka wrote:
It has a problem with pickling (it is solvable).
Can you elaborate? The end result is a property object just the same as you would get from using @property or calling property directly. I don't see how it can have any pickling problems beyond what properties already have.
The larger problem is with using private (double underscored) variables and super().
I don't know what you're talking about here. I didn't use any double-underscore names in my example.
I think what he may have meant is that if you tried accessing a double-underscore property of the outer class from the get/set methods, it won't properly de-mangle.
Similarly, if you wanted to overwrite a property by using this property approach in the sub-class, but also call super for the parent's class property getter from within the get/set this wouldn't work!?
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/ZN77S6... Code of Conduct: http://python.org/psf/codeofconduct/
On 3/07/20 5:42 am, Matthew Einhorn wrote:
I think what he may have meant is that if you tried accessing a double-underscore property of the outer class from the get/set methods, it won't properly de-mangle.
Ah, I see what you mean. I don't think that's a fatal problem; double-underscore names don't seem to get used very much, and if you did want to use one, it would just mean you couldn't use this particular way of defining properties.
Similarly, if you wanted to overwrite a property by using this property approach in the sub-class, but also call super for the parent's class property getter from within the get/set this wouldn't work!?
This is a problem with properties however you create them, and the same tecnhiques apply for dealing with it. You need to find the property object from the base class and extract its getter. -- Greg
On 3/07/20 5:42 am, Matthew Einhorn wrote:
Similarly, if you wanted to overwrite a property by using this property approach in the sub-class, but also call super for the parent's class property getter from within the get/set this wouldn't work!?
Realised after sending that you were talking about the magic of no-argument super() not working. That's probably true. I take it as a sign that no-argument super() is too magical for its own good. :-) -- Greg
02.07.20 13:26, Greg Ewing пише:
On 2/07/20 8:04 pm, Serhiy Storchaka wrote:
It has a problem with pickling (it is solvable).
Can you elaborate? The end result is a property object just the same as you would get from using @property or calling property directly. I don't see how it can have any pickling problems beyond what properties already have.
You are right since property objects currently are not pickleable by default. But it is possible to implement pickling support for property objects which will fail with your example (and I think third-party libraries do it). The difference is that full qualified names of getter and setter differ from the full qualified name of the property. It is solvable, but it may require more complex code.
The larger problem is with using private (double underscored) variables and super().
I don't know what you're talking about here. I didn't use any double-underscore names in my example.
Then try to use them. It would not work. class Test2: def __init__(self): self.__foo = 1 class foo(metaclass = Property): def get(self): return self.__foo Test2().foo class Test3Base: @property def foo(self): return 1 class Test3(Test3Base): class foo(metaclass = Property): def get(self): return super().foo + 1 Test3().foo
On 3/07/20 6:33 pm, Serhiy Storchaka wrote:
it is possible to implement pickling support for property objects which will fail with your example (and I think third-party libraries do it). The difference is that full qualified names of getter and setter differ from the full qualified name of the property.
In that case, they will fail for a variety of other reasonable ways of creating properties too, e.g. class Test: def get_foo(self): ... def set_foo(self, x): ... foo = property(get_foo, set_foo) -- Greg
participants (8)
-
Alex Hall
-
Chris Angelico
-
Greg Ewing
-
Matthew Einhorn
-
Ricky Teachey
-
Serhiy Storchaka
-
Stestagg
-
Steven D'Aprano