Accessors in Python (getters and setters)

mystilleef mystilleef at gmail.com
Thu Jul 13 10:38:35 EDT 2006


Bruno Desthuilliers wrote:
> 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('@')])"

Okay, I feel I need to make myself clear. I certainly I'm not blaming
Python for my mistakes. And I don't think language X is better than
Python or vice-versa. Okay scrap the vice-versa. It was silly of me to
name the variable tmp regardless of whatever excuses I have. This also
doesn't mean in the future I wouldn't use shitty names for my
attributes. :-) I most likely will. But at least now I know how to
minimize its impact of such carelessness. I mentioned them above but I
repeat it hear again.

1). Make all attributes of a class private/protected .
2). If a "non-callable" attribute is going to be used outside a class,
think about making it a property and name the property well, because
you never know...

Other than that we are just arguing semantics of language. For example,
your view of objects is by categorizing its attributes in callable and
non-callable. However, my categorization is state(data) and
behavior(methods). Neither ways of thinking about it is wrong. It just
reflects the way we design classes. When I'm coding I certainly don't
care about how Python accesses tmp. What I do care about is how to
change tmp without breaking code left, right and center and the
facilities Python provides for making my code robust. Most other OO
languages provide accessors in addition to keywords for that kind of
stuff. I knew it wasn't Pythonic, but I wanted to know the Pythonic way
to do it. So I asked. My query does not automatically suggest Python
sucks. Neither those it suggest that Java or other OO languages are
better. I asked because I sincerely wanted to know the Pythonic way
handling issues like that.




More information about the Python-list mailing list