Accessors in Python (getters and setters)

Bruno Desthuilliers onurb at xiludom.gro
Thu Jul 13 08:51:47 EDT 2006


mystilleef wrote:
> Bruno Desthuilliers wrote:
> 
>>mystilleef wrote:
>>
(snip)
>>>>
>>>>>I have used that name in
>>>>>dozens of places spanning over 27000 LOC.
>>>>
>>>>Too bad for you.
>>>
>>>
>>>Thank you, that was helpful.
>>
>>What am I supposed to say ? You choose a bad name for a public symbol,
>>used it in 27000 lines of Python code (which certainly took some time to
>>write),
> 
> 
> This is a __baseless__ statement.

Please explain what's wrong ? The "bad naming" part is your own
statement, the "27000 lines" is your own statement too, and I don't see
what's wrong with the assumption that producing 27000 lines of Python
took some time.

> For all you know I could have used
> the attribute twice in those 27000 lines of code.

How does this make my statement wrong ? Did I imply that you used this
attribute in each and any of the 27000 lines ?

> Even then searching
> for and replacing the attribute is tedious and error prone.

Indeed, and whether you believe it or not, I do share your pain here. I
had to face similar problem, and without even find and grep (a dumb
'CASE Tool' that stored it's proprietary language code in a
non-documented binary format and was missing decent, reliable
search/replace utilities). And the project was actually more than
50KLOC, forms description excluded.

> 
>>and all of a sudden you realize the name is bad and proceed to
>>fix the problem.
>  
> Yes, the same way many programmers suddenly realize their class,
> function, method, project, etc is poorly designed even after using it
> for months and proceed to fix the problem. Shit happens.

Agreed. I don't pretend to be better than anyone else at this.

> 
>>If you know of any other way than a massive
>>find/replace, please share.
> 
> 
> I wouldn't have started the topic if I did. Apparently the only
> solution is to rereference the attribute with a better name and pray
> and hope third party developers will use the better name. Or search and
> replace!

Is actually this code already published ? If yes, you have the option of
making the 'tmp' accessor a computed attribute pointing to the better
named one, have the hidden getter/setter code emit a warning (including
useful informations about where the attributes is accessed - this is
opened to inspection), and officialy deprecate the badly named attribute
so you can definitively get rid of it in a near release.

> 
>>But blaming the language for your error won't help. 
> 
> I did no such thing, you are making that up.

Sorry, that's what I understood. If it's me being guilty of
overreaction, please accept my apologies.

> 
>>>>>There's a chance that other
>>>>>develops might misinterpret exactly what "tmp" does. Plus I don't want
>>>>>emails from other developers querying me about what "tmp" is/does.
>>>>>"tmp" is obvious to me, but not necessarily to others.
>>>>
>>>>So why did you name it that way at first ?
>>>>
>>>
>>>What does it matter? There are 10 million and one reasons from given
>>>identifiers bad names.
>>
>>And how many reasons to use a bad name in 27000 LOCs before deciding to
>>fix the problem ?
>> 
> 
> Do you really think I used the name 27000 times in 27000 lines of code?

Of course not. But since 27KLOC is far from trivial for a Python
application, one can think that you had time to notice something wrong
with the naming/use of this attribute.

> Maybe I'm not making myself clear. Having to search 27000 lines of code
> to replace an identifier name is tedious and error prone.

Yes. You can of course encapsulate access to the attribute in a property
and have that property either emit a warning, raise an exception, log
the access etc, so if you missed some use of it, chances are you'll find
out pretty soon. Unit tests may help too - they can be great for
reporting broken code. You may also want to look if lint-like tools
(pylint etc) can help you there.

> 
>>>>>Now compare that
>>>>>to the accessors.
>>>>
>>>>But 'tmp' actually *is* an accessor.
>>>
>>>
>>>I didn't say it wasn't.
>>
>>Yes you did. May I quote ?
>>"""
>>"tmp" is obvious to me, but not necessarily to others. Now compare that
>>to the accessors. Not only do they improve readability at the expense
>>of more code, they actually allow me to change the lousily named
>>attribute "tmp" to "temporary_buffer" without grepping, seding,
>>searching, replacing and praying.
>>"""
>>
>>This obviously implies that you don't understand 'tmp' as being an accessor.
>> 
> 
> You are quoting me out of context. I was speaking with respect to real
> accessors, not Python's latent implementation mechanisms.

This I had understood. My point is that if you *see* a public attribute
name as really being an accessor with automagical default
getters/setters (FWIW, look at the __getattribute__ method and Python's
name lookup rules - you'll notice that it's really how it works), then
your "compare that to accessors" doesn't stand.

