OO in Python? ^^

M

Matthias Kaeppler

Hi,

sorry for my ignorance, but after reading the Python tutorial on
python.org, I'm sort of, well surprised about the lack of OOP
capabilities in python. Honestly, I don't even see the point at all of
how OO actually works in Python.

For one, is there any good reason why I should ever inherit from a
class? ^^ There is no functionality to check if a subclass correctly
implements an inherited interface and polymorphism seems to be missing
in Python as well. I kind of can't imagine in which circumstances
inheritance in Python helps. For example:

class Base:
def foo(self): # I'd like to say that children must implement foo
pass

class Child(Base):
pass # works

Does inheritance in Python boil down to a mere code sharing?

And how do I formulate polymorphism in Python? Example:

class D1(Base):
def foo(self):
print "D1"

class D2(Base):
def foo(self):
print "D2"

obj = Base() # I want a base class reference which is polymorphic
if (<need D1>):
obj = D1()
else:
obj = D2()

I could as well leave the whole inheritance stuff out and the program
would still work (?).

Please give me hope that Python is still worth learning :-/

Regards,
Matthias
 
H

Heiko Wundram

Matthias said:
<snip a whole lot of talk of someone still thinking in terms of C>

Let this enlighten your way, young padawan:

modelnine@phoenix ~/gtk-gnutella-downloads $ python
Python 2.4.2 (#1, Oct 31 2005, 17:45:13)
[GCC 3.4.4 (Gentoo 3.4.4-r1, ssp-3.4.4-1.0, pie-8.7.8)] on linux2
Type "help", "copyright", "credits" or "license" for more information.The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
And, _do_ (don't only read) the tutorial, and you'll understand why the
short example code you posted isn't pythonic, to say the least:
http://www.python.org/doc/2.4.2/tut/tut.html
and why inheritance in Python is necessary, but on a whole different level
of what you're thinking.

Oh, and on a last note, if you're german, you might as well join
de.comp.lang.python.

--- Heiko.
 
B

Brian Beck

Matthias said:
class Base:
def foo(self): # I'd like to say that children must implement foo
pass

def foo(self):
raise NotImplementedError("Subclasses must implement foo")

Now calling foo on a child instance will fail if it hasn't implemented foo.
And how do I formulate polymorphism in Python? Example:

class D1(Base):
def foo(self):
print "D1"

class D2(Base):
def foo(self):
print "D2"
obj = Base() # I want a base class reference which is polymorphic
if (<need D1>):
obj = D1()
else:
obj = D2()

I have no idea what you're trying to do here and how it relates to
polymorphism.
 
H

Heiko Wundram

Brian said:
I have no idea what you're trying to do here and how it relates to
polymorphism.

He's translating C++ code directly to Python. obj = Base() creates a
variable of type Base(), to which you can assign different object types (D
(), D2()) which implement the Base interface (are derived from Base).
Err... At least I think it's what this code is supposed to mean...

In C++ you'd do:

Base *baseob;

if( <i want d1> ) {
baseob = (Base*)new D1();
} else {
baseob = (Base*)new D2();
}

baseob->foo();

(should, if foo is declared virtual in Base, produce "d1" for D1, and "d2"
for D2)

At least IIRC, it's been quite some time since I programmed C++... ;-)
*shudder*

--- Heiko.
 
T

Tony Nelson

Matthias Kaeppler said:
obj = Base() # I want a base class reference which is polymorphic

obj now refers to an instance of Base.
if (<need D1>):
obj = D1()

obj now refers to an instance of D1(). The Base instance is
unreferenced.
else:
obj = D2()

obj now refers to an instance of D2(). The Base instance is
unreferenced.

Note that there is no code path that results in obj still referring to
an instance of Base. Unless making a Base had side effects, there is no
use in the first line.

I could as well leave the whole inheritance stuff out and the program
would still work (?).

That program might.

Please give me hope that Python is still worth learning :-/

Python has inheritance and polymorphism, implemented via dictionaries.
Python's various types of namespace are implemented with dictionaries.

Type this in to the Python interpreter:

class Base:
def foo(self):
print 'in Base.foo'

class D1(Base):
def foo(self):
print 'in D1.foo'
Base.foo(self)

class D2(Base):
def foo(self):
print 'in D2.foo'
Base.foo(self)

def makeObj():
return needD1 and D1() or D2()

needD1 = True
makeObj().foo()

needD1 = False
makeObj().foo()
________________________________________________________________________
TonyN.:' *firstname*nlsnews@georgea*lastname*.com
' <http://www.georgeanelson.com/>
 
M

Martin Christensen

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Matthias> sorry for my ignorance, but after reading the Python
Matthias> tutorial on python.org, I'm sort of, well surprised about
Matthias> the lack of OOP capabilities in python. Honestly, I don't
Matthias> even see the point at all of how OO actually works in
Matthias> Python.

It's very common for Python newbies, especially those with backgrounds
in languages such as C++, Java etc. to not really 'get' the Python way
of handling types until they've had a fair amount of experience with
Python. If you want to program Pythonically, you must first unlearn a
number of things.

For instance, in e.g. the Java tradition, if a function needs a
triangle object, it'll take a triangle object as an argument. If it
can handle any type of shape, it'll either take a shape base class
instance as an argument or there'll be some kind of shape interface that
it can take. Argument types are strictly controlled. Not so with
Python. A Python solution will typically take any type of object as an
argument so long as it behaves as expected, and if it doesn't, we deal
with the resulting exception (or don't, depending on what we're trying
to accomplish). For instance, if the function from before that wants a
shape really only needs to call an area method, anything with an area
method can be used successfully as an argument.

Some have dubbed this kind of type check 'duck typing': if it walks
like a duck and quacks like a duck, chances are it'll be a duck. To
those who are used to (more or less) strong, static type checks, this
will seem a reckless approach, but it really works rather well, and
subtle type errors are, in my experience, as rare in Python as in any
other language. In my opinion, the tricks the C*/Java people
occasionally do to get around the type system, such as casting to the
fundamental object type, are worse because they're seldom expected and
resulting errors thus typically more subtle.

In my very first post on this news group a number of years ago, I
asked for an equivalent of Java's interfaces. The only reply I got was
that I didn't need them. While the reason was very obvious, even with
what I knew about Python, it still took a while to sink in. From what
I can tell, you're in somewhat the same situation, and the two of us
are far from unique. As I said in the beginning, Python newbies with a
background in statically typed languages typically have a lot to
unlearn, but in my opinion, it's well worth it.


Martin
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)
Comment: Using Mailcrypt+GnuPG <http://www.gnupg.org>

iEYEARECAAYFAkOba8oACgkQYu1fMmOQldXzcgCg0JEGTEG7xC/yAx8C1VFO8H1R
LWwAnRJ8AxHBe8YoHcDC5oGRfYaPHTfX
=HdTR
-----END PGP SIGNATURE-----
 
P

Paul Boddie

Well, unless you are (or he is) in with the GNOME crowd, C probably
isn't really the object-oriented language acting as inspiration here.

[Zen of Python]

Of course the ZoP (Zen of Python) is deep guidance for those
languishing in some design dilemma or other, but not exactly helpful,
concrete advice in this context. (I'm also getting pretty jaded with
the recent trend of the ZoP being quoted almost once per thread on
comp.lang.python, mostly as a substitute for any real justification of
Python's design or any discussion of the motivations behind its
design.) That said, the questioner does appear to be thinking of
object-oriented programming from a statically-typed perspective, and
I'd agree that, ZoP or otherwise, a change in perspective and a
willingness to accept other, equally legitimate approaches to
object-orientation will lead to a deeper understanding and appreciation
of the Python language.

Anyway, it appears that the questioner is confusing declarations with
instantiation, amongst other things:
And how do I formulate polymorphism in Python? Example:

class D1(Base):
def foo(self):
print "D1"

class D2(Base):
def foo(self):
print "D2"

obj = Base() # I want a base class reference which is polymorphic

Well, here one actually gets a reference to a Base object. I know that
in C++ or Java, you'd say, "I don't care exactly what kind of Base-like
object I have right now, but I want to be able to hold a reference to
one." But in Python, this statement is redundant: names/variables
potentially refer to objects of any type; one doesn't need to declare
what type of objects a name will refer to.
if (<need D1>):
obj = D1()
else:
obj = D2()

Without the above "declaration", this will just work. If one needs an
instance of D1, one will assign a new D1 object to obj; otherwise, one
will assign a new D2 object to obj. Now, when one calls the foo method
on obj, Python will just find whichever implementation of that method
exists on obj and call it. In fact, when one does call the method, some
time later in the program, the object held by obj doesn't even need to
be instantiated from a related class: as long as the foo method exists,
Python will attempt to invoke it, and this will even succeed if the
arguments are compatible.

All this is quite different to various other object-oriented languages
because many of them use other mechanisms to find out whether such a
method exists for any object referred to by the obj variable. With such
languages, defining a base class with the foo method and defining
subclasses with that method all helps the compiler to determine whether
it is possible to find such a method on an object referred to by obj.
Python bypasses most of that by doing a run-time check and actually
looking at what methods are available just at the point in time a
method is being called.
I could as well leave the whole inheritance stuff out and the program would still work
(?).

Correct. Rewinding...
Does inheritance in Python boil down to a mere code sharing?

In Python, inheritance is arguably most useful for "code sharing", yes.
That said, things like mix-in classes show that this isn't as
uninteresting as one might think.

Paul
 
H

Heiko Wundram

Paul said:
Well, unless you are (or he is) in with the GNOME crowd, C probably
isn't really the object-oriented language acting as inspiration here.

Pardon this glitch, I corrected it in a followup-post somewhere along the
line, it's been some time since I've last used C/C++ for more than just
Python module programming and as such the term C has come to be synonymous
to "everything not Python" for me. ;-)
[Zen of Python]

