By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
448,672 Members | 1,628 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 448,672 IT Pros & Developers. It's quick & easy.

Is there a consensus on how to check a polymorphic instance?

P: n/a
hi all,
I'm a newbie Python programmer with a C++ brain inside. I have a
lightweight framework in which I design a base class and expect user to
extend. In other part of the framework, I heavily use the instance of
this base class (or its children class). How can I ensure the instance
IS-A base class instance, since Python is a fully dynamic typing
language?

I searched and found several different ways to do this:

1. In `Text Processing in Python', David Mertz suggests using `hasattr'
to check abilities instead of type checking.

2. In ActiveState's Python Cookbook site, Alex Martelli suggested a
safe and accurate `look before you leap' technique on
http://aspn.activestate.com/ASPN/Coo...n/Recipe/52291

3. In this group, some others suggest using isinstance() builtin
function to check instance.

Now my questions:
1. Is my design speaks native Pythonish? Such design is native in C++
and Java OO style, but I'm not sure about this in Python.

2. As I state in title, is there a consensus on how to check a
polymorphic instance? I find all the three ways above is a little bit
tedious compared with C++ and Java, in which compile-time type checking
ensures the interface protocol, any better ideas in Python?
Any comments will be appreciated.

Jul 18 '05 #1
Share this Question
Share on Google+
37 Replies


P: n/a
As you say yourself, many people in this group will probably recommend
solution #3. I would agree with them.

You should keep in mind that Alex's recipe (solution #2) was published in
2001 (before version 2.2 which introduced big changes in terms of OO) so I'm
not sure he would recommend that solution anymore. I'll leave it to him to
give his final judgement.

I haven't read Mertz's book and I don't know in what context he recommends
solution #1. Personally I see it useful in situations where you want to
check whether the class has a method like next() for example, so you want to
see whether the object is iterable. But if you implement a base class with
an attribute x1 it may be risky to just check objects for having that
attribute. The object may have an attribute with that name but its role may
be completely different than what you expect (e.g., your class may use x1
for a list, but your object is an instance of a class that uses x1 for a
string). Same thing with methods.

Go with isinstance(). That's what it's for.

Dan

"Mike Meng" <me******@gmail.com> wrote in message
news:11**********************@z14g2000cwz.googlegr oups.com...
hi all,
I'm a newbie Python programmer with a C++ brain inside. I have a
lightweight framework in which I design a base class and expect user to
extend. In other part of the framework, I heavily use the instance of
this base class (or its children class). How can I ensure the instance
IS-A base class instance, since Python is a fully dynamic typing
language?

I searched and found several different ways to do this:

1. In `Text Processing in Python', David Mertz suggests using `hasattr'
to check abilities instead of type checking.

2. In ActiveState's Python Cookbook site, Alex Martelli suggested a
safe and accurate `look before you leap' technique on
http://aspn.activestate.com/ASPN/Coo...n/Recipe/52291

3. In this group, some others suggest using isinstance() builtin
function to check instance.

Now my questions:
1. Is my design speaks native Pythonish? Such design is native in C++
and Java OO style, but I'm not sure about this in Python.

2. As I state in title, is there a consensus on how to check a
polymorphic instance? I find all the three ways above is a little bit
tedious compared with C++ and Java, in which compile-time type checking
ensures the interface protocol, any better ideas in Python?
Any comments will be appreciated.

Jul 18 '05 #2

P: n/a
Mike Meng wrote:
I'm a newbie Python programmer with a C++ brain inside. I have a
lightweight framework in which I design a base class and expect user to
extend. In other part of the framework, I heavily use the instance of
this base class (or its children class). How can I ensure the instance
IS-A base class instance, since Python is a fully dynamic typing
language?


The short answer is that if you want to ensure an instance is-a subclass
of the base class, you should use isinstance:
class HasF(object): .... def f(self):
.... raise NotImplementedError
.... class SubHasF(HasF): .... def f(self):
.... return self.__class__
.... class NonsubHasF(object): .... def f(self):
.... return self.__class__
.... isinstance(SubHasF(), HasF) True isinstance(NonsubHasF(), HasF) False

However it's often not necessary to go this route. Consider:
class HasNoF(object): .... pass
.... def use_c(c): .... try:
.... f = c.f
.... except AttributeError:
.... raise TypeError('argument to use_c must have an f method')
.... return f()
.... use_c(SubHasF()) <class '__main__.SubHasF'> use_c(NonsubHasF()) <class '__main__.NonsubHasF'> use_c(HasNoF())

Traceback (most recent call last):
File "<interactive input>", line 1, in ?
File "<interactive input>", line 5, in use_c
TypeError: argument to use_c must have an f method

Is it really necessary that the classes passed to your equivalent of
'use_c' are actually subclasses of your equivalent to 'HasF'? Or is it
just necessary that they support the appropriate methods? If it's the
second, I would just test for the appropriate methods and catch the
AttributeErrors (and possibly the TypeError that's thrown if 'f' isn't
callable).

If you can give a little more detail on your particular example, I (and
others) can probably give you more helpful suggestions...

Steve
Jul 18 '05 #3

P: n/a
Thank you Dan,
Solution #1 is concluded from 'Text Processing in Python', section
1.1.3, `Pythonic Polymorphisim', around there, here is an example he
provides:

def toDOM(xml_src = None):
from xml.dom import minidom
if hasattr(xml_src, 'documentElement'):
return xml_src # it's already a DOM object
elif hasattr(xml_src, 'read'):
# it is something that knows how to read data
return minidom.parseString(xml_src.read())
elif type(xml_src) in (StringType, UnicodeType):
# it is a filename of an XML document
xml = open(xml_src).read()
return minidom.parseString(xml)
else:
raise ValueError, "Must be initialized with " + \
"filename, file-like object, or DOM object"
What's your opinion?

Jul 18 '05 #4

P: n/a
Mike Meng wrote:
1. In `Text Processing in Python', David Mertz suggests using `hasattr'
to check abilities instead of type checking.


hasattr can give you problems if the attribute you're checking for is
calculated on the fly since hasattr basically does the same thing
getattr does:
class C(object): .... def time():
.... def fget(self):
.... time.sleep(5)
.... return time.time()
.... return dict(fget=fget)
.... time = property(**time())
.... c = C()
hasattr(c, 'time') True c.time

1101191279.3729999

If you run the code above, you'll get the 5 second pause both during the
hasattr call and the c.time access. If any object you use calculates
attribute values on the fly, calling hasattr before directly accessing
the attribute will cause the value to be calculated twice.

Steve
Jul 18 '05 #5

P: n/a
hi Steve,
Your example is helpful. Thank you.

Here is my problem. My project needs to parse some user input
string according to specific rules. The rules can be defined with a
string, a CSV file, an XML file, and other forms. Followed the standard
OO style, I designed a dictionary-like base class to abstract those
different sources and provides other parts a uniform interface to query
those rules. I put some hard code into the base class and expect user
to inherit from it.

Any good suggestion?

Mike

Jul 18 '05 #6

P: n/a
Mike Meng wrote:
Here is my problem. My project needs to parse some user input
string according to specific rules. The rules can be defined with a
string, a CSV file, an XML file, and other forms. Followed the standard
OO style, I designed a dictionary-like base class to abstract those
different sources and provides other parts a uniform interface to query
those rules. I put some hard code into the base class and expect user
to inherit from it.


The big question, I guess, is what do you want to happen if a user did
not inherit from your base class, but still provides all the appropriate
functionality? In a dynamically-typed language like Python the answer
is usually that the user's class should still be considered valid,
though of course there are exceptions to this rule.

