Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #52635 > unrolled thread

Python getters and setters

Started byFernando Saldanha <fsaldan1@gmail.com>
First post2013-08-17 09:53 -0700
Last post2013-08-17 22:26 -0700
Articles 8 — 6 participants

Back to article view | Back to comp.lang.python


Contents

  Python getters and setters Fernando Saldanha <fsaldan1@gmail.com> - 2013-08-17 09:53 -0700
    Re: Python getters and setters MRAB <python@mrabarnett.plus.com> - 2013-08-17 18:10 +0100
    Re: Python getters and setters Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-08-17 17:18 +0000
      Re: Python getters and setters Tim Chase <python.list@tim.thechases.com> - 2013-08-17 12:31 -0500
      Re: Python getters and setters Irmen de Jong <irmen.NOSPAM@xs4all.nl> - 2013-08-18 00:52 +0200
        Re: Python getters and setters Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-08-17 23:40 +0000
          Re: Python getters and setters Chris Angelico <rosuav@gmail.com> - 2013-08-18 02:07 +0100
    Re: Python getters and setters Fernando Saldanha <fsaldan1@gmail.com> - 2013-08-17 22:26 -0700

#52635 — Python getters and setters

FromFernando Saldanha <fsaldan1@gmail.com>
Date2013-08-17 09:53 -0700
SubjectPython getters and setters
Message-ID<227bd47c-0e86-4d65-985f-e59aa4f25294@googlegroups.com>
I am new to Python.

I understand that it is "unpythonic" to write getters and setters, and that property() can be used if necessary.

This deals with the case of attributes, but there are other kinds of information available within a class.

Suppose my class contains an attribute called "data" that can potentially provide a lot of information that will be needed by class users. I have two options:

1) For each piece of information within data (e.g., length) I write a method that retrieves that information:

    def data_length(self):
        return len(self.data)

2) I do not create such a method. Users that are interested in that information will have to write len(obj.data), where obj is a previously instantiated object of my class.

Which one of the two alternatives fits better with the Python philosophy? The first alternative is more work for me, creates a "heavier" class and may have slower performance, but makes things easier for the user and is more implementation independent.

Thanks for the help.

FS

[toc] | [next] | [standalone]


#52636

FromMRAB <python@mrabarnett.plus.com>
Date2013-08-17 18:10 +0100
Message-ID<mailman.17.1376759432.23369.python-list@python.org>
In reply to#52635
On 17/08/2013 17:53, Fernando Saldanha wrote:
> I am new to Python.
>
> I understand that it is "unpythonic" to write getters and setters, and that property() can be used if necessary.
>
> This deals with the case of attributes, but there are other kinds of information available within a class.
>
> Suppose my class contains an attribute called "data" that can potentially provide a lot of information that will be needed by class users. I have two options:
>
> 1) For each piece of information within data (e.g., length) I write a method that retrieves that information:
>
>      def data_length(self):
>          return len(self.data)
>
> 2) I do not create such a method. Users that are interested in that information will have to write len(obj.data), where obj is a previously instantiated object of my class.
>
> Which one of the two alternatives fits better with the Python philosophy? The first alternative is more work for me, creates a "heavier" class and may have slower performance, but makes things easier for the user and is more implementation independent.
>
If the attribute is public, i.e. the user is expected to write obj.data, 
then
len(obj.data) is the right way to get its length.

[toc] | [prev] | [next] | [standalone]


#52637

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2013-08-17 17:18 +0000
Message-ID<520fb04f$0$30000$c3e8da3$5496439d@news.astraweb.com>
In reply to#52635
On Sat, 17 Aug 2013 09:53:07 -0700, Fernando Saldanha wrote:

> Suppose my class contains an attribute called "data" that can
> potentially provide a lot of information that will be needed by class
> users. I have two options:
> 
> 1) For each piece of information within data (e.g., length) I write a
> method that retrieves that information:
> 
>     def data_length(self):
>         return len(self.data)

Certainly not. Python is not Java.


> 2) I do not create such a method. Users that are interested in that
> information will have to write len(obj.data), where obj is a previously
> instantiated object of my class.

This one.