I find pointing to the ZoP pretty important, especially for people who start
to use the language. I know the hurdle that you have to overcome when you
grew up with a language which forces static typing on you (I learnt Pascal
as my first language, then used C, C++ and Java extensively for quite some
time before moving on to Perl and finally to Python), and when I started
using Python I had just the same feeling of "now why doesn't Python do this
like C++ does it, I loose all my security?" or something similar.

What got me thinking was reading the ZoP and seeing the design criteria for
the language. That's what actually made me realize why Python is the way it
is, and since that day I am at ease with the design decisions because I can
rationally understand and grip them and use the design productively. Look
at namespaces: I always understood what a namespace was (basically a
dictionary), but it took Tim Peters three lines

"""
Simple is better than complex.
Flat is better than nested.
Namespaces are one honking great idea -- let's do more of those!
"""

to actually get a hint at what the designer thought about when he
implemented namespaces as they are now, with the simplicity that they
actually have. It's always better to follow the designers thoughts about
something he implemented than to just learn that something is the way it is
in a certain language.

I still have that awkward feeling for Perl. TIMTOWTDI just doesn't cut it
when it's yelled at me, I still can't see a single coherent vision which
Larry Wall followed when he designed the language. That's why I decided to
drop it. ;-)

Maybe I'm assuming things by thinking that others also follow my line of
thought, but I've actually had very positive responses so far when telling
people that a certain feature is a certain way and then pointing them to
the ZoP, they all pretty much told me after a certain time of thought that
"the decision made sense now."