For example, say I write the following function:
def process_rules(rules): .... for rule in rules:
.... print rule.name
....

Do I really care if the 'rules' object inherits from, say, the Rules
class? Probably not. I care that it is iterable, and that the items
that it contains have a name attribute. Here's a few different ways I
could write an object that conforms to this interface:
class Rule(object): .... def __init__(self, name):
.... self.name = name
.... process_rules([Rule(s) for s in 'abc']) a
b
c class Rules(object): .... def __init__(self, names):
.... self.names = names
.... def __iter__(self):
.... for name in self.names:
.... yield Rule(name)
.... process_rules(Rules('abc')) a
b
c class NastyRules(object): .... def __init__(self, names):
.... self.names = names
.... self.index = -1
.... def __iter__(self):
.... return iter([self]*len(self.names))
.... def __getattr__(self, attr):
.... if attr == 'name':
.... self.index += 1
.... return self.names[self.index]
.... raise AttributeError
.... process_rules(NastyRules('abc')) a
b
c

Of course, if you write your code like NastyRules, you should be drug
out into the street and shot ;) but the point is that there are a
variety of ways that a class could support the given interface and still
do everything you've asked it to. If you'd like to give your users the
option to implement the interface in whatever way seems most
appropriate, you shouldn't be testing isinstance:
isinstance([Rule(s) for s in 'abc'], list) True isinstance(Rules('abc'), list) False isinstance(NastyRules('abc'), list) False

In the example above, if you test isinstance, you disallow your user
from writing either of the other two implementations.

Note that you can still give useful error messages if you catch the
appropriate exceptions:
def process_rules(rules): .... try:
.... rules = iter(rules)
.... except TypeError:
.... raise TypeError('process_rules argument must '
.... 'support iterator protocol')
.... for rule in rules:
.... try:
.... print rule.name
.... except AttributeError:
.... raise TypeError('process_rules argument must '
.... 'produce objects with name attribute')
.... process_rules([Rule(s) for s in 'abc']) a
b
c process_rules(Rules('abc')) a
b
c process_rules(NastyRules('abc')) a
b
c process_rules(1) Traceback (most recent call last):
File "<interactive input>", line 1, in ?
File "<interactive input>", line 5, in process_rules
TypeError: process_rules argument must support iterator protocol process_rules(range(10))

Traceback (most recent call last):
File "<interactive input>", line 1, in ?
File "<interactive input>", line 11, in process_rules
TypeError: process_rules argument must produce objects with name attribute

So again, the real question here is: If a user creates a class that
appropriately implements the interface, but doesn't inherit from your
base class, should their code fail?

Steve
Jul 18 '05 #7

P: n/a
I was referring to a different kind of context in my posting. Not exactly
the code implementation but rather how is the code being used. Are you the
only one using this code and can you be confident that the function toDOM
will be called only with objects that it is designed to handle? Or are you
implementing this function as part of a module that will be used by
hundreds/thousands of users over the next ten years?

In other words, how much do you trust the test that if hasattr(xml_src,
'documentElement') is true then "it's already a DOM object"? What if a user
who downloads your code off the internet 2 years from now calls toDOM with
an object that he implemented and that has an attribute 'documentElement'
but that object behaves in a way totally different from what you expect for
a DOM object? Even you may make such a mistake if you use this code again
only one year from now, after forgetting how you implemented it.

OTOH, you may not have much of a choice but use the hasattr test in this
case if documentElement is an attribute that is commonly used by all the DOM
parser implementations and you want to be able to switch parsers without
changing your code. I am using the assumption that not all the parsers
inherit from a common base class or else you could use isinstance with that
class. I am not familiar enough with XML parsers so I don't know if
documentElement is such an attribute.

The other hasattr test in the example, for a 'read' attribute, is such a
case. 'read' is a commonly used attribute name for methods that read data.
It doesn't matter whether it is a file, or a stream, or a queue, or
whatever, as long as it is readable and read() returns a string. Mind you,
this is still an incomplete test because it does not guarantee the string to
be XML. But it is probably the best choice compared to testing for all the
possible sources of XML text that you might use.

I see actually two issues here. One is of checking inputs and that comes
down to safety vs. flexibility. You make a test very strict or very
flexible depending on many criteria. You have to consider your users,
future changes, etc.

The other issue is one of design. How do you design a set of classes that
need to follow a common interface but implement that interface in different
ways? You can create a base class and subclass all the other classes from
this one. But thanks to python's dynamic typing, if the base class is
abstract and all its attributes are overridden in the subclasses, you may as
well just forget about inheritance and just implement all the subclasses to
follow an interface protocol. That's what iterators and generators are.

So, getting back to our topic, if you use inheritance to implement your
classes or you have only one class, then use isinstance. If you have a set
of classes that do not have a common base class and instead just implement
an interface protocol, then use hasattr. Your question was about
polymorphic instances. That implies inheritance, but you may want to
consider also protocol implementation and that is what other posters are
suggesting.

I think that an interesting question here is whether inheritance or protocol
implementation is the preferred way in python. I'm not sure what the answer
is to that question. One advantage I see in inheritance is to allow
stronger checks like isinstance vs. hasattr. I come from a very
conservative background in software development and strong checks are deeply
rooted in my mind. But it looks to me that python is not about that or else
it wouldn't have dynamic typing.

Dan

