is python Object oriented??
Steven D'Aprano
steven at REMOVE.THIS.cybersource.com.au
Wed Feb 4 02:10:26 EST 2009
On Tue, 03 Feb 2009 12:09:46 +0100, Bruno Desthuilliers wrote:
>> I love Python, and I'm greedy and want it all: I want a dynamic,
>> easy-to- use language *and* a compiler that can protect me from myself
>
> That's not what compilers are for.
So you say.
>> and bad data.
>
> I definitly fail to see how a compiler could protect you from *runtime*
> issues ???
I'm surprised you can't. Any time a compiler prevents an error by
detecting it at compile-time (say, attempting to store a 96-bit float
into the memory space allocated for a 32-bit int), it has prevented a
runtime error.
Perhaps what you are thinking is that I meant compilers can protect you
from "all" runtime issues. That would be silly. Although, in principle,
we can get close, for some definition of "close".
Here's a toy function, and a specification:
def inverse(x):
return 1.0/x
Specification: input must be a float, output must be a float.
Is that function correct? No, because it will raise an exception if
x==0.0. Python's compiler is dumb and will allow you to write incorrect
functions, but a smart compiler could look at that function and see that
it was incorrect. The fix would then be to change the code, or the
specification, or both.
Smart compilers turn some runtime errors into compile-time errors. The
earlier you catch the error, the easier it is to fix it.
Now, that's a toy example. Languages like Ada make correctness proofs,
well, perhaps not easy, but merely difficult compared to impossible for
languages like Python.
To bring it back to private/public attributes, side-effects make
correctness proofs difficult. Modifications of attributes are side-
effects. When attributes are subject to being modified by arbitrary code,
it is harder to reason about the correctness of the code:
class Inverter(object):
def __init__(self, x):
# Pre-condition: x is always a float
if x:
self.x = x
else:
raise ValueError("x must not be zero")
# Post-condition: if self.x exists, it is a non-zero float
def invert(self):
return 1.0/self.x
Is method invert correct?
No, it is not, because the invariant that self.x is non-zero may have
been broken by some arbitrary piece of code elsewhere. Our ability to
reason about the correctness of the code is weakened significantly.
Sticking an underscore in front of x does not help: there's nothing to
stop some arbitrary function elsewhere changing _x to zero.
There are two usual approaches to dealing with that problem which are
available to Python programmers:
(1) Paranoia. Make the developer responsible for checking everything all
the time:
def invert(self):
x = self.x
if isinstance(x, float) and x:
return 1.0/self.x
else:
raise MyError('post-condition x a non-zero float violated')
You can use decorators to decrease the amount of boilerplate, but you
have to remember to use the decorators; or you can use a metaclass to
automatically apply decorators, but that's hard and not very flexible.
Whichever way you do it, you're still responsible, and whatever way, you
still have to pay the performance penalty.
This also turns an unexpected exception into an expected exception, but
is otherwise rather useless. It doesn't tell you when the post-condition
was violated, or by what function, and that's the information you need in
order to fix the bug.
(2) Hope the error never occurs, and if it does, let the caller deal with
it. Hopefully you aren't your own caller.
That's often the Python approach. I've already written about the glorious
freedom such an approach gives the developer. I'm not being sarcastic:
even if you are your own caller, you get to put off worrying about a bug
which might never happen. This is a great strategy when the worst thing
that a bug will do is display an icon in the wrong position. Fix it in
the next release.
But that's not a viable strategy when the consequences of a bug might
include giving the patient 20,000 rads of radiation instead of 200:
http://www.ccnr.org/fatal_dose.html
There is a third strategy, sadly not available to Python programmers:
prevention by catching potential errors at compile-time. (Python does
catch some errors at compile-time, but only syntax errors.) If x can only
be modified in a few places, (that is, it is effectively hidden or
private) then it is much easier to reason about program correctness,
avoid bugs, and debug those bugs which do occur.
No, this is not a panacea which will prevent every imaginable bug. Nor is
it a replacement for unit-testing (which also does not prevent every
imaginable bug). It is a compliment to unit-testing.
Is it subject to over-use or misuse? Of course it is. So are properties,
and decorators, and metaclasses, and OO, and Singletons, and, well,
everything. Let's not close our eyes to *either* the costs or the
benefits, and let's not limit what Python might become in the future.
Python 3 has type annotations and Guido is encouraging people to
experiment with type systems. This was unthinkable a year or two ago.
What might Python 4000 bring?
--
Steven
More information about the Python-list
mailing list