Will python never intend to support private, protected and public?

El Pitonero pitonero at gmail.com
Tue Oct 4 11:32:18 EDT 2005


Paul Rubin wrote:
>
> Let's see, say I'm a bank manager, and I want to close my cash vault
> at 5pm today and set its time lock so it can't be opened until 9am
> tomorrow, including by me.  Is that "handcuffs"?  It's normal
> procedure at any bank, for good reason. It's not necessarily some
> distrustful policy that the bank CEO set to keep me from robbing the
> bank.  I might have set the policy myself.  Java lets me do something
> similar with instance variables.  Why is it somehow an advantage for
> Python to withhold such a capability?

If so, you would probably be the type of person that also likes static
typing, type safety and variable declaration, right? You like your
language with extra baggages to improve some safety concerns, which
most Python programmers don't seem to need.

>   def countdown():
>     n = 3
>     while n > 0:
>        yield n
>   g = countdown()
>   print g.next()  # 3
>   print g.next()  # 2
>
> where's the Python feature that lets me modify g's internal value of n
> at this point?

You missed the point. I have, for fun, built a GUI application in
Python, while the program is running. I just kept on adding more and
more code to it. This, while the Python program is running. I was able
to change the GUI's look-and-feel, add more buttons, menus, all while
the programming is running. I was able to change the class definition,
preserve the previous object state variables. For that, you already
have event-based program and can use module reload and some metaclass
tricks to automatically relink your objects to new classes. Sure,
Python is not as friendly as Lisp/Scheme for interactive programming,
but you can still do a lot.

> [Other stuff incomprehensible and snipped].

Sigh, I gave you the keywords: containment and aggregation.

There are two ways of enhancing functionality from an existing
object/class. One way is by inheritance, that's aggregation. Another
way is by containment, that means that instead of inheriting, you add
the additional features as an object contained in the new object.

Vault encapsulation is one way to do OOP. But by no means it is the
only way. The whole access level thing (the "private" keyword) is not
an essential part of OOP. The concept of a "private" namespace is not
necessary for OOP. Just because you learned OOP from one particular
school of thought, does not mean that that's the only way. Let us call
your OOP "school A".

Another way of OOP is to accept by default that everything in the class
hierarchy is inheritable. And Python is by no means the only language
that does that. Now, obviously in this type of OOP you can run into
name collision. But if you actually follow this other school of
thought, you would organize your variables differently. Let us call
this type of OOP "school B".

Let me be more clear: when you have variables that are so, so, so
private that no one else should touch, from school B's point of view,
those variables do not belong to the object. They belong to another
object.

Let us say there is a financial instrument that pays some fixed coupon
interest:

class Bond:
    volatility = None
    interest = None
    def __init__(self):
        self.volatility = 0.3 # volatility from credit risk
        self.interest = 0.4

Now, you have another instrument that pays variable interest:

class VariableInterestBond(Bond):
    volatility = None # this one collides with the base class
    def __init__(self):
         Bond.__init__(self)
         self.volatility = 0.2 # volatility for variable interest
    def calculate(self):
         interest = self.get_variable_interest()
         ...
    def get_variable_interest(self):
         return self.interest * (1 + random.random()*self.volatility)
    ...

In this example, the subclass's "volatility" meant something else but
collides with the base class's "volatility". It should have been a
private variable, but now it accidentally overwrote an existing
variable.

We are trying to combine two "features" in the hierarchy tree:


Bond
|
|
+----- Variable Interest
|
|
Variable-Interest Bond

There are two ways to add the "variable interest" feature to the "bond"
object. One way is by aggregation (inheritance), which is shown above.
Another way is by containment.

If the subsclass's "volatility" should be private and encapsulated from
the main class hierarchy (i.e. Bond-like objects), then from school B's
point of view, it does not belong to the bond object. It would be
better to encapsulate it into another object.

class VariableInterest:
    volatility = None
    def __init__(self, interest):
        self.interest = interest
        self.volatility = 0.2
    def get_variable_interest(self):
         return self.interest * (1 + random.random()*self.volatility)

class VariableInterestBond(Bond):
    variable_interest_calculator = None
    def __init__(self):
         Bond.__init__(self)
         self.variable_interest_calculator =
VariableInterest(self.interest)
    def calculate(self):
         interest =
self.variable_interest_calculator.get_variable_interest()
         ...

That's one way of implementing it. Notice the name collision is gone.
It is gone because you have properly organized your concepts and your
objects.

If you start to follow more along school B's way of thinking, you
actually can eliminate the subclass, too.

class Bond:
    ...
    def set_variable_rate(self):
         self.variable_interest_calculator =
VariableInterest(self.interest)
    ...

That is, by moving the features from aggregation into containment,
often the depth of class inheritance tree can be shortened, too.

--------------------------------

So, if you really think along the lines of school B, the chance of name
collision reduces greatly. Because in your mind the state variables of
the objects are more important/meaningful than in the case of school A.
In school A you would often clutter the object's namespace with
auxiliary variables. In school B you move the clutter to somewhere
else.

As for Python's double leading underscores, it's just a short-term
"hardware" solution to name collision. In my opinion, a "software" code
refactoring is the long-term solution.




More information about the Python-list mailing list