"Mike Meng" <me******@gmail.com> wrote in message
news:11**********************@f14g2000cwb.googlegr oups.com...
Thank you Dan,
Solution #1 is concluded from 'Text Processing in Python', section
1.1.3, `Pythonic Polymorphisim', around there, here is an example he
provides:

def toDOM(xml_src = None):
from xml.dom import minidom
if hasattr(xml_src, 'documentElement'):
return xml_src # it's already a DOM object
elif hasattr(xml_src, 'read'):
# it is something that knows how to read data
return minidom.parseString(xml_src.read())
elif type(xml_src) in (StringType, UnicodeType):
# it is a filename of an XML document
xml = open(xml_src).read()
return minidom.parseString(xml)
else:
raise ValueError, "Must be initialized with " + \
"filename, file-like object, or DOM object"
What's your opinion?

Jul 18 '05 #8

P: n/a

"Steven Bethard" <st************@gmail.com> wrote in message
news:lkCod.88653$5K2.13406@attbi_s03...
Mike Meng wrote:
Here is my problem. My project needs to parse some user input
string according to specific rules. The rules can be defined with a
string, a CSV file, an XML file, and other forms. Followed the standard
OO style, I designed a dictionary-like base class to abstract those
different sources and provides other parts a uniform interface to query
those rules. I put some hard code into the base class and expect user
to inherit from it.


The big question, I guess, is what do you want to happen if a user did not
inherit from your base class, but still provides all the appropriate
functionality? In a dynamically-typed language like Python the answer is
usually that the user's class should still be considered valid, though of
course there are exceptions to this rule.


I have a question here, as we are discussing now protocol interfaces vs.
inheritance. Is using a class that implements a protocol without inheriting
from a base class still "polymorphism"? There are probably many definitions
for polymorphism and probably all those definitions can be interpreted in
such a way that they accept also protocols. But what I would like to hear
is what is the general opinion of people who use python. I am biased
because I come from a C++ and Java background and I am still used to a
certain practical meaning for "polymorphism". But it's beginning to dawn on
me that it is only a bias and polymorphism does apply also to python
protocol interfaces. Is that generally accepted?

Dan
Jul 18 '05 #9

P: n/a
Dan Perl wrote:
I have a question here, as we are discussing now protocol interfaces vs.
inheritance. Is using a class that implements a protocol without inheriting
from a base class still "polymorphism"?


Some good definitions:

http://en.wikipedia.org/wiki/Polymor...ter_science%29)

There are probably two types of polymorphism relevant to the current
discussion: parametric polymorphism and subtyping polymorphism. In
parametric polymorphism, a function is written so as to work with
objects of different types. Subtyping polymorphism is a subtype of
parametric polymorphism that restricts the polymorphism to only objects
that inherit from a specified base class.

In its most basic use, Python is inherently parametrically polymorphic
-- all functions are written so as to work with different types.
Consider a few simple functions:
def f(x): .... return 'f(%s)' % x
.... def g(x): .... return 2**x
....

Both of these functions work perfectly well with a wide range of objects:
f(1), f(2.0), f('abc'), f([1,2,3]) ('f(1)', 'f(2.0)', 'f(abc)', 'f([1, 2, 3])') g(4), g(5.0), g(decimal.Decimal(6)) (16, 32.0, Decimal("64"))

I didn't have to do anything special to make them work this way. Any
object that supports __str__ can be passed to f, e.g.:

class S(object): .... def __str__(self):
.... return 'str'
.... f(S()) 'f(str)'

and any object that supports __rpow__ (or __coerce__, or some other way
to be raised to a power) can be passed to g, e.g.:
class P(object): .... def __rpow__(self, other):
.... return other**other
.... g(P()) 4

So both functions f and g are parametrically polymorphic. If you wanted
to artifically restrict f or g to support only subtype polymorphism, you
could do this with isinstance. In most cases, I would probably advise
against this -- if you're using isinstance to restrict your functions to
only subtype polymorphism, you're reducing the usefulness of your code.

This is not to say that there are never any times when you want to use
isinstance. Here's a good example from my code for a Bunch class[1]:

class Bunch(object):
...
def update(self, *args, **kwds):
if len(args) == 1:
other, = args
if isinstance(other, self.__class__):
other = other.__dict__
try:
self.__dict__.update(other)
except TypeError:
raise TypeError('cannot update Bunch with %s' %
type(other).__name__)
elif len(args) != 0:
raise TypeError('expected 1 argument, got %i' % len(args))
self.__dict__.update(kwds)

In the code here, we use isinstance to check on whether or not to use
the argument's __dict__. Now I could have written the isinstance
section as:

try:
other = other.__dict__
except AttributeError:
pass

and then the function would 'work' with any object that had a __dict__
attribute. But since other classes are not guaranteed to use __dict__
in the same way that Bunch does (e.g. they're not guaranteed to have all
attributes in their __dict__s), this would give some potentially
confusing results:
class C(object): .... def x():
.... def get(self):
.... return 5
.... return dict(fget=get)
.... x = property(**x())
.... c = C()
b = Bunch()
b.update(c)
b.x

Traceback (most recent call last):
File "<interactive input>", line 1, in ?
AttributeError: 'Bunch' object has no attribute 'x'

The point here is that I'm using isinstance here because I *know* there
are objects out there that, unlike Bunch objects, may have 'attributes'
not stored in their __dict__, and I *know* they might get passed to
Bunch.update. Since I can't appropriately support their use, I want the
user to be notified of this (with the TypeError).

Of course, I'm still making heavy use of the more general parametric
polymorphism because even if the object doesn't inherit from Bunch, I
still pass it to dict.update, which works with any type that supports
the mapping protocol (and a few other types besides). So if, in the
future dict.update gains support for some new type, my code doesn't
artifically restrict the type of parameter that can be passed to my
function.

Hope this was helpful (or at least vaguely intelligible) ;)

STeve
[1] Thanks to Peter Otten for the implementation suggestion
Jul 18 '05 #10

P: n/a
In article <Nq********************@rogers.com>,
"Dan Perl" <da*****@rogers.com> wrote:
"Steven Bethard" <st************@gmail.com> wrote in message
news:lkCod.88653$5K2.13406@attbi_s03...

The big question, I guess, is what do you want to happen if a user did not
inherit from your base class, but still provides all the appropriate
functionality? In a dynamically-typed language like Python the answer is
usually that the user's class should still be considered valid, though of
course there are exceptions to this rule.


I have a question here, as we are discussing now protocol interfaces vs.
inheritance. Is using a class that implements a protocol without inheriting
from a base class still "polymorphism"? There are probably many definitions
for polymorphism and probably all those definitions can be interpreted in
such a way that they accept also protocols. But what I would like to hear
is what is the general opinion of people who use python. I am biased
because I come from a C++ and Java background and I am still used to a
certain practical meaning for "polymorphism". But it's beginning to dawn on
me that it is only a bias and polymorphism does apply also to python
protocol interfaces. Is that generally accepted?


Not only are there many definitions of polymorphism, there are
several kinds of polymorphism, each with many definitions. You
could spend some time with a web search engine and get lots of
reading material on this, and I think you would find that a good
deal of it is coming from a point of view that kind of tends to
ignore object oriented programming altogether. Check it out.
If you want to stay with the more immediately relevant OOP
thinking on this, search for polymorphism and smalltalk.

Note that C++ and Java need two mechanisms in support of something
approximating parametric polymorphism. The subtype polymorphism
I think you're thinking of is the classic OO device, but it doesn't
support really generic functions and types. The template system
has been bolted onto these languages (I gather Java has one now)
for this purpose. With these two mechanisms, they more or less
support parametric polymorphism, in a statically typed, compiled
language. Python is not statically typed, of course, so where
the literature about parametric polymorphism focuses on types
you have to decide for yourself how important that is in principle.
In the end it of course doesn't really matter what you call it.

Donn Cave, do**@u.washington.edu
Jul 18 '05 #11

P: n/a
In article <YWKod.137814$HA.44609@attbi_s01>,
Steven Bethard <st************@gmail.com> wrote:
....
This is not to say that there are never any times when you want to use
isinstance. Here's a good example from my code for a Bunch class[1]:

class Bunch(object):
...
def update(self, *args, **kwds):
if len(args) == 1:
other, = args
if isinstance(other, self.__class__):
other = other.__dict__
try:
self.__dict__.update(other)
except TypeError:
raise TypeError('cannot update Bunch with %s' %
type(other).__name__)
elif len(args) != 0:
raise TypeError('expected 1 argument, got %i' % len(args))
self.__dict__.update(kwds)

In the code here, we use isinstance to check on whether or not to use
the argument's __dict__. ....
The point here is that I'm using isinstance here because I *know* there
are objects out there that, unlike Bunch objects, may have 'attributes'
not stored in their __dict__, and I *know* they might get passed to
Bunch.update. Since I can't appropriately support their use, I want the
user to be notified of this (with the TypeError).

Of course, I'm still making heavy use of the more general parametric
polymorphism because even if the object doesn't inherit from Bunch, I
still pass it to dict.update, which works with any type that supports
the mapping protocol (and a few other types besides). So if, in the
future dict.update gains support for some new type, my code doesn't
artifically restrict the type of parameter that can be passed to my
function.

Hope this was helpful (or at least vaguely intelligible) ;)


I don't really see that isinstance() was necessary in principle
here. Not that it makes any difference to me, but it looks like
you created this problem by defining these semantics in Bunch
but failing to assign any identifiable attribute - we could in
principle make another unrelated class "quack like a Bunch",
but we'd have no way to inform your update function of this.

Donn Cave, do**@u.washington.edu
Jul 18 '05 #12

P: n/a

"Donn Cave" <do**@u.washington.edu> wrote in message
news:do************************@gnus01.u.washingto n.edu...
In article <YWKod.137814$HA.44609@attbi_s01>,
Steven Bethard <st************@gmail.com> wrote:
...
This is not to say that there are never any times when you want to use
isinstance. Here's a good example from my code for a Bunch class[1]:

class Bunch(object):
...
def update(self, *args, **kwds):
if len(args) == 1:
other, = args
if isinstance(other, self.__class__):
other = other.__dict__
try:
self.__dict__.update(other)
except TypeError:
raise TypeError('cannot update Bunch with %s' %
type(other).__name__)
elif len(args) != 0:
raise TypeError('expected 1 argument, got %i' % len(args))
self.__dict__.update(kwds)

In the code here, we use isinstance to check on whether or not to use
the argument's __dict__. ...
The point here is that I'm using isinstance here because I *know* there
are objects out there that, unlike Bunch objects, may have 'attributes'
not stored in their __dict__, and I *know* they might get passed to
Bunch.update. Since I can't appropriately support their use, I want the
user to be notified of this (with the TypeError).

Of course, I'm still making heavy use of the more general parametric
polymorphism because even if the object doesn't inherit from Bunch, I
still pass it to dict.update, which works with any type that supports
the mapping protocol (and a few other types besides). So if, in the
future dict.update gains support for some new type, my code doesn't
artifically restrict the type of parameter that can be passed to my
function.

Hope this was helpful (or at least vaguely intelligible) ;)


I don't really see that isinstance() was necessary in principle
here. Not that it makes any difference to me, but it looks like
you created this problem by defining these semantics in Bunch
but failing to assign any identifiable attribute - we could in
principle make another unrelated class "quack like a Bunch",
but we'd have no way to inform your update function of this.


I'm not sure what you mean by an "identifiable attribute", but I think we
are going back to an issue I pointed out in another posting in this thread.
It makes a huge difference whether your code is something that only you are
using and changing during the next few weeks or it's an off-the-shelf
library that will be maintained and changed for years from now and will be
used by many clients. What is "identifiable" to you (or even to someone in
the next cubicle) for the next few weeks may not be identifiable a few years
from now or to someone who downloaded your module from the web.

I hope this was "at least vaguely intelligible"!

Dan
Donn Cave, do**@u.washington.edu

Jul 18 '05 #13

P: n/a

"Donn Cave" <do**@u.washington.edu> wrote in message
news:do************************@gnus01.u.washingto n.edu...
Not only are there many definitions of polymorphism, there are
several kinds of polymorphism, each with many definitions. You
could spend some time with a web search engine and get lots of
reading material on this, and I think you would find that a good
deal of it is coming from a point of view that kind of tends to
ignore object oriented programming altogether. Check it out.
If you want to stay with the more immediately relevant OOP
thinking on this, search for polymorphism and smalltalk.

Note that C++ and Java need two mechanisms in support of something
approximating parametric polymorphism. The subtype polymorphism
I think you're thinking of is the classic OO device, but it doesn't
support really generic functions and types. The template system
has been bolted onto these languages (I gather Java has one now)
for this purpose. With these two mechanisms, they more or less
support parametric polymorphism, in a statically typed, compiled
language. Python is not statically typed, of course, so where
the literature about parametric polymorphism focuses on types
you have to decide for yourself how important that is in principle.
In the end it of course doesn't really matter what you call it.
Yes, you are right, subtype polymorphism is what I had in mind and I was
implicitly questioning on the acceptance of 'polymorphism' as one type or
the other, even though I was not aware of the formal separation between the
two types. Having learned of polymorphism years ago in the C++ and Java
context where it was used only the sense of subtype polymorphism, it was
still the way I interpreted the term in the subject line of this thread.
From now on I will keep in mind the more general meaning of parametric
polymorphism.

Dan
Donn Cave, do**@u.washington.edu

Jul 18 '05 #14

P: n/a
Donn Cave wrote:
Steven Bethard wrote:
...
The point here is that I'm using isinstance here because I *know* there
are objects out there that, unlike Bunch objects, may have 'attributes'
not stored in their __dict__, and I *know* they might get passed to
Bunch.update. Since I can't appropriately support their use, I want the
user to be notified of this (with the TypeError).
[snip]
I don't really see that isinstance() was necessary in principle
here. Not that it makes any difference to me, but it looks like
you created this problem by defining these semantics in Bunch
but failing to assign any identifiable attribute


Yup. The reason I "needed" isinstance was that my implementation works
directly with __dict__ instead of creating some __dict__-like attribute
and writing the appropriate code in __getattr__ and __setattr__ to use
this __dict__-like attribute instead. I actually thought about using a
__dict__-like attribute instead of __dict__ for exactly this reason (so
that I could just check for the attribute instead of testing
isinstance), but it seemed like a lot of overhead for not much gain (at
least, for the intended uses of Bunch -- see the "generic object -
moving toward PEP" and "generic object implementation" threads.)

Steve
Jul 18 '05 #15

P: n/a
On 22 Nov 2004 22:40:10 -0800, Mike Meng <me******@gmail.com> wrote:
hi Steve,
Your example is helpful. Thank you.

Here is my problem. My project needs to parse some user input
string according to specific rules. The rules can be defined with a
string, a CSV file, an XML file, and other forms. Followed the standard
OO style, I designed a dictionary-like base class to abstract those
different sources and provides other parts a uniform interface to query
those rules. I put some hard code into the base class and expect user
to inherit from it.


Your base class defines a "protocol" (loosely defined as a set of
methods, associated behavior & usage pattern). It's more than an
"interface", in strict C++/Java speak, because a protocol defines the
interaction between objects in a dynamic fashion.

In Python, you can use isinstance() to implement a safe, but somewhat
rigid, solution to your problem. Using hasattr() is another option,
that doesn't restrict you from using only derived classes; however,
it's more confusing, because you may mistake a partial implementation
of the protocol for a full one, depending on what & when you check.

The new-style approach to the problem is adaptation. I've written a
short article on adaptation, with some references to the adaptation
PEP, and also to PyProtocols, that is a recent implementation of the
idea that goes much further than the original PEP did. Some big
projects like Zope already use adaptation.

http://pythonnotes.blogspot.com/2004...daptation.html

The use case for adaptation is as follows; you call adapt(object,
protocol), and the adapt call will return an object that is guaranteed
to support your protocol. Of course, if your object is from the
correct base class, then it's automatically returned. If the user
implements a new class from the scratch, but that happens to implement
your protocol, then he may implement the proper adaptation hooks to
make the adapt() call succeed.

The main advantage of using adaptation, in your case, is that it
cleans up the code somewhat. You call adapt() only once, and you can
simply use the object that is returned from it without fear. In
opposition to isinstance (which only does a simple inheritance check),
adapt() can do some work on the object it receives to 'transform' it
into a new object that supports the protocol. It's a much more dynamic
approach to the problem.

--
Carlos Ribeiro
Consultoria em Projetos
blog: http://rascunhosrotos.blogspot.com
blog: http://pythonnotes.blogspot.com
mail: ca********@gmail.com
mail: ca********@yahoo.com
Jul 18 '05 #16

P: n/a
In article <tq********************@rogers.com>,
"Dan Perl" <da*****@rogers.com> wrote:
"Donn Cave" <do**@u.washington.edu> wrote in message
news:do************************@gnus01.u.washingto n.edu...
In article <YWKod.137814$HA.44609@attbi_s01>,
Steven Bethard <st************@gmail.com> wrote: ....
The point here is that I'm using isinstance here because I *know* there
are objects out there that, unlike Bunch objects, may have 'attributes'
not stored in their __dict__, and I *know* they might get passed to
Bunch.update. Since I can't appropriately support their use, I want the
user to be notified of this (with the TypeError).
.... I don't really see that isinstance() was necessary in principle
here. Not that it makes any difference to me, but it looks like
you created this problem by defining these semantics in Bunch
but failing to assign any identifiable attribute - we could in
principle make another unrelated class "quack like a Bunch",
but we'd have no way to inform your update function of this.


I'm not sure what you mean by an "identifiable attribute", but I think we
are going back to an issue I pointed out in another posting in this thread.
It makes a huge difference whether your code is something that only you are
using and changing during the next few weeks or it's an off-the-shelf
library that will be maintained and changed for years from now and will be
used by many clients. What is "identifiable" to you (or even to someone in
the next cubicle) for the next few weeks may not be identifiable a few years
from now or to someone who downloaded your module from the web.


I wasn't really worried about people yet. Perhaps I should have
said more simply that there is no attribute, period, for this.
Instead, in effect, the (base) class itself is used as that attribute.
That's an unnecessary limitation, because we can easily support
the required semantics from an unrelated class.

I must have missed your post, but I infer that you're claiming that
subtype polymorphism is easier to comprehend. It wouldn't have
occurred to me. I do think the current situation is a little
defective when it comes to reasoning about complicated code, but
I don't think subtyping is a very obvious way to define interfaces.
On one hand, it obviously isn't true that all objects that might
support an interface must inherit that support from a common base,
and it also isn't true that all objects derived from a common base
reliably support that interface. How easy would it be to inherit
from "Bunch" and add some feature that confounds that function we
saw earlier with the isinstance()?

Donn Cave, do**@u.washington.edu
Jul 18 '05 #17

P: n/a

"Donn Cave" <do**@u.washington.edu> wrote in message
news:do************************@gnus01.u.washingto n.edu...
In article <tq********************@rogers.com>,
"Dan Perl" <da*****@rogers.com> wrote:
"Donn Cave" <do**@u.washington.edu> wrote in message
news:do************************@gnus01.u.washingto n.edu...
> In article <YWKod.137814$HA.44609@attbi_s01>,
> Steven Bethard <st************@gmail.com> wrote: ... >> The point here is that I'm using isinstance here because I *know*
>> there
>> are objects out there that, unlike Bunch objects, may have
>> 'attributes'
>> not stored in their __dict__, and I *know* they might get passed to
>> Bunch.update. Since I can't appropriately support their use, I want
>> the
>> user to be notified of this (with the TypeError). ... > I don't really see that isinstance() was necessary in principle
> here. Not that it makes any difference to me, but it looks like
> you created this problem by defining these semantics in Bunch
> but failing to assign any identifiable attribute - we could in
> principle make another unrelated class "quack like a Bunch",
> but we'd have no way to inform your update function of this.
I'm not sure what you mean by an "identifiable attribute", but I think we
are going back to an issue I pointed out in another posting in this
thread.
It makes a huge difference whether your code is something that only you
are
using and changing during the next few weeks or it's an off-the-shelf
library that will be maintained and changed for years from now and will
be
used by many clients. What is "identifiable" to you (or even to someone
in
the next cubicle) for the next few weeks may not be identifiable a few
years
from now or to someone who downloaded your module from the web.


I wasn't really worried about people yet. Perhaps I should have
said more simply that there is no attribute, period, for this.
Instead, in effect, the (base) class itself is used as that attribute.
That's an unnecessary limitation, because we can easily support
the required semantics from an unrelated class.

I must have missed your post, but I infer that you're claiming that
subtype polymorphism is easier to comprehend. It wouldn't have
occurred to me. I do think the current situation is a little
defective when it comes to reasoning about complicated code, but
I don't think subtyping is a very obvious way to define interfaces.
On one hand, it obviously isn't true that all objects that might
support an interface must inherit that support from a common base,
and it also isn't true that all objects derived from a common base
reliably support that interface. How easy would it be to inherit
from "Bunch" and add some feature that confounds that function we
saw earlier with the isinstance()?


I was rather saying that subtype polymorphism (through inheritance, rather
than protocols) and checking with isinstance is safer. You are right that
inheritance is not foolproof either. But if someone is inheriting from your
base class, they are bound to be more aware of the interface and to be more
careful when overriding an attribute that would completely change the
behavior of their objects. A protocol can be broken in many other ways. In
the case of a well known protocol like iterators most people know better
than to use the name "next" for any attribute other than a method used for
iteration. But if you create a little known protocol interface with a
poorly chosen attribute name like "x" it is very likely that someone is
going to use the same name in a class for some attribute that is completely
different (e.g., data instead of a method). And if they are not explicitly
inheriting from a base class they may never be aware they are obfuscating a
behavior that is being used somewhere in their code. Note that I said
"explicitly inheriting", which infers that people can make the same kind of
mistakes also if they inherit from some class without knowing it.

This discussion feels to me like deja vu. It's a kind of philosophical
discussion in which I've been involved before, with me on this side of the
argument and with other people being on the side that says that "python is
designed for people who rely more on their users and for users who are more
reliable, the other people should use other languages" (my own
paraphrasing). I'm not saying this with any disrespect toward the other
side. After all, this is probably inherent in a dynamically typed language,
and dynamic typing has other advantages instead. It's just something I
still have to get used to: what is python good for, in what kind of
applications it should be used and when should you just use a different
(perhaps a strongly typed) language.

Dan
Donn Cave, do**@u.washington.edu

Jul 18 '05 #18

P: n/a
In article <mq********************@rogers.com>,
"Dan Perl" <da*****@rogers.com> wrote:
....
I was rather saying that subtype polymorphism (through inheritance, rather
than protocols) and checking with isinstance is safer. You are right that
inheritance is not foolproof either. But if someone is inheriting from your
base class, they are bound to be more aware of the interface and to be more
careful when overriding an attribute that would completely change the
behavior of their objects. A protocol can be broken in many other ways. In
the case of a well known protocol like iterators most people know better
than to use the name "next" for any attribute other than a method used for
iteration. But if you create a little known protocol interface with a
poorly chosen attribute name like "x" it is very likely that someone is
going to use the same name in a class for some attribute that is completely
different (e.g., data instead of a method). And if they are not explicitly
inheriting from a base class they may never be aware they are obfuscating a
behavior that is being used somewhere in their code. Note that I said
"explicitly inheriting", which infers that people can make the same kind of
mistakes also if they inherit from some class without knowing it.

This discussion feels to me like deja vu. It's a kind of philosophical
discussion in which I've been involved before, with me on this side of the
argument and with other people being on the side that says that "python is
designed for people who rely more on their users and for users who are more
reliable, the other people should use other languages" (my own
paraphrasing). I'm not saying this with any disrespect toward the other
side. After all, this is probably inherent in a dynamically typed language,
and dynamic typing has other advantages instead. It's just something I
still have to get used to: what is python good for, in what kind of
applications it should be used and when should you just use a different
(perhaps a strongly typed) language.


I'm very sympathetic to the advantages of static typing
(NB, I read here that Python is strongly, though dynamically,
typed. It is not statically typed.) Rather than embrace
subtype polymorphism through inheritance, however, I see it
as evidence that no one has figured out how to make static
typing really work with OOP. There has to be a better way
to do it.

Donn Cave, do**@u.washington.edu
Jul 18 '05 #19

P: n/a
"Donn Cave" <do**@u.washington.edu> wrote in message
news:do************************@gnus01.u.washingto n.edu...
I'm very sympathetic to the advantages of static typing
(NB, I read here that Python is strongly, though dynamically,
typed. It is not statically typed.) Rather than embrace
subtype polymorphism through inheritance, however, I see it
as evidence that no one has figured out how to make static
typing really work with OOP. There has to be a better way
to do it.
You're right, I should have said statically typed instead of strongly typed.

But did you really mean "no one has figured out how to make *static* typing
really work with OOP" or did you mean *dynamic* typing?
Donn Cave, do**@u.washington.edu

Jul 18 '05 #20

P: n/a
> I come from a very conservative background in software development
and strong checks are deeply rooted in my mind.


So do I, Dan.

I just can't imagine a `wild' object whose interface is valid can
truely do the right things. While in fact, when I typing this word, I
realize we can't ensure the internal protocol will be obeyed even when
the object IS-A base class instance.