--- Heiko.
 
M

Matthias Kaeppler

Heiko said:
He's translating C++ code directly to Python. obj = Base() creates a
variable of type Base(), to which you can assign different object types (D
(), D2()) which implement the Base interface (are derived from Base).
Err... At least I think it's what this code is supposed to mean...

In C++ you'd do:

Base *baseob;

if( <i want d1> ) {
baseob = (Base*)new D1();
} else {
baseob = (Base*)new D2();
}

baseob->foo();

(should, if foo is declared virtual in Base, produce "d1" for D1, and "d2"
for D2)

At least IIRC, it's been quite some time since I programmed C++... ;-)
*shudder*

Yes, that's what I tried to express (the cast to Base* is redundant here
by the way, since D1/D2 are also of type Base; you can always hold a
base class pointer to derived types without type conversion).

I have also read the other answers to my question, and I am really sorry
if I have sounded ignorant in my post, but it's harder than I thought to
move to a language where all these techniques one had learned in years
of hard work suddenly become redundant :)
I'm so used to statically typed languages that the shift is very
confusing. Looks as if it isn't as easy to learn Python afterall, for
the mere reason of unlearning rules which don't apply in the world of
Python anymore (which seem to be quite a lot!).

Regards,
Matthias
 
M

Matthias Kaeppler

Brian said:
def foo(self):
raise NotImplementedError("Subclasses must implement foo")

That's actually a good idea, though not as nice as a check at
"compile-time" (jesus, I'm probably talking in C++ speech again, is
there such a thing as compile-time in Python at all?!)

Another thing which is really bugging me about this whole dynamically
typing thing is that it seems very error prone to me:

foo = "some string!"

# ...

if (something_fubar):
fo = "another string"

Oops, the last 'o' slipped, now we have a different object and the
interpreter will happily continue executing the flawed program.

I really see issues with this, can anyone comment on this who has been
working with Python more than just a day (like me)?

Regards,
Matthias
 
T

Torsten Bronger

Hallöchen!

Matthias Kaeppler said:
[...]

Another thing which is really bugging me about this whole
dynamically typing thing is that it seems very error prone to me:

foo = "some string!"

# ...

if (something_fubar):
fo = "another string"

Oops, the last 'o' slipped, now we have a different object and the
interpreter will happily continue executing the flawed program.

I really see issues with this, can anyone comment on this who has
been working with Python more than just a day (like me)?

There are even a couple of further checks which don't happen
(explicitly) in a dynamic language like Python, and which do happen
in most statically typed languages like C++. And yes, they are a
source of programming mistakes.

However, in everyday programming you don't feel this. I don't make
more difficult-to-find mistakes in Python than I used to make in my
C++ code. But what you do feel is the additional freedom that the
dynamic approach gives to you.