> 
>>>>>Not only do they improve readability
>>>>
>>>>Err... do you find:
>>>>
>>>>obj.set_temporary_buffer(val)
>>>>val = obj.get_temporary_buffer()
>>>>
>>>>really more readable than:
>>>>
>>>>obj.temporary_buffer = val
>>>>val = obj.temporary_buffer
>>>
>>>
>>>I didn't name the attribute temporary_buffer, I named it tmp.
>>
>>You didn't name the *accessor* 'temporary_buffer', you named it 'tmp'.
>>You are *exactly* in the same situation as if you had used getter/setter
>>named "get_tmp" and 'set_tmp". And your point about getters/setters
>>"improving readability" is moot.
> 
> No I'm not. Naming attributes and naming methods are entirely different
> situations.

Why ?

In Python, a "method" is nothing else than a descriptor (attribute)
returning a callable. I agree that there's a small difference between a
callable and a non-callable object, in that you can apply the __call__
operator only to callables, but this doesn't make such a difference
here. In Python, methods are attributes. And naming is naming.

> 
(snip)
>>
>>>In Java there's a clear distinction between attributes
>>>and methods.
>>
>>Yes, and that's a big weakness of Java. But that's another point, and
>>absolutely irrelevant here. Failing to understand a language semantics
>>gives no right to complain about the language.
>> 
> 
> I never complained about either languages. You are making that up.

I may be misunderstanding your point about "Java having a clear
distinction between attributes and methods" and overinterpreting it as
implying "Python doesn't and it led me into a bad situation, hence Java
is Good(tm) and Python is Bad(tm)". If so, once again, I do apologize.

>>>>>Sure, if you are dealing with less
>>>>>than a 1000LOC you can get away with using "tmp" or renaming it easily.
>>>>>But if you are dealing with a much larger code base and more
>>>>>developers, issues like this rapidly become a nuisance.
>>>>
>>>>Indeed. But it's *your* responsability to choose good names for the API.>
>>>
>>>I choose good names for most of my APIs. But there cases when you never
>>>know an attribute will actually be an API before hand.
>>
>>Then don't make it part of the API. And anyway, 27000 lines of Python
>>take some time to write, so you had the opportunity to address the
>>problem long ago - FWIW, you could have fixed it as soon as you noticed
>>you were accessing this attribute in client code.
>> 
> 
> Yes, I fixed it then. Which was 27000 lines of code later.

Not exactly a garden party.

>>>>>Yes, it is possible to name crappy accessors too (e.g set_tmp/get_tmp).
>>>>
>>>>or 'tmp'.
>>>>
>>>>>But developers tend to pay more attention to given methods/functions
>>>>>less crappy names, at least when compared to data attributes.
>>>>
>>>>s/developpers/you/>
>>>
>>>Ha, right! I bet you are perfect developer.
>>
>>Certainly not, but I wouldn't confuse my own problems with a general
>>rule. Note FWIW that I wouldn't blame a language (or my lack of
>>understanding of that language) for my own errors.
>> 
> 
> Can you point me to exactly where I blamed Python for anything?

Point addressed above.

>>>>>This
>>>>>stems from the fact that in many languages data attributes aren't
>>>>>usually part of the API,
>>>>
>>>>Once again, in Python, there is *no* such thing as 'data attributes'.
>>>>*All* attributes are *objects* - some of them callable.
>>>>
>>>
>>>I didn't say attributes weren't objects.
>>
>>Nope, but you constantly bring that false "data/method" dichotomy in
>>your justifications for having badly named a public symbol.
> 
> There is a dichotomy between data and methods. Yes even in Python.

The only dichotomy, from a technical POV, is that methods are callable -
but so are classes, and any object that has a '__call__' attribute that
is itself a callable. The 'dichotomy' here is the same as the dichotomy
between iterable and non iterable objects. Of between file-like and
non-file like objects. IOW, the difference amounts to supported
interface, not to the very nature of the concerned objects.

>>>>>I know I would not name the accessors set_tmp/get_tmp, because my
>>>>>philosophy is that methods/functions need to have meaningful names and
>>>>>state their intended purpose.
>>>>
>>>>That's true for each and every name in a program.
>>>>
>>>>
>>>>
>>>>>I don't hold data attributes to such
>>>>>standards
>>>>
>>>>Too bad for you.
>>>>
>>>Thank you.
>>
>>For what ? You "don't hold data attributes to such standard". As a
>>consequence (and as a result of not having addressed the problem
>>sooner), you end up fixing 27000 lines of code. Too bad for you then.
>>Some would haved learned from the mistake, which then would have turned
>>into experience. Seems more and more obvious that you prefer to come
>>whining here instead and blaming the language for your own errors. Too
>>bad for you again.
>>
> 
> That's just silly. 