Maybe it's time to rethink.

It seems to me the virtue of dynamic langauge is, if it looks like a
cat, it's a cat. But the problem is still there: how do you know what
it looks like before you treat it as a cat? isinstance(), , as Steve
state, is too rigid.

Jul 18 '05 #21

P: n/a
Quoth "Dan Perl" <da*****@rogers.com>:
| "Donn Cave" <do**@u.washington.edu> wrote in message
| news:do************************@gnus01.u.washingto n.edu...
| > I'm very sympathetic to the advantages of static typing
| > (NB, I read here that Python is strongly, though dynamically,
| > typed. It is not statically typed.) Rather than embrace
| > subtype polymorphism through inheritance, however, I see it
| > as evidence that no one has figured out how to make static
| > typing really work with OOP. There has to be a better way
| > to do it.
|
| You're right, I should have said statically typed instead of strongly typed.
|
| But did you really mean "no one has figured out how to make *static* typing
| really work with OOP" or did you mean *dynamic* typing?

Static. I figure dynamic & OOP get along fine -- as Smalltalk showed,
along with I believe Objective C and of course Python. Static typing
and Functional Programming seem to go hand in glove, cf. Haskell type
classes. Static OOP seems to invite inelegant and unsound compromises.

Donn Cave, do**@u.washington.edu
Jul 18 '05 #22