Basically it's a matter of taste and purpose whether you want to be
controlled heavily or not. Python is particularly liberal, which I
appreciate very much.

Tschö,
Torsten.
 
E

Ernst Noch

Matthias said:
That's actually a good idea, though not as nice as a check at
"compile-time" (jesus, I'm probably talking in C++ speech again, is
there such a thing as compile-time in Python at all?!)

Another thing which is really bugging me about this whole dynamically
typing thing is that it seems very error prone to me:

foo = "some string!"

# ...

if (something_fubar):
fo = "another string"

Oops, the last 'o' slipped, now we have a different object and the
interpreter will happily continue executing the flawed program.

I really see issues with this, can anyone comment on this who has been
working with Python more than just a day (like me)?

Regards,
Matthias

Matthias,

maybe this article is of interest for you:
http://www.mindview.net/WebLog/log-0025

Regards,
Ernst
 
S

Steven D'Aprano

That's actually a good idea, though not as nice as a check at
"compile-time" (jesus, I'm probably talking in C++ speech again, is
there such a thing as compile-time in Python at all?!)

Yes. Python, like Java, compiles your code into byte code which is then
executed by the Python runtime engine.

Back in the early days of computing the distinction between compiled
languages and interpreted languages might have been meaningful, but not so
much today. There is significant fuzzy overlap between the two.


Another thing which is really bugging me about this whole dynamically
typing thing is that it seems very error prone to me:

foo = "some string!"

# ...

if (something_fubar):
fo = "another string"

Oops, the last 'o' slipped, now we have a different object and the
interpreter will happily continue executing the flawed program.

Yes, this is one specific error which the Python compiler won't pick up
for you. However, it will pick up the related error:

foo = "some string!"
# ...
if (something_fubar):
bar = fo # should be foo

assuming there isn't some other name fo which already exists.


I really see issues with this, can anyone comment on this who has been
working with Python more than just a day (like me)?

Python works well with test-driven development. Test-driven development
will pick up this sort of error, and many other errors too, with less
effort and more certainty than compile-time checking. The problem with
static typed languages is that they make the programmer do a lot of the
work, just so the compiler can pick up a few extra errors at compile time
rather than at run time.

But since the compiler can't save you from run time errors, you still
should be doing test-driven development. But if you are doing test-driven
development, the value of the static type checking is rather low.

After all, "does it compile?" is only one test out of many, and really the
least important. Lots of code compiles that doesn't work; however no code
that works can possibly fail to compile.

Having said that, static type checking is not utterly useless, and rumour
has it that Python may some day include some version of it.

Oh, if you are tempted to fill your Python code with manual type checks,
using type() or isinstance(), I suggest that you learn about "duck typing"
first. Otherwise known as "latent typing".
 
B

bonono

Steven said:
Python works well with test-driven development. Test-driven development
will pick up this sort of error, and many other errors too, with less
effort and more certainty than compile-time checking. The problem with
static typed languages is that they make the programmer do a lot of the
work, just so the compiler can pick up a few extra errors at compile time
rather than at run time.
Any language would be benefited from test-driven development, python
needs it because of its dynamic nature.

And I don't think Haskell make the programmer do a lot of work(just
because of its static type checking at compile time).
 
C

Chris Mellon

That's actually a good idea, though not as nice as a check at
"compile-time" (jesus, I'm probably talking in C++ speech again, is
there such a thing as compile-time in Python at all?!)

Another thing which is really bugging me about this whole dynamically
typing thing is that it seems very error prone to me:

foo = "some string!"

# ...

if (something_fubar):
fo = "another string"

Oops, the last 'o' slipped, now we have a different object and the
interpreter will happily continue executing the flawed program.

I really see issues with this, can anyone comment on this who has been
working with Python more than just a day (like me)?

You are totally correct and this does cause errors. However, I'd like
you to take a few minutes and go back over all your C and C++ and Java
code and make note of how many lines of code and how many complicated
constructs you've used over the years to subvert the type system in
those languages. In my experience, and in many other peoples
experience, dynamic ducky typing saves far more work than it costs,
even when you factor in "typo bugs" which would be prevented by static
typing. Thats not even counting bugs in the extra code you've written
to work around the compiler. In fact, I'd say that a signifigant
portion of my time in writing C++ code is spent convincing the
compiler to do what I want, and not figuring out what I want. In
Python, the opposite is true. My experience in that regard is pretty
typical - you'll find scores of stories on the web of C++ programmers
who switched to Python and breathed a sigh of relief.

All that said, there are automated checkers that can assist in
avoiding these kind of
bugs (as well as several others). Look into PyChecker and PyLint.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,000
Messages
2,570,252
Members
46,848
Latest member
CristineKo

Latest Threads

Top