Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #52635 > unrolled thread
| Started by | Fernando Saldanha <fsaldan1@gmail.com> |
|---|---|
| First post | 2013-08-17 09:53 -0700 |
| Last post | 2013-08-17 22:26 -0700 |
| Articles | 8 — 6 participants |
Back to article view | Back to comp.lang.python
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
| From | Fernando Saldanha <fsaldan1@gmail.com> |
|---|---|
| Date | 2013-08-17 09:53 -0700 |
| Subject | Python 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]
| From | MRAB <python@mrabarnett.plus.com> |
|---|---|
| Date | 2013-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]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2013-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]
| From | Tim Chase <python.list@tim.thechases.com> |
|---|---|
| Date | 2013-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]
| From | Irmen de Jong <irmen.NOSPAM@xs4all.nl> |
|---|---|
| Date | 2013-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]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2013-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]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2013-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]
| From | Fernando Saldanha <fsaldan1@gmail.com> |
|---|---|
| Date | 2013-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