P: n/a

"Donn Cave" <do**@drizzle.com> wrote in message
news:1101273995.954137@yasure...
Quoth "Dan Perl" <da*****@rogers.com>:
| "Donn Cave" <do**@u.washington.edu> wrote in message
| news:do************************@gnus01.u.washingto n.edu...
| > I'm very sympathetic to the advantages of static typing
| > (NB, I read here that Python is strongly, though dynamically,
| > typed. It is not statically typed.) Rather than embrace
| > subtype polymorphism through inheritance, however, I see it
| > as evidence that no one has figured out how to make static
| > typing really work with OOP. There has to be a better way
| > to do it.
|
| You're right, I should have said statically typed instead of strongly
typed.
|
| But did you really mean "no one has figured out how to make *static*
typing
| really work with OOP" or did you mean *dynamic* typing?

Static. I figure dynamic & OOP get along fine -- as Smalltalk showed,
along with I believe Objective C and of course Python. Static typing
and Functional Programming seem to go hand in glove, cf. Haskell type
classes. Static OOP seems to invite inelegant and unsound compromises.
It's hard to argue against Smalltalk. I haven't used it for many years so I
don't remember it but it was THE language of choice for an OOP course I took
oh so many years ago. And I still see it mentioned as a model for OOP. I
never learned Objective C so I cannot argue with that either.
Unfortunately, as much as I am learning to like Python, I don't think I see
it as a picture perfect OOP language. It may be a controversial opinion but
I am not at all satisfied with encapsulation in Python (you cannot hide any
of a class's implementation).

Can you elaborate on problems that static languages have with OOP? We have
been touching on parametric polymorphism and that is a valid point. I also
see multiple inheritance as being very poorly implemented in C++ and Java to
the point where books on C++ that treat the subject discourage you from
using it and Java has effectively designed it out of the language (you can
implement many interfaces but you can extend only one class). What do you
have in mind?

Dan
Donn Cave, do**@u.washington.edu

Jul 18 '05 #23

P: n/a
Donn Cave wrote:
Rather than embrace
subtype polymorphism through inheritance, however, I see it
as evidence that no one has figured out how to make static
typing really work with OOP.


I'm not sure I follow your argument here. Java interfaces provide the
same sort of protocol-based typing as Python does (though of course you
have do declare interfaces everywhere to make them work this way).
Going back to my earlier Python example:
def process_rules(rules):

.... for rule in rules:
.... print rule.name
....

the corresponding Java might look something like:

public interface Rule {
public Object getName();
}

public class RuleProcessor {
public void processRules(Iterable<Rule> rules){
for (Rule rule: rules) {
System.out.println(rule.getName())
}
}
}

Note that because Java is statically typed, you have to declare all the
appropriate interfaces: Iterable is in java.lang, and Rule is declared
above. In Python, the interfaces are implied by the use, and left
undeclared.

Of course, you have to declare your interfaces in a suitably generic
manner. I could have written this replacing Iterable<Rule> with
Collection<Rule> or List<Rule>, but Iterable<Rule> is the type that
makes the fewest commitments about the parameter and still allows me to
iterate over the Rule objects. Similarly, I could have written getName
to return a String, but by declaring getName with Object as a return
type, I make only the necessary commitment that getName returns a value.

The annoying thing, of course, is what you do when suddenly *one* use of
a Rule does require, say, a String result of getName:

public class OtherRuleProcessor {
public void processRules(Iterable<Rule> rules){
for (Rule rule: rules) {
String[] s = rule.getName().split('\\s')
}
}
}

Do I now declare a new interface for this Rule and add another
implements clause to all my classes that implement the original Rule as
well? Do I make the original Rule interface less general by changing
the return type from Object to String? It can get nasty rather quickly...

Python, of course, avoids this by not declaring types. =)

