Python Forum
Curious about decorator syntax
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Curious about decorator syntax
#1
I'm curious as to the rationale behind the choice of decorator syntax for getters and setters. Take, for example:
    @property
    def prop(self):
        return self._prop
        
    @prop.setter
    def prop(self, value):
        if not isinstance(value, int | float) or value <= 0:
            raise ValueError("positive number expected")
        self._prop = value
 

It seems to me that the following syntax would have been both more consistent, clearer, and less prone to typos (note that you don't need to specify the property name in both the @property line and def line):
    @getter
    def prop(self):
        return self._prop
        
    @setter
    def prop(self, value):
        if not isinstance(value, int | float) or value <= 0:
            raise ValueError("positive number expected")
        self._prop = value
Reply
#2
(Apr-30-2023, 01:58 PM)rjdegraff42 Wrote: I'm curious as to the rationale behind the choice of decorator syntax
It is not only syntax, it is management of Python objects. Take the following code
class A:
    @property
    def prop(self):
        return self._prop
    print(prop)

    @prop.setter
    def spam(self, value):
        if not isinstance(value, int | float) or value <= 0:
            raise ValueError("positive number expected")
        self._prop = value
    print(prop)
    print(spam)

a = A()
a.spam = 4
print(a.prop)
print(a.spam)
Output:
<property object at 0x7f51dff434c0> <property object at 0x7f51dff434c0> <property object at 0x7f51dffd19e0> 4 4
The property decorator transforms a function into a property object which implement the descriptor protocol. The decorator prop.setter takes a function and returns a new property object copied from prop updated with the new setter function.

In the above example, you will notice that I created two property objects A.prop and A.spam for class A. They both always return the same value because they have the same getter, but prop is read-only while spam can be written.

Also remember that due to the semantics of decorators syntax, the above code is equivalent to
class A:
    def prop(self):
        return self._prop
    prop = property(prop)

    def spam(self, value):
        if not isinstance(value, int | float) or value <= 0:
            raise ValueError("positive number expected")
        self._prop = value
    spam = prop.setter(spam)

a = A()
a.spam = 4
print(a.prop)
print(a.spam)
Reply
#3
Now that just makes me cringe. The following:

@prop.setter
def spam(self, value):
should be illegal. Many years ago when I maintained AGC/SCADA code written in FORTRAN I came across the following:

INTEGER NINE /7/
What possible rationale could be given for your example?
Reply
#4
(Apr-30-2023, 05:57 PM)rjdegraff42 Wrote: should be illegal.
That's because you think in terms of syntax, but again it is not syntax, it is semantics. In the same way
NINE = 7
is perfectly legal in many languages, not just in Python.

In python, a property is an object. You don't declare a property, you create a property object. Think of properties as objects, learn the descriptor protocol and you'll see that it makes perfect sense.
Reply
#5
Just because something is legal doesn't make it right.

My point was for consistency in syntax. Since getters and setters apply only to properties it would make sense to have @getter and @setter decorators rather than @property. That would explicitly identify both the getter and setter and also eliminate the redundancy of having to specify the setter name in both the def and the decorator. Note that my suggestion could be implemented without breaking existing code. I thought clarity was one of the goals of Python.
Reply
#6
(May-02-2023, 02:23 PM)rjdegraff42 Wrote: That would explicitly identify both the getter and setter and also eliminate the redundancy of having to specify the setter name in both the def and the decorator.
That's because you still don't take into account what decorators really are. Here is an alternative way, which simply works
class A:
    def _foo_getter(self):
        return 10

    def _foo_setter(self, value):
        print('Hello there!')

    def _foo_deleter(self):
        print('Deleting foo')

    foo = property(_foo_getter, _foo_setter, _foo_deleter)
(May-02-2023, 02:23 PM)rjdegraff42 Wrote: Note that my suggestion could be implemented without breaking existing code.
Try to implement it and you will see that it is not that obvious. I imagine a possible implementation involving a metaclass or a class decorator.
(May-02-2023, 02:23 PM)rjdegraff42 Wrote: I thought clarity was one of the goals of Python.
The current usage of properties is extremely simple, basically, it is
class A:
    foo = some_property_object

a = A()
a.foo = value
print(a.foo)
The decorators are only syntactic sugar to create the property instance.
Reply
#7
Why do you consider

class A:
    def _foo_getter(self):
        return 10
 
    def _foo_setter(self, value):
        print('Hello there!')
 
    def _foo_deleter(self):
        print('Deleting foo')
 
    foo = property(_foo_getter, _foo_setter, _foo_deleter)
clearer than

class A:

    @getter
    def foo(self):
        return 10

    @setter 
    def foo(self, value):
        print('Hello there!')

    @deleter 
    def foo(self):
        print('Deleting foo')


I think the symmetry of my suggestion would be preferable. I liken the existing way to giving a list of items as

    1. first item
    2. second item
    C. third item
While the intent is understood, it lacks symmetry and clarity. It brings to mind a beef I have with Windows in that the registry entries are, in my opinion (and in the opinion of at least one former MS engineer), moronic.

Special folder      Registry entry name
--------------      -------------------
My Music            My Music
My Pictures         My Pictures
My Video            My Videos
My Documents        Personal
This, after all, is why Python has standards for the naming of classes, methods, etc.
Reply
#8
I'm not saying the current syntax is clearer. In fact properties don't exist at all in Python's syntax. Nor do static methods or class methods. All these OOP features don't exist at the syntactic level. They exist only at the semantic level by the virtue of the descriptor protocol. A simple concept, the descriptor object gives a way to implement all of them, and more. Their simplicity comes from the fact that they belong to this same family of descriptor objects.

You are suggesting something very different: create a specific syntax for the properties alone. I don't think it simplifies the language in any way. In python, the code that you are writing is equivalent to
class A:

    def foo(self):
        return 10
    foo = getter(foo)
 
    def foo(self, value):  # this erases the foo that we just defined
        print('Hello there!')
    foo = setter(foo)
 
    def foo(self): # again this erases the foo that we just defined
        print('Deleting foo')
    foo = deleter(foo)
So if you want your syntax to work, you must start by changing the semantics of Python's DECORATORS, otherwise it won't work, but this is a very bad idea. We could not trust the decorator syntax anymore.

In the name of a superficial simplicity, you are ready to destroy the coherence of the language.
Reply
#9
While I see your point, I don't see how my suggestion breaks anything, although comparing
class A:
 
    def foo(self):
        return 10
    foo = getter(foo)
  
    def foo(self, value):  # this erases the foo that we just defined
        print('Hello there!')
    foo = setter(foo)
  
    def foo(self): # again this erases the foo that we just defined
        print('Deleting foo')
    foo = deleter(foo)
to
class A:

    @getter
    def foo(self):
        return 10

    @setter    
    def foo(self, value):  # this erases the foo that we just defined
        print('Hello there!')

    @deleter  
    def foo(self): # again this erases the foo that we just defined
        print('Deleting foo')
I don't see a whole lot of difference. I still think my suggestion is clearer. Again, I don't see how my suggestion breaks any existing code since there are not currently @getter, @setter, or @deleter decorators. In the absence of any changes, I would prefer to use your latest format since that is the most consistent.
Reply
#10
(May-02-2023, 07:58 PM)rjdegraff42 Wrote: I don't see a whole lot of difference.
You don't see a whole lot of difference because you're only looking whether the code is more or less pretty, but this is not at all the question. According to the semantics of decorators, these two pieces of code are strictly equivalent. There is no difference AT ALL in what they DO. Now my question is: what are your three functions getter() setter() and deleter() supposed to do exactly? I think it will be tricky to specify.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  the order of running code in a decorator function akbarza 2 536 Nov-10-2023, 08:09 AM
Last Post: akbarza
  ABC Module and @property decorator, Pythonic Way? muzikman 21 5,704 Aug-18-2021, 06:08 PM
Last Post: muzikman
  decorator adamfairhall 0 1,571 Aug-18-2020, 08:38 AM
Last Post: adamfairhall
  Use of @property decorator ruy 16 6,583 Jun-09-2020, 05:29 PM
Last Post: buran
  Decorator staticmethod Use Cases Devarishi 3 2,657 May-20-2019, 04:27 AM
Last Post: Devarishi
  How can we override decorator? bhojendra 2 9,391 May-12-2019, 11:15 PM
Last Post: ichabod801
  curious syntax with dictionary item inselbuch 3 2,780 Mar-09-2019, 04:21 PM
Last Post: ichabod801
  Decorator question Dan741 1 2,400 Nov-14-2018, 10:05 AM
Last Post: wavic
  Decorator toy code throws syntax errors kevinxhi 3 3,590 Sep-04-2017, 03:01 AM
Last Post: kevinxhi
  accessing variables from a decorator ashwin 1 3,117 Jun-30-2017, 04:11 PM
Last Post: nilamo

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020