property decorator?

Cameron Simpson cs at cskk.id.au
Wed Dec 20 19:50:31 EST 2017


On 20Dec2017 15:56, Irv Kalb <Irv at furrypants.com> wrote:
>I am trying  to work through the concept of the @property decorator with respect to object oriented programming.
>
>I believe that I understand how the @property and @<variableName>.setter work - and that they are used to turn what looks like direct access to instance variables into method calls in an object.    It seems like these two decorators are essentially fancy substitutes for traditional getter and setter methods.

That is correct. In a "pure" OO environment, all internal state would be 
hidden, and accessed and modified by getters and setters, or by side effects of 
other methods.

Python, like many languages, exposes the internal attributes of objects. Which 
is convenient, and I for one find pure "getter/setter" methods cumbersome and 
tedious.

A property lets you do a few things: to present some state that lends itself to 
looking like a plain variable, as a variable; to put constraints on the values 
set on such a variable (eg to raise exceptions for forbidden values) or to have 
side effects (eg reducing a Semaphore's capacity might block until enough is 
released); and to promote something that was formerly a plain old attribute 
with a value into something more complex while keeping the previous API of 
accessing the attribute directly.

>But, I'm having a hard time understanding why these names were chosen.
[...]
>My questions about this are really historical.  From my reading, it looks like 
>using an @property decorator is a reference to an older approach using a built 
>in "property" function.

Maybe so; I cannot speak to this. But a property is a well defined Python 
concept. The "property" decorator is a builtin function (well, a clas sinstance 
constructor) for creating one from a function, and seems pretty naturally named 
to me:

  https://docs.python.org/3/library/functions.html#property

>But here goes:
>
>1) Why were these decorator names chosen?  These two names @property and @<name>.setter don't seem to be very clear to me.  At a minimum, they don't match.  Wouldn't decorator names like @<name>.getter and @<name>.setter have been better - more explicit?

Yes, but there's a bootstrap issue: having @<name>.getter work immediately 
implies that <name>.getter is already an existing function, because the @ 
syntax applies a function to another function, returning a function. And 
"getter" is at the least a public name, possibly already in use for something 
else.

Don't forget that @foo is a general system for wrapping functions, and that 
@property is just one example. So if you have a class:

  class Klass:
    @foo
    def method(self, bah):
      ...

it creates the function "method", and then computes "foo(method)", which 
normally would return another function that itself calls the original "method", 
and defines "Klass.method" as that new function.

So if you do this:

  class Klass:
    @property
    def method(self):
      return 3

That defines Klass.method as a property. Until it is a property, you can't talk 
about Klass.method.setter. And you need to be specific: "method.setter" is a 
setter for that specific property ("method").

You can't do this:

  @method.getter
  def method(self):
    return 3

because "method" is not yet defined when you say "method.getter".

First you need to make it a property, which must be done with something _not_ 
named "method", because that name doesn't exist to start with. And that 
something is the builtin function "property", which is used to create 
properties via the decoator syntax.

_After_ that property is made, _then_ you can talk about "method.setter" 
because properties have a ".setter" method which constructs property setting 
functions.

Hoping this make the _process_ more clear, particularly that "method" isn't 
known to start with, therefore "method.getter" is not a name you can use.

>2)  Alternatively, if the getter was going to use the @property decorator, 
>then it would seem complimentary  to have the decorator name for the 
>associated setter function to have the word "property" in its also, something 
>like @propertySetter.

Because <objectName>.<name> is a property now, the existingence of a .setter 
function is implied; onemerely has to use it to define the setter method.

>3)  If I create a method with the @property decorator, is there anything else that is implied about the name of the method other than you can now refer to the <objectName>.<name> - which calls the appropriate method?

It is the ability to refer to <objectName>.<name> which is what makes a 
property a property. As a property, it also has <objectName>.<name>.setter and 
<objectName>.<name>.deleter defined, which are functions which can be used as 
decorators to define setter and deleter methods.

>My guess/understanding is that in my example above, "salary" is considered the 
>property name, which is how you would refer to it outside of the object.  
>Inside the class, you use the property name as the name of both the setter and 
>the getter methods.  Is that the right way to think about it?

Usually I think of the property "salary" as a "property", which looks like an 
attribute. Whether I access it from inside or outside the class is irrelevant.

Because the property doesn't itself store any state, I'd usually have an 
internal "_salary" attribute for something simple like this. Other properties 
might have more complex state or very expressed as some computed value of other 
state (eg some hypothetical "payroll_tax" which might derive from the salary 
and not itself have separate state).

>Finally, it seems very odd to me that when you use the @property decorator and 
>the @<variableName>.setter, that both of the methods that are decorated need 
>to have the same name (but of course different set of parameters.)  As a 
>teacher, this seems like it would be an extremely difficult concept to get 
>across to students, as this does not work the same way as other Python 
>functions (and methods).

Actually, it works exactly like other Python functions and methods. You just 
need to exercise sufficient care. I can also redefine a method, and in the past 
have made bugs by defining the same method twice (and then changing the first 
definition, with no effect!) This kind of thing is what lint tools are good at 
catching, and what some coding styles help prevent (in this case, had I been 
using a style where all methods were defined in lexical order I might have 
readily noticed the repeated definition).

>Without a decorator, the second function of the same name overrides an earlier 
>function of the same name, as in this simple example:
>
>def myFunction( ):
>    print('In first version of myFunction')
>
>def myFunction( ):
>    print('In second version of myFunction')
>
>myFunction()
>
>Which prints:  In second version of myFunction

Yes. A pitfall of a dynamic language where names may be redefined. Treat it as 
a general issue rather than special to properties (though as you say, it would 
be easy for a new person to forget the @salary.setter decoration).

>My most recent language before Python was ActionScript 3 (the language of 
>Flash).  It implemented a similar concept of being able to use what appeared 
>to be an explicit data reference which turns into a method call, like this 
>example (from StackOverflow):
>
>private var _loggy:String;
>
>public function get loggy ():String
>{
>  return _loggy;
>}
>
>public function set loggy ( loggy:String ):void
>{
>  // checking to make sure loggy's new value is kosher etc...
>  _loggy = loggy;
>}
>This syntax made the concept easy to understand and implement in an ActionScript class (even though it also uses the same name for both function).

That is kind of neat, though very specific to properties. The @decorator syntax 
if more general than that, and since it was possible to implement properties 
using it it is probably considered better to do so than to add further 
complexity to the language syntax. Which I'm inclined to agree with, though one 
can certainly argue the other way as well.

Cheers,
Cameron Simpson <cs at cskk.id.au> (formerly cs at zip.com.au)



More information about the Python-list mailing list