is python Object oriented??
bruno.42.desthuilliers at websiteburo.invalid
Wed Feb 4 11:12:40 CET 2009
Steven D'Aprano a écrit :
> 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.
While it's quite clear that a compiler for a statically typed language
can catch at least some type errors, this is still not what a compiler
is for. A compiler is here to compile you source code into a suitable
representation for the execution environment. And FWIW, some compilers
are rather weaker than Python when it comes to "protecting you from
>>> 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),
Oh, you meant "type error". But this is a programming error, not "bad
data". "bad data" is something you get (usually from the outside world)
at runtime only. Well, the way I understand it, at least.
> 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.
Ok, let's see. What happens if you pass 0.0 to the function as-is ? Yes,
indeed, you get a runtime error. Now what happens if you add error
handling to the function itself ? What will the error handler do ? Yes,
raise a runtime error. So far, I don't see any gain here. No, no, don't
answer yet. Sure, what you need here is a compiler that can inspect the
whole code source using this function to make sure it is always properly
invoked. Mmm, wait - will this work with shared libs ? I'm afraid not.
Ok, so you have to change the specification to : "input must be a
non-zero-float", and use the type system to define non-zero-float type.
> Smart compilers turn some runtime errors into compile-time errors.
> 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.
Indeed. And that's fine : if we want maximum type safety and
as-possibly-proved code, we know which tool to use. Now good luck doing
web development, system admin, app scripting and quite a lot of other
things (well, perhaps more that 80% of ordinary application programming)
Can you understand that not everybody needs maximum type safety /
correctness proof etc for all his code ? That all this "security" stuff
comes with a price that may not be worth paying ?
> To bring it back to private/public attributes, side-effects make
> correctness proofs difficult. Modifications of attributes are side-
Then use a functional language !-)
> When attributes are subject to being modified by arbitrary code,
> it is harder to reason about the correctness of the code:
Indeed. Not the right tool, obviously. OTHO, it makes it way easier to
use as glue between different, sometimes not highly compatible components.
> 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:
Paranoiacs (also known as control-freaks) are not happy using Python.
They usually prefer Ada or, at least, Java.
> def invert(self):
> x = self.x
> if isinstance(x, float) and x:
> return 1.0/self.x
> raise MyError('post-condition x a non-zero float violated')
The correct solution is indeed to make x a property with a sanity check
in the setter - in which case you'll have a useful traceback - or as a
read-only property, or even better to just declare it as implementation
(naming it _x).
> (2) Hope the error never occurs, and if it does, let the caller deal with
If the attribute is either a read-only property or an implementation
attribute, then the guy that messed with it is responsible for the
possible consequences. That's the contract, yes.
> Hopefully you aren't your own caller.
I'm usually the first user of my own libs, and my coworkers comes
immediatly after. IOW, I eat my own dogfood, thanks.
> That's often the Python approach.
As I explained above, the "Python approach" is actually a bit different.
In your example code, you explicitely declared x as a *public*
attribute. Which is not wise - whatever the language - if you have to
maintain invariants on this attribute. Make it either an implementation
attribute or a read-only property.
(snip usual stuff about nuclear disasters, x-ray-overdosed patients etc)
> 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.)
Yes. Because *everything* else happens at runtime. Remember, even "def"
and "class" are executable statements.
> 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.
Would you then say that Ruby has "real privates" attributes ?
> 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).
More information about the Python-list