OO in Python? ^^

D

Damjan

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:

Python IS Object Oriented, since everything is an object in Python,
even functions, strings, modules, classes and class instances.

But Python is also dynamically typed so inheritance and polymorphism,
ideas coming from other languages, are not that much important.
Please give me hope that Python is still worth learning

Python is different than C/C++, Java and co.
If you can pass over it, you'll see for yourself if it's worth learning.
 
D

Donn Cave

[email protected] (Aahz) said:
Right, you can get good genericity with Haskell's typeclasses (I've
posted about that often in the past, and desperately and so far
unsuccessfully tried to convince Guido to use something close to
typeclasses rather than "interfaces" for such purposes as PEP 246
[protocol adaptation]); it's the state of _templates_ in Haskell,
specifically, which I was rather dubious about (it may be that I just
haven't dug into them deep enough yet, but they do seem not a little
"convoluted" to me, so far).

Hrm. I don't recall anything about typeclasses, so my suspicion is that
you were writing something lengthy and above my head. Can you write
something reasonably short about it? (I'm asking partly for your
benefit, because if it makes sense to me, that will likely go a long way
toward making sense to Guido -- we seem to think similarly in certain
ways.)

Couple of points to hopefully augment Alex's response

- A type class may very well provide no implementation at all.
They certainly can, but they are still useful when they don't, and
indeed I think it's fair to say that isn't their main purpose.

- Doesn't hurt to recall that Haskell is a functional language.
Type classes have a superficial and maybe useful similarity to
object classes, but in Haskell, if data objects have methods,
they aren't aware of them, so to speak. If I define a new
type class for some reason, I may make instances for every
data type I can think of -- Int, Bool, (), whatever. Now do
these types suddenly sprout new methods? Not really! I just
have a set of functions that are now defined over all these
types. Outside the scope of these declarations, of course these
functions are unknown, and the data types are the same in either
case.

- If type classes are very commonly written in Haskell applications,
it's news to me. The need to define functions over a set of
data types in this way is relatively unusual - extremely useful
in the core language where you get Eq, Ord, Show and such basic
properties, but I think less needed higher up in the application
hierarchy. That might be an interesting philosophical question,
as a contrast between the basic world views of FP versus OOP, but
of course you'd want to check it with someone with a lot more
Haskell than I have.

Donn Cave, (e-mail address removed)
 
K

Kay Schluehr

Alex said:
Aahz said:
Hrm. I don't recall anything about typeclasses, so my suspicion is that
you were writing something lengthy and above my head. Can you write
something reasonably short about it? (I'm asking partly for your
benefit, because if it makes sense to me, that will likely go a long way
toward making sense to Guido -- we seem to think similarly in certain
ways.)

Think of a typeclass as something "like an interface, more than an
interface" to which a type can easily be "adapted" by a third programmer
even if the two programmers who wrote the type and typeclass were
working separately and with no knowledge of each other -- not too far
from the vaguer idea of "protocol" I support in PEP 246 (which focuses
on the adaptation), except that in Haskell things happen at compile time
while in Python we prefer to avoid the strong distinction between
compile time and runtime.

You may think of a typeclass as akin to an abstract baseclass, because
it's not constrained to only giving the signatures of methods, it can
also supply some default implementations of some methods in terms of
others. Guido blogged in August about interfaces versus ABCs, not
remembering why he had once Pronounced about preferring ABCs, and in his
critique of ABCs he mentions that one weakness of their ability to
provide default implementations is that you have to decide about what is
the most fundamental subset, in whose terms the rest is implemented.
But *typeclasses do away with that need*. Consider (arbitrary
pythonesquoid syntax):

typeclass mapping:
def __getitem__(self, key):
_notthere=[]
result = self.get(key, _notthere)
if result is notthere: raise KeyError
return result
def get(self, key, default):
try: return self[key]
except KeyError: return default
# etc etc

this LOOKS like mutual recursion, but since it's a typeclass it doesn't
mean that: it means __getitem__ may be defined (and then get uses the
provided default implementation unless overridden) OR get may be defined
(and then it's __getitem__ that may use the default implementation
supplied by the the typeclass, or else override it).

