Python getters and setters


F

Fernando Saldanha

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
 
Ad

Advertisements

M

MRAB

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.
 
S

Steven D'Aprano

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)
 
T

Tim Chase

# 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
 
I

Irmen de Jong

Certainly not. Python is not Java.



This one.



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
 
S

Steven D'Aprano

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.
 
Ad

Advertisements

C

Chris Angelico

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
 
Ad

Advertisements

F

Fernando Saldanha

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 morethan one reasonable definition. For example, when dealing with time seriesdata 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)"
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top