Steve
Jul 18 '05 #24

P: n/a
On 23 Nov 2004 19:58:31 -0800, Mike Meng <me******@gmail.com> wrote:
I come from a very conservative background in software development
and strong checks are deeply rooted in my mind.
So do I, Dan.

I just can't imagine a `wild' object whose interface is valid can
truely do the right things. While in fact, when I typing this word, I
realize we can't ensure the internal protocol will be obeyed even when
the object IS-A base class instance.

Maybe it's time to rethink.


I understand, and I found myself using isinstance more often that I
would like to admit. Being trained in OO Pascal & Delphi it comes as
no surprise...
It seems to me the virtue of dynamic langauge is, if it looks like a
cat, it's a cat. But the problem is still there: how do you know what
it looks like before you treat it as a cat? isinstance(), , as Steve
state, is too rigid.


Time for adapt(), I think...

--
Carlos Ribeiro
Consultoria em Projetos
blog: http://rascunhosrotos.blogspot.com
blog: http://pythonnotes.blogspot.com
mail: ca********@gmail.com
mail: ca********@yahoo.com
Jul 18 '05 #25

P: n/a
On 23 Nov 2004 19:58:31 -0800,
"Mike Meng" <me******@gmail.com> wrote:

First off, I have only been following this thread on and off, so if I'm
repeating things, I apologize.
It seems to me the virtue of dynamic langauge is, if it looks like a
cat, it's a cat ...
Google for "duck typing."
... But the problem is still there: how do you know what it looks like
before you treat it as a cat? isinstance(), , as Steve state, is too
rigid.


