Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Function Overloading Without Typechecking

6 views
Skip to first unread message

Michael Chermside

unread,
Jan 15, 2002, 12:37:39 PM1/15/02
to
I have to confess... I come to Python from the world of static typing,
and am still adjusting my mindset to dynamic typing. But I'm interested
in learning how to use Python's dynamic typing (as opposed to weak
typing) effectively. Frequent comments from Alex Martelli and others
suggest that if I really want to write pythonic-ly, I will try NOT to
check the types of the values I am passed. Just use it, and unless the
caller passed me the wrong kind of thing, it'll work.

Well, that's fine, but I have gotten used to having method overloading
in Java and C++. So I like to write things like this:

def spamThemAll(stuffToSpam):
if type(stuffToSpam) in types.StringTypes:
stuffToSpam = stuffToSpam.split()
for s in stuffToSpam:
spam(s)

That way I can invoke the method with two different kinds of values:

spamThemAll( ['a', 'b', 'c'] )
spamThemAll( 'a b c' )

... and it automatically converts. Convenient, but dangerous. For
instance, if I had used "type(stuffToSpam) == types.String" as my test,
then it wouldn't have worked for unicode strings. And this version will
probably break on some other type which I haven't prepared for (UserString).

So what do I do? I can think of a few possibilities:

1) Just don't do it. Only accept one type and make the caller
convert.

[But this is awkward for the caller!]

2) Use optional keyword arguments:
def spamThemAll( str=None, seq=None ):
if str is not None:
seq = str.split()
for s in seq:
spam(s)

[But this makes the caller use keyword args always!]

3) Create multiple methods:
def spamThemAll_withString(str):
return spamThemAll_withSeq( str.split() )

[But this creates annoying extra functions!]

How do those of you who are more experienced with this handle this issue?

-- Michael Chermside

Emile van Sebille

unread,
Jan 15, 2002, 1:21:00 PM1/15/02
to

"Michael Chermside" <mch...@destiny.com> wrote in message
news:mailman.1011116242...@python.org...

I look to make a function that will do the right thing for the
allowable args and then use it. For your example, something
like:

>>> def tolist(arg):
return filter(None, [a.strip() for a in list(arg)])

>>> tolist('a b c')


['a', 'b', 'c']

>>> tolist(['a','b','c'])


['a', 'b', 'c']

I sometimes run into situations where an obvious answer doesn't
come to mind, or when certain types don't play nice, and then I
revert to type checking as well. ;-)


Emile van Sebille
em...@fenx.com

---------


Fernando Pérez

unread,
Jan 14, 2002, 7:10:12 AM1/14/02
to
Emile van Sebille wrote:


> I look to make a function that will do the right thing for the
> allowable args and then use it. For your example, something
> like:
>
>>>> def tolist(arg):
> return filter(None, [a.strip() for a in list(arg)])
>
>>>> tolist('a b c')
> ['a', 'b', 'c']
>>>> tolist(['a','b','c'])
> ['a', 'b', 'c']
>

But your version does:
In [21]: tolist('a bcd e')
Out[21]=
['a', 'b', 'c', 'd', 'e']

while I think he'd want:

In [22]: 'a bcd e'.split()
Out[22]=
['a', 'bcd', 'e']

I'm *very* interested in an elegant, 'pythonic' solution to this, as I tend
to do a lot of what the original poster does and would love to learn of a
better, more robust way to do it.

Thanks,

f.

Alex Martelli

unread,
Jan 15, 2002, 3:29:01 PM1/15/02
to
Michael Chermside wrote:
...

> Well, that's fine, but I have gotten used to having method overloading
> in Java and C++. So I like to write things like this:
>
> def spamThemAll(stuffToSpam):
> if type(stuffToSpam) in types.StringTypes:
> stuffToSpam = stuffToSpam.split()
...

> So what do I do? I can think of a few possibilities:
...

> How do those of you who are more experienced with this handle this issue?

def spamThemAll(stuffToSpam):
# split if splittable
try: stuffToSpam = stuffToSpam.split()
except AttributeError: pass
# etc

General approach: decide what it means in context for an object x to "be
a string". In this specific context, it seems to mean "have a .split()
method", so just try to use that method as you think it should be usable,
and use a try/except to handle the possibility that no such method is
in fact there. Situations of this kind make up a majority of cases.

Another case, rarer but by no means rare, is something like the need to
treat a string as 'atomic' (a "scalar") as opposed to lists, tuples, and
other "true" sequences (while in Python itself, a string is a sequence).

For that need I generally synthesize a simple test:

try: mysteryObject+''
except TypeError:
try: for x in mysteryObject: break
except TypeError: isSequence = 0
else: isSequence = 1
else: isSequence = 0 # string-like object -> non-sequence

Similarly, I might 'test for Numeric' by similarly trying a + 0, etc.