> Which one of the two alternatives fits better with the Python
> philosophy? The first alternative is more work for me, creates a
> "heavier" class and may have slower performance, but makes things easier
> for the user and is more implementation independent.

How is this:

obj.data_length()

easier for the user than this?

len(obj.data)


The second is shorter to type, and it is a standard idiom that works 
everywhere. You want to know the length of something? len(something), no 
matter what it is. You don't have to play a game of "Guess the method 
name" with every class you come across.


# Yes, this is good, consistent design
len(myrecord.field)
len(obj.data)
len(data.value)
len(collection[key])


# No, this is crappy, inconsistent design
myrecord.field_len()
obj.data_length()
data.get_length_of_value()
collection.key_len(key)




-- 
Steven

[toc] | [prev] | [next] | [standalone]


#52638

FromTim Chase <python.list@tim.thechases.com>
Date2013-08-17 12:31 -0500
Message-ID<mailman.18.1376760589.23369.python-list@python.org>
In reply to#52637
On 2013-08-17 17:18, Steven D'Aprano wrote:
> # Yes, this is good, consistent design
> len(myrecord.field)
> len(obj.data)
> len(data.value)
> len(collection[key])

I would also add that, if the primary goal of your class is to
encapsulate the data, you can do

  class MyClass:
    def __init__(self, ...):
      self.data = []
    def __len__(self):
      return len(self.data)

which allows for the even clearer

  my_obj = MyClass(...)
  manipulate(my_obj)
  if len(my_obj) > 42:
    do_important_stuff()

-tkc

[toc] | [prev] | [next] | [standalone]


#52644

FromIrmen de Jong <irmen.NOSPAM@xs4all.nl>
Date2013-08-18 00:52 +0200
Message-ID<520ffe9a$0$15920$e4fe514c@news.xs4all.nl>
In reply to#52637
On 17-8-2013 19:18, Steven D'Aprano wrote:
> On Sat, 17 Aug 2013 09:53:07 -0700, Fernando Saldanha wrote:
> 
>> Suppose my class contains an attribute called "data" that can
>> potentially provide a lot of information that will be needed by class
>> users. I have two options:
>>
>> 1) For each piece of information within data (e.g., length) I write a
>> method that retrieves that information:
>>
>>     def data_length(self):
>>         return len(self.data)
> 
> Certainly not. Python is not Java.
> 
> 
>> 2) I do not create such a method. Users that are interested in that
>> information will have to write len(obj.data), where obj is a previously
>> instantiated object of my class.
> 
> This one.
> 
> 
>> Which one of the two alternatives fits better with the Python
>> philosophy? The first alternative is more work for me, creates a
>> "heavier" class and may have slower performance, but makes things easier
>> for the user and is more implementation independent.
> 
> How is this:
> 
> obj.data_length()
> 
> easier for the user than this?
> 
> len(obj.data)
> 