You don't.

Python also takes a "we're all adults here" philosophy. If your API
specifies "cat," and I pass your function an "InterfaceAdapter," it's
*my* problem.

If you're truly paranoid, make sure that your arguments at least have
the methods you're going to use:

# write 'foo' to the file-like object x; don't bother if x is not
# sufficiently file-like

def f( x ):
try: x.write
except AttributeError:
pass
else:
x.write( 'foo' )

# this works, too, but doesn't scale well (i.e., if 'foo' is some
# other non-trivial (maybe non-trusted) operation that can raise
# AttributeError):

def f( x ):
try: x.write( 'foo' )
except AttributeError:
pass

Regards,
Dan

--
Dan Sommers
<http://www.tombstonezero.net/dan/>
Never play leapfrog with a unicorn.
Jul 18 '05 #26

P: n/a
Mike Meng wrote:
It seems to me the virtue of dynamic langauge is, if it looks like a
cat, it's a cat.
I think this is more accurately expressed as "if it *acts* like
a cat, then you can treat it like a cat". Your version implies
that you really do care what it _is_, which goes back to the
static typing thinking again.
But the problem is still there: how do you know what
it looks like before you treat it as a cat? isinstance(), , as Steve
state, is too rigid.


The argument is that you probably don't really need to know
what it looks like *before* you try to use it, even if you
think you do. The cases where you really do are probably those
Alex Martelli's recipe (mentioned near the start of the thread)
is intended to help. (Specifically, avoiding the situation
where the object implements the required protocol only partially,
but you don't find that out until you've already started using
the object, possibly corrupting it in the process.)

-Peter
Jul 18 '05 #27