Rarest, but still interesting (I did hit upon it once or twice) is the case
I address in a Cookbook recipe,
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52291

I guess it was a bit silly of me to touch only upon the rarest of cases
of "type-checking" needs rather than directly address the typical
interesting cases best covered by "easier to get forgiveness than
permission" (try/except) rather than "look before you leap" (type
testing) -- but I was keen to show that even when it _seems_ type
checking is the only alternative, it still _isn't_ (...mostly...). And, the
need I cover in the recipe had in fact come up recently for me when
I posted it -- sometimes unusual occurrences are closer to our
attention just because of their unusual nature.

Still, there are plenty of examples of "egfp" and its preferability
to "lbyl" in the Cookbook, albeit not as sharp as recipe 52291:-).


Alex

Quinn Dunkan

unread,
Jan 16, 2002, 3:28:23 PM1/16/02
to
On Tue, 15 Jan 2002 12:37:39 -0500, Michael Chermside <mch...@destiny.com>
wrote:

>Well, that's fine, but I have gotten used to having method overloading
>in Java and C++. So I like to write things like this:
>
> def spamThemAll(stuffToSpam):
> if type(stuffToSpam) in types.StringTypes:
> stuffToSpam = stuffToSpam.split()
> for s in stuffToSpam:
> spam(s)
>
>That way I can invoke the method with two different kinds of values:
>
> spamThemAll( ['a', 'b', 'c'] )
> spamThemAll( 'a b c' )
>
>... and it automatically converts. Convenient, but dangerous. For
>instance, if I had used "type(stuffToSpam) == types.String" as my test,
>then it wouldn't have worked for unicode strings. And this version will
>probably break on some other type which I haven't prepared for (UserString).
>
>So what do I do? I can think of a few possibilities:
>
> 1) Just don't do it. Only accept one type and make the caller
> convert.
>
> [But this is awkward for the caller!]

Not in the above example:

spamThemAll('a b c'.split())

I've never run into a situation where I have to do much type conversion,
except string->whatever at the input stage.

> 2) Use optional keyword arguments:
> def spamThemAll( str=None, seq=None ):
> if str is not None:
> seq = str.split()
> for s in seq:
> spam(s)
>
> [But this makes the caller use keyword args always!]

Yeah, and it's gross besides because now you have a bunch of mutually
exclusive keywords.

> 3) Create multiple methods:
> def spamThemAll_withString(str):
> return spamThemAll_withSeq( str.split() )
>
> [But this creates annoying extra functions!]

Yeah, better to have a thought-out seperate set of functions to convert types
if you need to do lots of it for whatever reason, rather than lots of
*_from_foo *_to_bar. Combinatorial explosion, etc.

>How do those of you who are more experienced with this handle this issue?

I'd go for #1, but I almost never feel the need to play games with types like
that. The exception is occaisionally treating singleton as [singleton] in a
function that wants complicated nested structure, e.g.:

((attr1, (f1, ('bar', 'baz', 'faz')),
(attr2, (f2, ('zaf', 'zab')))
(attr3, (f3, 'qux')))) # last 'qux' short for ('qux',)

It's like lisp which lets you do

(let ((x nil)) ...)
or
(let ((x)) ...)
or
(let (x) ...)

Dave Kuhlman

unread,
Jan 24, 2002, 12:39:52 PM1/24/02
to
Michael Chermside <mch...@destiny.com> wrote:
>
> I have to confess... I come to Python from the world of static typing,
> and am still adjusting my mindset to dynamic typing. But I'm interested
> in learning how to use Python's dynamic typing (as opposed to weak
> typing) effectively. Frequent comments from Alex Martelli and others
> suggest that if I really want to write pythonic-ly, I will try NOT to
> check the types of the values I am passed. Just use it, and unless the
> caller passed me the wrong kind of thing, it'll work.
>

I'm rigid. I believe that overloaded methods are non-Pythonic.
They are confusing and non-explicit.

Overloaded functions say to me "Don't worry about the type of the
arguments; this function will do the right thing. You don't need to
understand this.

Overloaded functions hide things. The Python attitude says to make
things explicit. Therefore, overloaded functions are non-Pythonic.

The designers of Java omitted overloaded operators, I believe,
because overloading operators enables programmers to write
confusing, obfuscated code. Overloaded functions and methods enable
programmers to do the same thing.

Sorry for the rant. I just hate it when people try to write code
so that I can't understand it.

And, no, I'm not paranoid. OK, maybe a little in the late
afternoon.

- Dave


--
Dave Kuhlman
dkuh...@rexx.com
http://www.rexx.com/~dkuhlman


-----= Posted via Newsfeeds.Com, Uncensored Usenet News =-----
http://www.newsfeeds.com - The #1 Newsgroup Service in the World!
-----== Over 80,000 Newsgroups - 16 Different Servers! =-----

0 new messages