Looking for the best way to translate an idiom

P

Paul Moore

I'm translating some code from another language (Lua) which has
multiple function return values. So, In Lua, it's possible to define a
function

function f()
return 1,2,3
end

which returns 3 values. These can then be used/assigned by the caller:

a,b,c = f()

So far, much like Python, but the key difference is that in Lua,
excess arguments are ignored - so you can do

a = f()
or even
a = f() + 1

where in the latter, a is 2 (as addition only needs 1 argument, so the
extra ones are discarded).

This results in the code which I'm trying to translate having
functions which return multiple arguments, the first of which is the
"main" result, and the following values are "extra" results
(specifically, the position at which a match is found, followed by the
"captured" values of the match).

I'm trying to find a natural equivalent in Python. So far, I've
considered the following:

- return a tuple (pos, captures). This is messy, because you end up
far too often unpacking the tuple and throwing away the second value
- return an object with pos and captures attributes. This is better,
as you can do match(...).pos to get the position alone, but it doesn't
really feel "pythonic" (all those calls with .pos at the end...)
- have 2 calls, one to return just the position, one to return both.
This feels awkward, because of the 2 method names to remember.

To make things worse, Lua indexes strings from 1, so it can use a
position of 0 to mean "no match" - so that a simple "did it match?"
test looks like if (match(....))... - where in Python I need if
(matchpos(...) != -1)... (Although if I return a match object, I can
override __nonzero__ to allow me to use the simpler Lua form).

Can anyone suggest a good idiom for this situation? At the moment, I'm
returning a match object (the second option) which seems like the
least bad of the choices (and it mirrors how the re module works,
which is somewhat useful). But I'd like to know what others think. If
you were using a pattern matching library, what interface would you
prefer?

I suspect my intuition isn't accurate here, as most of the use I've
made of the library is in writing tests, which isn't typical use :-(

Thanks for any assistance.

Paul
 
B

Bruno Desthuilliers

Paul Moore a écrit :
I'm translating some code from another language (Lua) which has
multiple function return values. So, In Lua, it's possible to define a
function

function f()
return 1,2,3
end

which returns 3 values. These can then be used/assigned by the caller:

a,b,c = f()

So far, much like Python, but the key difference is that in Lua,
excess arguments are ignored - so you can do

a = f()
or even
a = f() + 1

where in the latter, a is 2 (as addition only needs 1 argument, so the
extra ones are discarded).

This results in the code which I'm trying to translate having
functions which return multiple arguments, the first of which is the
"main" result, and the following values are "extra" results
(specifically, the position at which a match is found, followed by the
"captured" values of the match).

I'm trying to find a natural equivalent in Python. So far, I've
considered the following:

- return a tuple (pos, captures). This is messy, because you end up
far too often unpacking the tuple and throwing away the second value

if you only want the first returned value, you can just apply a slice:

def f():
return 1,2,3

a = f()[0] + 1
- return an object with pos and captures attributes. This is better,
as you can do match(...).pos to get the position alone, but it doesn't
really feel "pythonic" (all those calls with .pos at the end...)

FWIW, Python 2.6 has NamedTuple objects...
- have 2 calls, one to return just the position, one to return both.
This feels awkward, because of the 2 method names to remember.

You forgot one solution: passing a flag to the function to specify if
you want only the main result or the extra ones as well, and give this
flag the appropriate default value depending on most common use case.
To make things worse, Lua indexes strings from 1, so it can use a
position of 0 to mean "no match" - so that a simple "did it match?"
test looks like if (match(....))... - where in Python I need if
(matchpos(...) != -1)... (Although if I return a match object, I can
override __nonzero__ to allow me to use the simpler Lua form).

Can anyone suggest a good idiom for this situation? At the moment, I'm
returning a match object (the second option) which seems like the
least bad of the choices (and it mirrors how the re module works,
which is somewhat useful). But I'd like to know what others think. If
you were using a pattern matching library, what interface would you
prefer?

The one that happens to be the most simple for the most common usecase,
while still providing support for more advanced stuff.
I suspect my intuition isn't accurate here, as most of the use I've
made of the library is in writing tests, which isn't typical use :-(

So perhaps you should start writing some real-life code ?
Thanks for any assistance.

Not sure I've been that helpful. Sorry...
 
P

Paul Moore

if you only want the first returned value, you can just apply a slice:

def f():
    return 1,2,3

a = f()[0] + 1