P: n/a
In article <s4********************@rogers.com>,
"Dan Perl" <da*****@rogers.com> wrote
....
It's hard to argue against Smalltalk. I haven't used it for many years so I
don't remember it but it was THE language of choice for an OOP course I took
oh so many years ago. And I still see it mentioned as a model for OOP. I
never learned Objective C so I cannot argue with that either.
Unfortunately, as much as I am learning to like Python, I don't think I see
it as a picture perfect OOP language. It may be a controversial opinion but
I am not at all satisfied with encapsulation in Python (you cannot hide any
of a class's implementation). Can you elaborate on problems that static languages have with OOP? We have
been touching on parametric polymorphism and that is a valid point. I also
see multiple inheritance as being very poorly implemented in C++ and Java to
the point where books on C++ that treat the subject discourage you from
using it and Java has effectively designed it out of the language (you can
implement many interfaces but you can extend only one class). What do you
have in mind?


I am really too unschooled in these matters to pursue that.
I have mentioned Haskell a couple of times, and that's my
perspective on static typing. I recommend it as an interesting
exercise: learn Haskell, find out what static typing is about.
It isn't perfect, but it's leagues ahead of C++ etc.

I don't have any beef with multiple inheritance, in fact the
notion of "mix-in" classes might have some potential to be the
basis for a more sound approach to OO.

Donn Cave, do**@u.washington.edu
Jul 18 '05 #28

P: n/a
Donn Cave wrote:
I don't have any beef with multiple inheritance, in fact the
notion of "mix-in" classes might have some potential to be the
basis for a more sound approach to OO.


The people that have a beef with multiple inheritance have it because
multiple inheritance can sometimes break encapsulation by forcing the
designer of a class to be aware of changes in the inheritance chain far
above its parents (even when no methods/attributes are added by the
change). A classic reference:

Snyder, Alan. "Encapsulation and Inheritance in Object-Oriented
Programming Languages"
http://www-plan.cs.colorado.edu/diwa...ers/snyder.pdf

Python tries to combat some of these problems with a good method
resolution order:

http://www.python.org/2.3/mro.html

I don't make enough use of multiple inheritance to ever have run into
these kinds of problems, but the literature in this area is enormous...

Steve
Jul 18 '05 #29

P: n/a
"Dan Perl" <da*****@rogers.com> writes:
Can you elaborate on problems that static languages have with OOP?


They make dynamic polymorphism impossible.

Which is why most object-oriented C++ programs are dynamically typed;
only the programmer is burdened with the work that the mostly-absent
dynamic type system should be doing.

Dynamic polymorphism crucially requires knowledge of the *run-time*
(dynamic) type of objects, in order to be able to dispatch to the
correct method. In C++ you turn your classes into dynamically typed
ones with the "virtual" keyword, which introduces a vtable and
run-time type identification (RTTI). As long as you are only
interested in calling methods which are declared in some base class,
this dynamic type systems looks almost satisfactory. As soon as you
want to use a method present in the subclass but not in the
superclass, it is up to you to faff around with dynamic_cast and
checking of the resulting pointer; all stuff which a dynamic type
system should be doing for you, but C++ makes you do yourself because
it pretends that your programs are statically typed when, in fact,
they are dynamically typed.
Jul 18 '05 #30

P: n/a
Jacek Generowicz wrote:
"Dan Perl" <da*****@rogers.com> writes:
Can you elaborate on problems that static languages have with OOP?


They make dynamic polymorphism impossible.

Which is why most object-oriented C++ programs are dynamically typed;
only the programmer is burdened with the work that the mostly-absent
dynamic type system should be doing.

Dynamic polymorphism crucially requires knowledge of the *run-time*
(dynamic) type of objects, in order to be able to dispatch to the
correct method. In C++ you turn your classes into dynamically typed
ones with the "virtual" keyword, which introduces a vtable and
run-time type identification (RTTI). As long as you are only
interested in calling methods which are declared in some base class,
this dynamic type systems looks almost satisfactory. As soon as you
want to use a method present in the subclass but not in the
superclass, it is up to you to faff around with dynamic_cast and
checking of the resulting pointer; all stuff which a dynamic type
system should be doing for you, but C++ makes you do yourself because
it pretends that your programs are statically typed when, in fact,
they are dynamically typed.

If your programs need that much dynamic_cast to work, then your programs are
bad. Besides, it's easy to do a 'safe' dynamic_cast, just assert that the
pointer you get isn't 0 after the cast.
Jul 18 '05 #31

P: n/a
Christophe Cavalaria <ch*************@free.fr> writes:
Jacek Generowicz wrote:
"Dan Perl" <da*****@rogers.com> writes:
Can you elaborate on problems that static languages have with OOP?
They make dynamic polymorphism impossible.

Besides, it's easy to do a 'safe' dynamic_cast, just assert that the
pointer you get isn't 0 after the cast.


Yes, you can "easily" do the work that the absent dynamic type system
should be doing for you (and Turing equivalence tells us that that we
can implement a decent dynamic type system as well). The fact remains
that the objects *are* *dynamically* typed, even though the language
claims to be statically typed. The fact that statically typed
languages feel the need to introduce dynamic typing (however poorly
supported it may be) in order to support OOP, points to the fact that
static type systems DO have problems with OOP, which is what the
question was about.

Specifically, dynamic polymorphism is impossible without dynamic
typing.
Jul 18 '05 #32

P: n/a
Quoth Jacek Generowicz <ja**************@cern.ch>:
[ ... re C++ casting to subtype ]
| Yes, you can "easily" do the work that the absent dynamic type system
| should be doing for you (and Turing equivalence tells us that that we
| can implement a decent dynamic type system as well). The fact remains
| that the objects *are* *dynamically* typed, even though the language
| claims to be statically typed. The fact that statically typed
| languages feel the need to introduce dynamic typing (however poorly
| supported it may be) in order to support OOP, points to the fact that
| static type systems DO have problems with OOP, which is what the
| question was about.
|
| Specifically, dynamic polymorphism is impossible without dynamic
| typing.

Given a model for polymorphism that means smuggling an object
around wrapped in some degree of mystery about its type, elegant
and sound static typing does seem unlikely. But C++ and its
descendents have always been easy targets. Would you happen to
know something about how Objective CAML's OO system approaches this?

I see in the on-line documentation this paragraph:

Be aware that subtyping and inheritance are not related. Inheritance
is a syntactic relation between classes while subtyping is a semantic
relation between types. For instance, the class of colored points could
have been defined directly, without inheriting from the class of points;
the type of colored points would remain unchanged and thus still be a
subtype of points.

http://caml.inria.fr/ocaml/htmlman/manual005.html

I'm thinking that this might be more or less typical of academic
ideas about OO typing, and it seems conceptually appealing. I
don't know the details though.

Donn Cave, do**@u.washington.edu
Jul 18 '05 #33

P: n/a
Donn Cave <do**@drizzle.com> wrote:
Quoth Jacek Generowicz <ja**************@cern.ch>: Given a model for polymorphism that means smuggling an object
around wrapped in some degree of mystery about its type, elegant
and sound static typing does seem unlikely. But C++ and its
descendents have always been easy targets. Would you happen to
know something about how Objective CAML's OO system approaches this?
In several ways: First, by using parametric polymorphism (not defined
as in this thread above, but by allowing "type variables" (type parameters)
which are applied as necessary, both in every expression, and in
object types. Second, by using so-called "row types", i.e. types
for incomplete records (in OCaml, they are denoted by the trailing dots
in a type like < get_x : int; set_x : int -> 'a; .. > ).

Together, they allow you to statically type most of the usual applications
of inheritance, virtual methods, etc. There used to be an somewhat
more contrived example in the OCaml manual with a subject/observer
pattern, virtual methods, and both abstract and concrete classes,
but I think they removed that some time ago.

As for
Specifically, dynamic polymorphism is impossible without dynamic
typing.
I haven't heard the term "dynamic typing" in this context, and I think
it's a bit unfortunate, because it's not "typing" in the usual
sense. If Jacek just means virtual methods, as in
In C++ you turn your classes into dynamically typed ones with the
"virtual" keyword, which introduces a vtable and run-time type
identification (RTTI).
that's easy to do in OCaml, and it also works in the type classes
of Haskell. The problem Jacek describes here:
As soon as you want to use a method present in the subclass but not
in the superclass, it is up to you to faff around with dynamic_cast
and checking of the resulting pointer;


is solved by keeping the parametric type "uninstantiated" as long
as possible, and instantiate it with the concrete type only when
it is clear that you really need the method present in the subclass.
Or, you handle this with the row types.

It does not work in every case one can think of right from the start
(you have to be able to statically guarantee that you have really the
subclass to work with at this point of your code, and not the
superclass), so you might to rewrite your code a bit to make it
work. Maybe it's easier to see if we do concrete examples.
I see in the on-line documentation this paragraph:

Be aware that subtyping and inheritance are not related. [...] I'm thinking that this might be more or less typical of academic
ideas about OO typing, and it seems conceptually appealing. I
don't know the details though.


The "inheritance is not subtyping"-motto is somewhat orthogonal to
this issue. It comes originally from a paper by Benjamin Pierce, with
IIRC the same title. If you think this concept through, it really helps
to sort out some of the confusion that comes with the "usual" OOL's
like Java and C++.

- Dirk
Jul 18 '05 #34

P: n/a
Peter Hansen <pe***@engcorp.com> wrote:
> But the problem is still there: how do you know what
it looks like before you treat it as a cat? isinstance(), , as Steve
state, is too rigid.


The argument is that you probably don't really need to know
what it looks like *before* you try to use it, even if you
think you do. The cases where you really do are probably those
Alex Martelli's recipe (mentioned near the start of the thread)
is intended to help. (Specifically, avoiding the situation
where the object implements the required protocol only partially,
but you don't find that out until you've already started using
the object, possibly corrupting it in the process.)


Exactly. The recipe's still in the CB 2nd edition (which I should be
editing right now instead of doing usenet, with deadline so terribly
close and lots of enhancements left to do, but hey, it IS 1:30 AM, I
deserve a little break;-).

The right solution is adaptation (PEP 246, Eby's PyProtocols, etc), but
until we can convince Guido of that, so that adaptation becomes
widespread, duck typing, and occasionally (when needed) "accurate LBYL"
remain the best approach.

And don't forget unit-tests -- see Robert Martin's now-famous article at
http://www.artima.com/weblogs/viewpost.jsp?thread=4639 .
Alex
Jul 18 '05 #35

P: n/a
Dan Perl <da*****@rogers.com> wrote:
I have a question here, as we are discussing now protocol interfaces vs.
inheritance. Is using a class that implements a protocol without inheriting
from a base class still "polymorphism"?
``still'' is inappropriate here. It is _fully_ polymorphism, of course.
It's known as signature-based polymorphism. C++ has it in templates,
only, where it's the basis of the whole power of the standard library
containers, algorithms, iterators, etc. Python has it everywhere,
except where some coder breaks everything with 'isinstance' or the like.
There are probably many definitions
for polymorphism and probably all those definitions can be interpreted in
such a way that they accept also protocols. But what I would like to hear
Otherwise they're very broken and useless definitions.
is what is the general opinion of people who use python. I am biased
because I come from a C++ and Java background and I am still used to a
certain practical meaning for "polymorphism".
One that doesn't apply to C++ templates?!
But it's beginning to dawn on
me that it is only a bias and polymorphism does apply also to python
protocol interfaces. Is that generally accepted?


Yep.
Alex
Jul 18 '05 #36

P: n/a
Christophe Cavalaria <ch*************@free.fr> wrote:
If your programs need that much dynamic_cast to work, then your programs are
bad.
Wrong. Look at the absolute mess that the Visitor design pattern is,
and how Robert Martin made it much less horrible by turning it into
Dynamic Visitor -- which needs so much dynamic_cast it hurts.

In Java, you're dynamic_cast'ing all of the time in a totally obscure
way, each time you're pulling stuff out of a containter.

SomeSillyType foo = (SomeSillyType) (mycontainer.getsomething());

since the container's method returns Object, the cast is dynamic -- only
checked at runtime. C++, and the latest Java, give up OO in favour of
another paradigm (generic programming) to deal with this, and if this
isn't an admission that their "static" (ha!) typesystem doesn't work
with OOP properly, I don't know what would count as one.
Besides, it's easy to do a 'safe' dynamic_cast, just assert that the
pointer you get isn't 0 after the cast.


or catch the dynamically generated exception in Java's case, sure. You
can "easily" implement what a decent language would have in the first
place... except that co-variance and counter-variance continue to dog
every effort to make a decent "static OOP typesystem", of course...:-)
Alex
Jul 18 '05 #37

P: n/a
Dirk Thierbach <dt********@gmx.de> wrote:
Donn Cave <do**@drizzle.com> wrote:
Quoth Jacek Generowicz <ja**************@cern.ch>:
Specifically, dynamic polymorphism is impossible without dynamic
typing.
I haven't heard the term "dynamic typing" in this context,


Sorry, that should have been "dynamic polymorphism".

- Dirk
Jul 18 '05 #38

This discussion thread is closed

Replies have been disabled for this discussion.