It's not easier for the user perse, but it might be preferable from a design point of
view. For the direct components of obj, it's probably alright to access them directly.
There's something called the Law of Demeter aka principle of least knowledge
(http://en.wikipedia.org/wiki/Law_of_Demeter) though. It basically argues against the
use of "more than one dot". Doing that ties the use of the object more to the actual
implementation/internal structure of the object.


Irmen

[toc] | [prev] | [next] | [standalone]


#52646

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2013-08-17 23:40 +0000
Message-ID<521009ee$0$30000$c3e8da3$5496439d@news.astraweb.com>
In reply to#52644
On Sun, 18 Aug 2013 00:52:06 +0200, Irmen de Jong wrote:

> On 17-8-2013 19:18, Steven D'Aprano wrote:
[...]
>> How is this:
>> 
>> obj.data_length()
>> 
>> easier for the user than this?
>> 
>> len(obj.data)
>> 
>> 
> It's not easier for the user perse, but it might be preferable from a
> design point of view. For the direct components of obj, it's probably
> alright to access them directly. 

Yes, and my comments assume that obj.data is public in the first place.


> There's something called the Law of
> Demeter aka principle of least knowledge
> (http://en.wikipedia.org/wiki/Law_of_Demeter) though. It basically
> argues against the use of "more than one dot". Doing that ties the use
> of the object more to the actual implementation/internal structure of
> the object.

A very good point! But not an easy one to understand. The Law of Demeter 
can be described as "if you want to walk the dog, talk to the dog, not 
the dog's legs". Another classic example is of the paperboy who reaches 
into your back pocket, pulls out your wallet, removes the money he is 
owed, then puts your wallet back:

# No, this is bad design.
class Paperboy:
    def take_payment(self, customer):
        payment_owed = 2
        wallet = customer.backpocket.get_wallet()
        money = wallet.take(payment_owed)
        self.money += money
        customer.backpocket.put_wallet(wallet)


It's bad design because it ties the paperboy to one specific 
implementation of customer. Perhaps the customer carries his wallet in a 
front pocket. Or she carries her purse in her handbag. Or she is the 
Queen of England, and doesn't carry money at all, but has a flunky who 
carries money for her. Better is:

class Paperboy:
    def take_payment(self, customer):
        payment_owed = 2
        self.money = customer.get_payment(payment_owed)

and raise an error if the customer doesn't implement get_payment.


The Law of Demeter is often characterised as "don't use more than one dot 
at a time", but of course that's wrong for two reasons:


- Re-writing take_payment to avoid multiple dots is still wrong:


    def take_payment(self, customer):
        payment_owed = 2
        pocket = customer.backpocket
        wallet = pocket.get_wallet()
        money = wallet.take(payment_owed)
        self.money += money
        pocket.put_wallet(wallet)


- If the attribute is part of the public interface, then it is okay to 
use it no matter how deep (how many dots) it is.

The Law of Demeter is really about being careful about what interface 
your classes provide. Customers should provide a get_payment method; dogs 
should provide a walk method. You shouldn't have to send individual step 
messages to the dog's legs.


-- 
Steven

[toc] | [prev] | [next] | [standalone]


#52648

FromChris Angelico <rosuav@gmail.com>
Date2013-08-18 02:07 +0100
Message-ID<mailman.22.1376788044.23369.python-list@python.org>
In reply to#52646
On Sun, Aug 18, 2013 at 12:40 AM, Steven D'Aprano
<steve+comp.lang.python@pearwood.info> wrote:
> The Law of Demeter is really about being careful about what interface
> your classes provide. Customers should provide a get_payment method; dogs
> should provide a walk method. You shouldn't have to send individual step
> messages to the dog's legs.

And it has its limits, too. If you're walking a dog on a leash, you
don't send a message to the leash to send a message to the dog to
walk. You talk directly to the dog. Like most programming principles,
it's something to read, to grok, and then to think about. I have yet
to find any rule which, if slavishly followed, will not lead to bad
code _somewhere_.

ChrisA

[toc] | [prev] | [next] | [standalone]


#52650

FromFernando Saldanha <fsaldan1@gmail.com>
Date2013-08-17 22:26 -0700
Message-ID<5e7fd63b-78b2-4afb-9a84-33afb19378e1@googlegroups.com>
In reply to#52635
The debate got more interesting than I expected. Without taking sides, I would like to add that perhaps my "length" example was misleading: length is easy to calculate. The information could be hard to extract from the data, either because complex calculations are involved, or because it is not apparent which pieces of information have to be combined. Also, notice that I never stated that the information was in the shape of an attribute.

The comparison below may seem clear cut if the information to be obtained is length but one could arrive at exactly the opposite conclusion by imagining that "length" is hard to calculate or extract. It may be easier and faster to look up a function in an API than figuring out how to do a complex calculation.

Even in the simple case where the information is "length" there may be more than one reasonable definition. For example, when dealing with time series data in matrix form it makes sense to consider the number of rows as the length of the data, but it also makes sense to define length as the number of elements of the matrix. So the word "len" in the first half of the example below could hide different concepts: consistent but misleading. The different methods in the second half could have the virtue of clarifying which concept of length applies in each case.

"# Yes, this is good, consistent design 
len(myrecord.field) 
len(obj.data) 
len(data.value) 
len(collection[key]) 


# No, this is crappy, inconsistent design 
myrecord.field_len() 
obj.data_length() 
data.get_length_of_value() 
collection.key_len(key)"

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web