Hmm, true. I'm not sure it's any less ugly, though :)
FWIW, Python 2.6 has NamedTuple objects...

I know, but I want to target 2.5+ (I still have a number of systems
running 2.5)
You forgot one solution: passing a flag to the function to specify if
you want only the main result or the extra ones as well, and give this
flag the appropriate default value depending on most common use case.

True, that's another option. It might work, I'll think about it.
The one that happens to be the most simple for the most common usecase,
while still providing support for more advanced stuff.

Exactly. I'll have to think some more about real use cases.
So perhaps you should start writing some real-life code ?

:) Once I have a library that's functional enough to do so, I may
well. Chicken and egg situation, to some extent... And I don't really
feel like switching to Lua just to get a feel for how the library gets
used!
Not sure I've been that helpful. Sorry...

Any feedback helps. Thanks for taking the time.

Paul.
 
S

Steve Holden

Bruno Desthuilliers wrote:
[...]
if you only want the first returned value, you can just apply a slice:

def f():
return 1,2,3

a = f()[0] + 1
<nit>That isn't a slice, it's indexing</nit>

regards
Steve
 
B

Bruno Desthuilliers

Steve Holden a écrit :
Bruno Desthuilliers wrote:
[...]
if you only want the first returned value, you can just apply a slice:

def f():
return 1,2,3

a = f()[0] + 1
<nit>That isn't a slice, it's indexing</nit>

Yeps, sorry - and thanks for the correction.
 
F

Fuzzyman

if you only want the first returned value, you can just apply a slice:
def f():
    return 1,2,3
a = f()[0] + 1

Hmm, true. I'm not sure it's any less ugly, though :)
FWIW, Python 2.6 has NamedTuple objects...

I know, but I want to target 2.5+ (I still have a number of systems
running 2.5)

There is a Python implementation of NamedTuple on the Python cookbook
that is compatible with Python 2.5.

Michael Foord
http://www.ironpythoninaction.com/
 
J

James Stroud

Paul said:
I'm translating some code from another language (Lua) which has
multiple function return values. So, In Lua, it's possible to define a
function

function f()
return 1,2,3
end

which returns 3 values. These can then be used/assigned by the caller:

a,b,c = f()

So far, much like Python, but the key difference is that in Lua,
excess arguments are ignored - so you can do

py> class mytuple(tuple):
def magic(self, astr):
names = astr.split()
for name, val in zip(names, self):
globals()[name] = val
....
py> t = mytuple((1,2,3))
py> t.magic('a b')
py> a
1
py> b
2

James
 
J

James Stroud

James said:
py> class mytuple(tuple):
def magic(self, astr):
names = astr.split()
for name, val in zip(names, self):
globals()[name] = val
...
py> t = mytuple((1,2,3))
py> t.magic('a b')
py> a
1
py> b
2

James


In case its not obvious:

def f():
return mytuple((1,2,3))

f().magic('a b')

You can parameterize lobbing off values, or use more magic:

py> class mytuple(tuple):
.... def magic(self, astr):
.... names = astr.split()
.... for name, val in zip(names, self):
.... globals()[name] = val
.... def __add__(self, other):
.... if isinstance(other, tuple):
.... return mytuple(tuple.__add__(self, other))
.... else:
.... return mytuple(self[other:])
....
py> t = mytuple((1,2,3))
py> t + 1
(2, 3)
py> def f():
return mytuple((1,2,3))
....
py> (f() + 1).magic('a b')
py> a
2
py> b
3

It's not as bad as a lot of the cynics are about to tell you it is. If
it has any badness at all, it's because of the need (according to what I
infer from your specification) to use the global namespace. If you want
to modify a "more local" namespace, you can use some stack frame
inspection to do some real magic:

py> import inspect
py> class mytuple(tuple):
def magic(self, astr):
names = astr.split()
for name, val in zip(names, self):
inspect.stack()[1][0].f_locals[name] = val
def __add__(self, other):
if isinstance(other, tuple):
return mytuple(tuple.__add__(self, other))
else:
return mytuple(self[other:])
....
py> def f():
return mytuple((6,7,8))
....
py> (f() + 1).magic('a b')
py> a
7
py> b
8

James
 
D

drobinow

I'm translating some code from another language (Lua) which has
multiple function return values. So, In Lua, it's possible to define a
function

    function f()
        return 1,2,3
    end