When you compile a typeclass you build a directed graph of dependencies
of methods on each other, which may include cycles; when you show how a
type adapts to a typeclass, you build a copy of that graph removing the
dependencies of those methods which do get explicitly implemented (in
the type or in the adapter) -- if the copy at the end of these
compilations still has cycles, or leaves (methods that the typeclass
requires and neither the type nor the adapter supply), then this raises
an exception (incomplete adaptation).

Thus, a typeclass clearly shows the semantics intended for methods that
depend on each other, and conveniently lets you, the adapter's author,
choose what to implement -- the typeclass's author has not been forced
to pick the "most fundamental" methods. ABCs, or extremely handy mixins
such as UserDict.DictMixin, do force a programmer who knows nothing
about the internals of your class (the author of the ABC or mixin) to
pick "most fundamental" methods. Thus, typeclasses are more useful than
ABCs by as much as ABC are more useful than (simply "syntactical")
interfaces -- coupled with adaptation mechanisms, the overall result can
be extremely handy (as any Haskell programmer might confirm).


Alex

I don't see why your typeclass illustration does not apply to ABCs as
well? The sanity checks for typeclasses you describe seem to be a
particular feature of Haskell or other pure functional languages
without destructive updates and messy aliasing problems. It's hardly
believable that this would be possible in Python and runtime checks
won't help much in this case.

By the way I also don't understand Guidos concerns. Maybe he is against
the whole idea of creating frameworks and partial implementations? It's
not very clear for what reasons he likes interfaces.

Kay
 
A

Alex Martelli

Kay Schluehr said:
typeclass mapping:
def __getitem__(self, key):
_notthere=[]
result = self.get(key, _notthere)
if result is notthere: raise KeyError
return result
def get(self, key, default):
try: return self[key]
except KeyError: return default
# etc etc
...
I don't see why your typeclass illustration does not apply to ABCs as

Because in a class, A or B or not, this code WOULD mean mutual recursion
(and it can't be checked whether recursion terminates, in general). In
a typeclass, it means something very different -- a dependency loop.
It's easy to check that all loops are broken (just as easy to check that
all purely abstract methods are implemented) in an adaptation.
well? The sanity checks for typeclasses you describe seem to be a
particular feature of Haskell or other pure functional languages
without destructive updates and messy aliasing problems. It's hardly
believable that this would be possible in Python and runtime checks
won't help much in this case.

Checks would happen at adapter-registration time, which is runtime: but
so is the time at which a 'class' or 'def' statement executes, of
course. Little but syntax-checks happens at compile-time in Python.

Nevertheless the checks would help enormously, and I don't see why you
would think otherwise. Prototypes of this (a metaclass performing the
checks upon inheritance, i.e. non-abstract-subclass creation, but that's
the same algorithm that could be run at adapter registration time if we
got rid of the requirement of inheritance in favor of adaptation) have
been posted to this group years ago. If a programmer does weird dynamic
things to a type or an adapter after the checks, fine, they'll get
exceptions if they later try to call (directly or indirectly) some
method that's disappeared, or the like, just like they would with ABC's,
interfaces, inheritance, or whatever else -- very few types are
dynamically altered in this way in most production programs, anyway, so
I won't lose any sleep over that possibility.
By the way I also don't understand Guidos concerns. Maybe he is against
the whole idea of creating frameworks and partial implementations? It's
not very clear for what reasons he likes interfaces.

Just read his blog on artima -- and maybe, if something there is unclear
to you, comment about it to see if you can get him interested in
explaining better.


Alex
 
K

Kay Schluehr

Alex said:
Because in a class, A or B or not, this code WOULD mean mutual recursion
(and it can't be checked whether recursion terminates, in general). In
a typeclass, it means something very different -- a dependency loop.
It's easy to check that all loops are broken (just as easy to check that
all purely abstract methods are implemented) in an adaptation.