Yes, agreed. My fault. Let's take it back and not start fighting because
of a misunderstanding.

(snip)

>>>>>and I imagine many developers don't either and least based on
>>>>>other people's code I've read. Plus there are many occassions when
>>>>>attributes are not intended to be APIs,
>>>>
>>>>Then mark them as being implementation (ie : prefix them with a single
>>>>underscore).
>>>
>>>I thought that too was unpythonic.
>>
>>It's a well-known convention, as you would have noticed if you had read
>>some existing python code, read PEP8, or just looked/asked for Python
>>naming conventions. FWIW, that's one of the first thing I learned with
>>Python.
>>
> 
> I know. Many people still think the private/public classification is
> unpythonic.

I'd rather say that many people thinks that language-enforced access
restrictors and the "public methods/private data" scheme are unpythonic.
 And that if you're not sure wether a given name should be API or
implementation then make it part of the API - but then choose a
meaningful name for it !-)


>>>>>but eventually become one.
>>>>>After all most data attributes are created with the purpose of serving
>>>>>methods.
>>>>
>>>>Nope. You have the class API, and the class implementation. Both made of
>>>>both callable and non-callable attributes.
>>>>
>>>
>>>Or objects have state and behavior. Data attributes represent state and
>>>methods represent behavior.
>>
>>Is a getter "behaviour" ? If yes, how does this differ from public
>>"data" attributes from a semantic POV ?
>> 
> Yes, a getter is a behavior. It instructs the object to perform an
> action. All methods are message channels to an object. 

Ok. Python's name lookup all go thru the __getattribute__ accessor. So
 obj.value
is a synonym for
 obj.__getattribute__('value')

which, according to your definition, is behaviour. Hence, there's *no*
"data attribute" in Python.

> A data attribute
> does not perform any action. It just holds data for an object.

The attribute itself may not perform any action, but looking up the name
is an action.

> Even
> though methods are attributes, attributes aren't necessarily methods.

Indeed.

> This is basic OO, I don't see anything semantically complex about it.

It becomes complex when the languages sees functions as first-order
objects supporting the Callable interfaces and let you define your own
callable objects, and sees attribute-access as call to an hidden (well,
not that much hidden) getter/setter mechanism that one as all lattitude
to customize in many ways. Seeing that way really takes you out of the
Java semantic/mindset (well, at least it did to me, and I guess for at
least a few others).

>>>I'm not new to Python
>>>either.
>>
>>New enough to not understand how Python's object model, semantic and
>>overall philosophy strongly differs from Java/C++.
>>
> 
> This isn't an issue about object models and semantics, this is a
> design/architecture issue with regards to eliminating the chances of
> errors in large source code.

I was talking about the way you IMHO try to force-fit the common
"private data/public method" scheme (and it's associated dichotomy
between data and functions) into Python. Of course callable and
non-callable attributes usually have different usages, but we're really
far from the Java model where's the dichotomy between "data" and "code"
is irremediable.

Now wrt/ your naming problem, I think that you would not have done the
mistake if you had apply has much care to the naming of a non-callable
attribute as you assert (and I don't doubt you on this) you would have
for a callable one. You explain that you failed to do so because of you
viewing "data attributes" (ie non-callable attributes) as fundamentally
different from "methods" (ie callable attributes), so I try to share the
POV that Python is really different here, hoping this will help you
detect similar errors sooner.


>>>>If you intented 'tmp' to be part of the API, then you're responsible for
>>>>the bad naming.
>>>
>>>I never intended it to be part of the API.
>>
>>So you should not have used it outside the class.
>>
> The fact that I did, means I should have.

Then if you felt it finally had to be part of the API *and* was badly
named, you should have renamed it immediatly.

But granted, it's easier to say so afterward than to make the right
choice in the moment.

> 
>>>It evolved to be an
>>>important part of the system.
>>
>>Then you had plenty of occasions to address the problem.
>> 
> 
> And how did you come up with that?

By understanding "evolved" as a process (implying a timeline), and
"important part of the system" as widely used.

(snip fighting part)

>>>I only just noticed because I was trying to implement
>>>a plug-in system and I needed to expose an important class. I would not
>>>have considered the issue a problem without this requirement. Except
>>>you are a prophet, you sometimes can't know before hand how certain
>>>classes will end up being used especially if requirements suddenly
>>>change.
>>
>>Yes, that's something I'm well aware of. And that's why I pay attention
>>to naming and to interface/implementation separation.
>>
> 
> Good for you.

That doesn't mean I get it right.

(snip, idem)


-- 
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in 'onurb at xiludom.gro'.split('@')])"



More information about the Python-list mailing list