which returns 3 values. These can then be used/assigned by the caller:

    a,b,c = f()

So far, much like Python, but the key difference is that in Lua,
excess arguments are ignored - so you can do

    a = f()
or even
    a = f() + 1

where in the latter, a is 2 (as addition only needs 1 argument, so the
extra ones are discarded).

This results in the code which I'm trying to translate having
functions which return multiple arguments, the first of which is the
"main" result, and the following values are "extra" results
(specifically, the position at which a match is found, followed by the
"captured" values of the match).

I'm trying to find a natural equivalent in Python. So far, I've
considered the following:

- return a tuple (pos, captures). This is messy, because you end up
far too often unpacking the tuple and throwing away the second value
- return an object with pos and captures attributes. This is better,
as you can do match(...).pos to get the position alone, but it doesn't
really feel "pythonic" (all those calls with .pos at the end...)
- have 2 calls, one to return just the position, one to return both.
This feels awkward, because of the 2 method names to remember.

To make things worse, Lua indexes strings from 1, so it can use a
position of 0 to mean "no match" - so that a simple "did it match?"
test looks like if (match(....))... - where in Python I need if
(matchpos(...) != -1)... (Although if I return a match object, I can
override __nonzero__ to allow me to use the simpler Lua form).

Can anyone suggest a good idiom for this situation? At the moment, I'm
returning a match object (the second option) which seems like the
least bad of the choices (and it mirrors how the re module works,
which is somewhat useful). But I'd like to know what others think. If
you were using a pattern matching library, what interface would you
prefer?

I suspect my intuition isn't accurate here, as most of the use I've
made of the library is in writing tests, which isn't typical use :-(

Thanks for any assistance.

Paul

I'm baffled by this discussion.
What's wrong with
a, dontcare, dontcare2 = f()
a = a + 1

Simple, clear, and correct.
 
J

James Stroud

I'm baffled by this discussion.
What's wrong with
a, dontcare, dontcare2 = f()
a = a + 1

Simple, clear, and correct.

1. This can't apply to a generalized f() that may return an arbitrary
number of arguments >= len(num_assignments_you_care_about).
2. The example chosen was misleading. You don't want to add 1 to the
first element of the tuple, you want to move the "start" of the returned
tuple up 1 and start assignment on the left from there.

James
 
J

James Stroud

James said:
inspect.stack()[1][0].f_locals[name] = val

I just double checked this. Because of the way locals are implemented in
cPython, this won't have the desired affect.

James
 
A

Aahz

In case its not obvious:

Ah, so that's where Bruno's extra apostrophe came from! ;-)


(Sorry about the spelling flame, but seeing three posts in quick
succession with incorrect spelling of its/it's pushed me into making a
public comment.)
 
J

James Stroud

Aahz said:
Ah, so that's where Bruno's extra apostrophe came from! ;-)


(Sorry about the spelling flame, but seeing three posts in quick
succession with incorrect spelling of its/it's pushed me into making a
public comment.)

Yes. I think it was the British who decided that the apostrophe rule for
"it" would be reversed from normal usage relative to just about every
other noun. I'm not sure the purpose--maybe it was to give compulsive
proofreaders a raison d'etre.
 
I

I V

Yes. I think it was the British who decided that the apostrophe rule for
"it" would be reversed from normal usage relative to just about every
other noun. I'm not sure the purpose--maybe it was to give compulsive
proofreaders a raison d'etre.

It's because "it" is a pronoun; you don't put an apostrophe in "his,"
either.
 
J

James Stroud

Ben said:
It also seems an indefensible claim to say that anyone “decided†it
would be that way, especially “the Britishâ€.


Or, more generally: Pronouns, which are different in just about every
other way from other nouns, are different in this way also. Is that
about right?

Can we start talking about python again?

--
James Stroud
UCLA-DOE Institute for Genomics and Proteomics
Box 951570
Los Angeles, CA 90095

http://www.jamesstroud.com
 
M

MRAB

James said:
Yes. I think it was the British who decided that the apostrophe rule for
"it" would be reversed from normal usage relative to just about every
other noun. I'm not sure the purpose--maybe it was to give compulsive
proofreaders a raison d'etre.
No possessive pronoun has an apostrophe.

Contractions of "is", etc, do, whether for nouns or pronouns.
 

Ask a Question

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

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

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,743
Messages
2,569,478
Members
44,899
Latest member
RodneyMcAu

Latest Threads

Top