I see. The problem is that a class *should* permit mutual recursion in
general while the semantics of a typeclass is different and a typeclass
instance must resolve the dependency loop introduced by the typeclass.
Checks would happen at adapter-registration time, which is runtime: but
so is the time at which a 'class' or 'def' statement executes, of
course. Little but syntax-checks happens at compile-time in Python.

Nevertheless the checks would help enormously, and I don't see why you
would think otherwise.

I don't think otherwise. I was just asking whether they are feasible.
The typeclass concept itself is very beautiful but it is structural and
I don't see how Python can deal with callgraph structures.
Prototypes of this (a metaclass performing the
checks upon inheritance, i.e. non-abstract-subclass creation, but that's
the same algorithm that could be run at adapter registration time if we
got rid of the requirement of inheritance in favor of adaptation) have
been posted to this group years ago. If a programmer does weird dynamic
things to a type or an adapter after the checks, fine, they'll get
exceptions if they later try to call (directly or indirectly) some
method that's disappeared, or the like, just like they would with ABC's,
interfaces, inheritance, or whatever else -- very few types are
dynamically altered in this way in most production programs, anyway, so
I won't lose any sleep over that possibility.

In case of class instantiation we have inheritance hierarchies
resulting from the actions of metaclasses. But the hierarchy is still a
structural property of a collection of classes i.e. an order on classes
that is exteriour to any of the classes. The mro can alsways be made
explicit. In case of a function call graph that has to be analyzed for
dependencies I don't see that such a simple distinctive scheme could
work. Or maybe it works and I just have no clue how completely opaque
call graphs as they appear in functions like this

def f(x):
h = g(x)
h()

can be tracked sufficiently in "register-time" in order to capture
dependencies. It is not clear what h can be unless g is called and it
might be as hard as type-inference or completely impossible to figure
this out without executing g. If otherwise the checker can guarantee
detection of a few obvious cycles where dependencies can be tracked
also syntactically, at least in principle like in your example, what
exactly is ensured by the checker and how can the definition of a
typeclass be adapted accordingly?

Kay
 
A

Alex Martelli

Kay Schluehr said:
work. Or maybe it works and I just have no clue how completely opaque
call graphs as they appear in functions like this

def f(x):
h = g(x)
h()

can be tracked sufficiently in "register-time" in order to capture
dependencies. It is not clear what h can be unless g is called and it
might be as hard as type-inference or completely impossible to figure
this out without executing g. If otherwise the checker can guarantee
detection of a few obvious cycles where dependencies can be tracked
also syntactically, at least in principle like in your example, what
exactly is ensured by the checker and how can the definition of a
typeclass be adapted accordingly?

My preference would be to have the dependency tracker mark explicit
dependencies only; here, if this was in a typeclass with the appropriate
additions of self., I'd mark f as dependent on g only. No real purpose
is served by allowing dependencies to be specified in a "cloaked" form,
anyway -- nor in going out of one's way to impede vigorous attempts by a
programmer to shoot himself or herself in the foot.


Alex
 
S

Steve Horsley

Matthias said:
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
This line is redundant. You don't appear to want to actually
create a Base object here, and the following code will ensure
that you end up having an 'obj' variable anyway.
if (<need D1>):
obj = D1()
else:
obj = D2()
OK. So now you have 'obj' referencing either a D1 or a D2. Both
D1 and D2 objects have a foo() method, so here is polymorphism.

There is no evidence of inheritance here though, because you
chose to override the only method that they could have inherited
from class Base. Now, if Base had also had a bar(self) method,
both would have inherited that.

Try this:

class Base:
def __init__(self):
self.value = 0
def foo(self):
print "Base", self.value
def bar(self):
self.value += 1

class D1(Base):
def foo(self):
print "D1", self.value

class D2(Base):
def foo(self):
print "D2", self.value

want1 = False
if want1:
obj = D1()
else:
obj = D2()

obj.foo()
obj.bar()
obj.foo()


Steve
 

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

Forum statistics

Threads
474,001
Messages
2,570,254
Members
46,849
Latest member
Fira

Latest Threads

Top