is python Object oriented??

Steven D'Aprano steven at REMOVE.THIS.cybersource.com.au
Wed Feb 4 01:51:46 CET 2009


On Tue, 03 Feb 2009 10:20:28 +0100, Thorsten Kampe wrote:


>> If a "private" keyword (or equivalent) were available, then the change
>> would need to be made in only one location rather than at every
>> occurrence off the identifier. That is much less error prone. Sure, you
>> can argue (as I'm sure someone will) that the users of the library
>> should be more careful. Yes they should, but why not let the language
>> help with such low-level details. That's what high-level languages are
>> for.
> 
> This scenario is highly "supposing" and doesn't look like a real-world-
> case to me.

You think that private attributes never become public?

Encoding a "private" flag in the name is a violation of Once And Only 
Once. Although the OAOO principle is generally referred to when 
discussing code, it is more generic and applies to other things as well. 
You should be able to state an attribute is private *once*, not every 
time you use it.

http://c2.com/cgi/wiki?OnceAndOnlyOnce

On the other hand, encoding that private flag in the name is a variation 
of *sensible* Hungarian Notation, as used by the Microsoft Apps team, not 
the stupid version used by the Windows team.

http://www.joelonsoftware.com/articles/Wrong.html

So that's a point in its favour.



> But anyway: the obvious solution in my humble opinion would
> be to do something like "public_attribute = _private_attribute". But
> that would be too simple, too "unjavaesque", right?!

No, it would be too *faulty*. It simply doesn't work. Consider:

class Parrot(object):
    _count = 0
    def foo(self):
        print 'foo'
        self._count += 1
    def bar(self):
        print "You have called foo %d times" % self._count


This works as expected:

>>> p = Parrot()
>>> p.foo()
foo
>>> p.foo()
foo
>>> p.bar()
You have called foo 2 times


Now you promote count from private to public, using your "obvious 
solution", and try to use it:


class Parrot(object):
    _count = 0
    count = _count
    def foo(self):
        print 'foo'
        self._count += 1
    def bar(self):
        print "You have called foo %d times" % self._count



>>> p = Parrot()
>>> p.foo()
foo
>>> p.count = 99
>>> p.bar()
You have called foo 1 times


Clearly the "obvious" solution simply isn't sufficient to get the result 
that you need. One solution is to just refactor the source code, so that 
the attribute identifier "_count" becomes "count", but beware of all the 
usual issues with source code refactoring.

Another solution is to leave _count as it is, and create a *property* 
called count, not a simple attribute:

count = property(
    lambda self: self._count, 
    lambda self, x: setattr(self, '_count', x),
    None,
    "Public interface to private attribute _count"
    )


but this adds complexity to the class and will be a maintenance headache 
as the class' methods diverge into those that manipulate self._count 
directly and other methods which manipulate self.count.



-- 
Steven



More information about the Python-list mailing list