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

PEP 322: Reverse Iteration (REVISED, please comment)

P: n/a
Based on your extensive feedback, PEP 322 has been completely revised.
The response was strongly positive, but almost everyone preferred
having a function instead of multiple object methods. The updated
proposal is at:

www.python.org/peps/pep-0322.html

In a nutshell, it proposes a builtin function that greatly simplifies reverse
iteration. The core concept is that clarity comes from specifying a
sequence in a forward direction and then saying, "inreverse()":

for elem in inreverse(seqn):
. . .

Unlike seqn[::-1], this produces a fast iterator instead of a full reversed
copy.

Discussions with Guido made it clear that inreverse() will not be extended
to cover all iterables. The proposal is about simplicity, expression, and
performance. As such, it would be counter-productive to take in a general
iterable, run it to completion, save the data in memory, and then iterate
over the data in reverse.
Raymond Hettinger
Jul 18 '05 #1
Share this Question
Share on Google+
31 Replies


P: n/a
Raymond Hettinger wrote:

Discussions with Guido made it clear that inreverse() will not be extended
to cover all iterables. The proposal is about simplicity, expression, and
performance. As such, it would be counter-productive to take in a general
iterable, run it to completion, save the data in memory, and then iterate
over the data in reverse.


Which of course can easily be done explicitely:

# Make some iterable unsuitable for inreverse
a = iter("Hello")

# Create a list that can be in'reversed explicitely
for i in inreverse(list(a)):
print i
As always, explicit is better than implicit ;-)

Maybe something alike should go into the docs...
- Werner

Jul 18 '05 #2

P: n/a
Hi,

was it considered to name the function just "reverse"?

This would read very naturally to me:

for elem in reverse(seq):
print elem
Also if you use it in function calls it looks good to me:

x = dostuff(reverse(seq), a, b)
Another possibility would be to have a static method for iter:

for elem in iter.reversed(sed):
print elem

(in that case I'd use an attribute rather than a verb, because
the generated iter(ator) will be reverse_d_).

The static method approach would clearly document that the
result is an iterator (which none of the other names proposed
really does IMHO)
In general, I'm +1 on the PEP
regards

Werner

Jul 18 '05 #3

P: n/a
py****@rcn.com (Raymond Hettinger) writes:
In a nutshell, it proposes a builtin function that greatly simplifies reverse
iteration. The core concept is that clarity comes from specifying a
sequence in a forward direction and then saying, "inreverse()":

for elem in inreverse(seqn):
. . .
I've got to say, I really don't like the name. Particularly, the
repeated "in" in "in inreverse..." strikes me as ugly.
Discussions with Guido made it clear that inreverse() will not be extended
to cover all iterables. The proposal is about simplicity, expression, and
performance. As such, it would be counter-productive to take in a general
iterable, run it to completion, save the data in memory, and then iterate
over the data in reverse.


I like the proposal, it's simple and expressive. But please, change
the name! I assume there's a good reason why the obvious "reverse()"
wasn't chosen. Of the alternatives in the PEP, backwards() doesn't
seem to read nicely (but it's better than inreverse!!!), and ireverse
is OK, but makes me think that it should be in itertools (or all the
itertools should be builtin). Some other possibilities:

rev(seq) - probably too abbreviated
reversed(seq) - adjective rather than verb, but reads well

As usual, the semantics is perfectly acceptable, but we'll have a nice
long argument over the name :-)

Paul.
--
This signature intentionally left blank
Jul 18 '05 #4

P: n/a
Werner Schiendl wrote:
...
Another possibility would be to have a static method for iter:

for elem in iter.reversed(sed):
print elem

(in that case I'd use an attribute rather than a verb, because
the generated iter(ator) will be reverse_d_).

The static method approach would clearly document that the
result is an iterator (which none of the other names proposed
really does IMHO)


I like this one! Another advantage is, one fewer built-in.

Problem is, iter is currently a built-in function, not a type...
Alex

Jul 18 '05 #5

P: n/a
+1
"Raymond Hettinger" <py****@rcn.com> wrote in message
news:5d**************************@posting.google.c om...
Based on your extensive feedback, PEP 322 has been completely revised.
The response was strongly positive, but almost everyone preferred
having a function instead of multiple object methods. The updated
proposal is at:

www.python.org/peps/pep-0322.html

In a nutshell, it proposes a builtin function that greatly simplifies reverse iteration. The core concept is that clarity comes from specifying a
sequence in a forward direction and then saying, "inreverse()":

for elem in inreverse(seqn):
. . .

Unlike seqn[::-1], this produces a fast iterator instead of a full reversed copy.

Discussions with Guido made it clear that inreverse() will not be extended
to cover all iterables. The proposal is about simplicity, expression, and
performance. As such, it would be counter-productive to take in a general
iterable, run it to completion, save the data in memory, and then iterate
over the data in reverse.
It's certainly clear enough, and I like it in general.

I'd appreciate a bit of discussion about why reverse() was rejected as
the name, though.

John Roth

Raymond Hettinger

Jul 18 '05 #6

P: n/a
In article <5d**************************@posting.google.com >, Raymond Hettinger wrote:
for elem in inreverse(seqn):
. . .


Is there an echo in inhere? +1 +1otherwise... =)

--
..:[ dave benjamin (ramenboy) -:- www.ramenfest.com -:- www.3dex.com ]:.
: d r i n k i n g l i f e o u t o f t h e c o n t a i n e r :
Jul 18 '05 #7

P: n/a
Raymond Hettinger wrote:
Based on your extensive feedback, PEP 322 has been completely revised.


Personally, for some tasks I have used reverse iteration quite a bit.

My use case has _always_ been that I want to modify a list (e.g.
adding or deleting items) in situ. In all other cases which I can
recall, I have quite happily iterated in the forward direction.

(Maybe this is because I am not afraid to call list.reverse(), and
insertion/deletion is the only use case where this doesn't really help.)

This means that the implementation restrictions described in the PEP are
fine with me (because a list has the requisite methods).

However, this also means that I am most interested in how the enumerate()
interface will work with this (because the current list index must be
known in order to properly add or delete items). enumerate() was
mentioned in the PEP but not discussed in any real depth.

My personal preference (since this is the only use case which I have
ever encountered) would be to enhance enumerate in a reasonable fashion,
and not necessarily even have (or at least care about) the reverse/ireverse/
whatever name underneath enumerate().

I will happily accept whatever syntax comes along (unless the enumerate
counts 0,1,2,3... while the index of the adjacent list object is decremented
[-1],[-2],[-3]... :)

BUT, for sheer usability I think it would be wonderful if enumerate()
took an optional second argument to let it know you want to go backward.
The most general syntax for this second argument would naturally be
a slice:

for idx,obj in enumerate(mylist,-1:-1:-1):
do whatever

but if people think this is too ugly something simpler could be
substituted (but think about the useful possibilities of a slice
argument here before you complain too hard about the ugliness...)

Some may argue that this restricts enumerate() to certain kinds of
iterators when using the slice (and thus adds a lot of special casing),
but it may be that, realistically, if ireverse() is going to have this
special casing, and enumerate() is going to work with ireverse(), you
already have the same problem (unless the user is expected to work
with the enumerate counting backwards from the actual list index).
I cannot say this for sure, however, because I did not see (or perhaps
read closely enough) the part of the PEP which discussed enumerate.

Regards,
Pat
Jul 18 '05 #8

P: n/a
Alex Martelli wrote:
Werner Schiendl wrote:
...
Another possibility would be to have a static method for iter:

for elem in iter.reversed(sed):
print elem


I like this one! Another advantage is, one fewer built-in.

Problem is, iter is currently a built-in function, not a type...


An additional advantage that came to my mind is that it would fit
Guido's preference about how to (possible) implement inline sort.

having

L = list.sorted(seq)

i = iter.reversed(seq)

seems very consistent to me.
best regards

Werner

Jul 18 '05 #9

P: n/a
> The static method approach would clearly document that the
result is an iterator (which none of the other names proposed
really does IMHO)
Better name are welcome!

Moving it somewhere else is not open. It is proposed as a builtin for a
reason -- it is a core looping tool like zip() or enumerate() and it is meant
to simplify rather than complicate code.

Static methods, class methods, and weird descriptors be darned, this is
not an exercise in how weirdly it can be implemented just to avoid
having a builtin.

Would everything be somehow better if Alex's wonderful sum() had
been implemented as a int.sum() classmethod or was tucked way in
another module? Of course not! Likewise, would zip() or enumerate()
be successful solutions to lock-step iteration and the loop-counter
problems if they were iter.zip() and iter.enumerate()? No, of course not.
Let's put an end to this silliness right now. The idea is offered as a
builtin or not at all; otherwise, the existing [::-1] starts to look better.

In general, I'm +1 on the PEP


Thanks! I certain it will make code more readable and reviewable.

The challenge with a PEP this simple is that experts feel this
overpowering urge to apply all their know-how and transform
in to something other than a clean, fast, simple solution.
Raymond Hettinger
Jul 18 '05 #10

P: n/a

"Werner Schiendl" <n1*******************@neverbox.com> wrote in
message news:3f********@brateggebdc5.br-automation.co.at...
Problem is, iter is currently a built-in function, not a type...

Since iter() constructs and returns a type 'iterator' object, I
expected that it might be a type object, just like int(), etc. If it
were turned into one, like int(), etc, have been, then
L = list.sorted(seq)
i = iter.reversed(seq)
seems very consistent to me.


would be exactly parallel and hence very consistent.

Unless there is a good reason to not make iter a type object, then
making it so could be part of the suggested implementation of the PEP.

Terry J. Reedy
Jul 18 '05 #11

P: n/a
> Since iter() constructs and returns a type 'iterator' object, I
expected that it might be a type object, just like int(), etc. If it
were turned into one, like int(), etc, have been, then . . . Unless there is a good reason to not make iter a type object, then
making it so could be part of the suggested implementation of the PEP.


iter() is a factory function that can return all kinds of things:
from random import random
iters = iter('str'), iter(['list']), iter(dict(a=1)), iter(random, None)
map(type, iters)

[<type 'iterator'>, <type 'listiterator'>, <type 'dictionary-iterator'>, <type
'callable-iterator'>]

Let's see if we can get back to the merits of the pep.
Looking at your own code, can you verify that ireverse()
is an improvement over what you have now.
Raymond Hettinger

Jul 18 '05 #12

P: n/a
Raymond Hettinger wrote:

Better name are welcome!

Moving it somewhere else is not open. It is proposed as a builtin for a
reason -- it is a core looping tool like zip() or enumerate() and it is meant
to simplify rather than complicate code.

Well, that of course limits the choice :-)

Let's see what's available:

inreverse --

seems a little clumsy when used in a "for item in inreverse(seq):",
which is probably the place where it's used most often.

The term itself seems reasonable to me.

ireverse --

IMHO that would suggest it belonged into itertools - which it
according to your explanatation does NOT.

So I'd rather 'reserve' that term if one day something there
is needed (or end up with iireverse ;-)

To my non-native-english eyes it's also a bit ugly.

And concerning simplicity, how to explain a newby the "i"
and why it's with "reverse" but not with "enumerate".

reverse --

According to the PEP this is not for discussion, although I'm
with John Roth in that I think this were worth some more
discussion.

I do not use Python too much (yet as often as possible, but
"job work" is done with something else.
Still I cannot imagine why anyone should confuse a method of
a list object (that does return None) with a builtin function
that returns "something" to walk through an arbitrary sequence
in reverse.

This one fits IMHO also best to the most-direct relatives you
mention in your post (zip, enumerate) in that it is a verb
without any prefix.

backwards --

I cannot remember having seem that term in any library or
programming language, it feels strange to me.

But it's still correct, a verb, fits the style IMHO.
Like it more than ireverse.
So my order from most-prefered to least-prefered is:

reverse

inreverse

backwards

ireverse

The challenge with a PEP this simple is that experts feel this
overpowering urge to apply all their know-how and transform
in to something other than a clean, fast, simple solution.


I'd not actually claim myself a Python expert ;-)
best regards

Werner

Jul 18 '05 #13

P: n/a
Raymond Hettinger wrote:
Since iter() constructs and returns a type 'iterator' object, I
expected that it might be a type object, just like int(), etc. If it
were turned into one, like int(), etc, have been, then . . .
Unless there is a good reason to not make iter a type object, then
making it so could be part of the suggested implementation of the PEP.


iter() is a factory function that can return all kinds of things:
from random import random
iters = iter('str'), iter(['list']), iter(dict(a=1)), iter(random,
None) map(type, iters)

[<type 'iterator'>, <type 'listiterator'>, <type 'dictionary-iterator'>,
[<type
'callable-iterator'>]


So why couldn't all these iterators inherit from iter just as unicode and
str do from basestring?
Let's see if we can get back to the merits of the pep.
I respect that you want to keep this discussion focused, and itertools is my
favourite new package - but sometimes il faut reculer pour mieux sauter :-)
Looking at your own code, can you verify that ireverse()
is an improvement over what you have now.


While I'm zipping and enumerating all the time, reversing is rare, so I'm
not desperately awaiting this builtin.
The most common usecase seems iteration over a sequence that is mutated in
the process:

class mutate(object):
def __init__(self, alist):
self.index = -1
self.alist = alist
def next(self):
self.index += 1
try:
self.alist[self.index]
except IndexError:
raise StopIteration
return self
def value(self):
return self.alist[self.index]
def __iter__(self):
return self
def delete(self):
del self.alist[self.index]
self.index -= 1

sample = range(10)
for item in mutate(sample):
if item.value() in [3,5,7]:
item.delete()
print sample

I'm sure that the above approach can be improved (in particular, it must not
rely on an index, i. e. random access) as I'm sure that it makes the
coder's intention clearer than the "reverse to enable deletion" idiom.

Peter
Jul 18 '05 #14

P: n/a
Peter Otten wrote:
Raymond Hettinger wrote: ,,,
iter() is a factory function that can return all kinds of things:


Right, and thus it must stay. Oh well.
So why couldn't all these iterators inherit from iter just as unicode and
str do from basestring?


They might, but then calling iter() should complain that iter is an
abstract baseclass, not instantiable, just like calling basestring()
does. Having an abstract baseclass that DOES return new objects when
called would be very perverse indeed. So, making iter a type is not
really a sensible option, alas.
Alex

Jul 18 '05 #15

P: n/a
Raymond Hettinger wrote:
The static method approach would clearly document that the
result is an iterator (which none of the other names proposed
really does IMHO)
Better name are welcome!

Moving it somewhere else is not open. It is proposed as a builtin for a
reason -- it is a core looping tool like zip() or enumerate() and it is


I don't think it has anywhere like the frequency of use of either.
Static methods, class methods, and weird descriptors be darned, this is
not an exercise in how weirdly it can be implemented just to avoid
having a builtin.
I just don't think it's worth making a built-in.

Would everything be somehow better if Alex's wonderful sum() had
been implemented as a int.sum() classmethod or was tucked way in
another module?
int.sum would be a disaster either way: if it was forced to return
an int it would reduce use cases substantially; if it wasn't it
would be just weird.

math.sum would be arguably equivalent to sum as a built-in -- a
tad less immediately accessible, but perhaps superior in that it
immediately suggests it's about numbers only, so we'd avoid the
icky performance trap you now get with sum(manylists, []) {which
makes sum only 2/3 wonderful at best -- fine with numbers ONLY}.
Of course not! Likewise, would zip() or enumerate()
be successful solutions to lock-step iteration and the loop-counter
problems if they were iter.zip() and iter.enumerate()? No, of course not.
zip is currently an INFERIOR solution for lock-step iteration: your
itertools.izip is much better for that. Unfortunately zip IS
constrained to return a list BUT that fact is not reflected in it
being a classmethod list.zipped (we didn't have classmethods when
zip was introduced, so that wasn't an option). If it had been made
one, then by now we might have a built-in version of izip, sigh.

Your enumerate is excellent, and just as frequently used as izip.
I prefer it as a builtin -- though I wouldn't be heartbroken if it
had been in itertools, but it's better in the builtins _because_
it's so frequently used.

Unfortunately we have too many and inappropriate builtins and they're
not going to go away until 3.0 (years away). This raises the bar for
other not-obviously-indispensable built-ins more than it would be
sensible to do in a hypothetical "greenfield design".

Let's put an end to this silliness right now. The idea is offered as a
builtin or not at all; otherwise, the existing [::-1] starts to look
better.
You're clearly overwrought, so I won't "see" that "silliness" and get
offended by it. If the choice was only to have the reverser (by
whatever name) as a built-in or not at all, I'd be -0 on the reverser --
slightly against, though not enough to bother opposing it. If it
could be sited somewhere appropriate (ok, not as iter.reversed if
that's technically unfeasible -- i did say "appropriate":-) I'd be +1.

The challenge with a PEP this simple is that experts feel this
overpowering urge to apply all their know-how and transform
in to something other than a clean, fast, simple solution.
The crux of our disagreement, namecalling apart, might be highlighted
by your assertion in another short and emotional post:
This is supposed to be something you can teach in the first half-hour


I can't imagine "teaching this in the first half-hour" of any Python
course I teach. There are FAR too many other functions, including
non-builtin ones such as sys.exit, and some in itertools too, that are
more important than this, in my own assessment.

So we disagree about frequence of usage, and consequently "warrantedness"
as a built-in -- big deal. I honestly don't understand how this purely
technical disagreement can explain these intense emotions.

_I_ am supposed to be the Latin, Mediterranean, hot-blooded enthusiast,
after all, and on average I think I cover the role adequately.

Oh well, with the incredible amount of good things you've done for Python
development, I guess you're surely entitled to blow your top occasionally,
if that's your preference. Also, presumably, to get your pet built-in
into the language, as you've slipped it past Guido. As for name, I
therefore second the neatest suggestion I've seen: since it IS a reversed
iter, and we can't directly call it that, then:

reti

Hey, it IS short, so if it's gonna be so heavily used, good thing, no?-)
Alex

Jul 18 '05 #16

P: n/a
On 28 Oct 2003 10:22:44 -0800, py****@rcn.com (Raymond Hettinger) wrote:
Based on your extensive feedback, PEP 322 has been completely revised.
The response was strongly positive, but almost everyone preferred
having a function instead of multiple object methods. The updated
proposal is at:

www.python.org/peps/pep-0322.html

In a nutshell, it proposes a builtin function that greatly simplifies reverse
iteration. The core concept is that clarity comes from specifying a
sequence in a forward direction and then saying, "inreverse()":

for elem in inreverse(seqn):
. . .

Unlike seqn[::-1], this produces a fast iterator instead of a full reversed
copy.
I'm thinking that a concise way to make a fast iterator from any slice expression
could subsume the inreverse functionality. IOW, a generator slice corresponding
in spirit to generator expressions. E.g., for the inreverse use,

for elem in seqn'[::-1]:
. . .

(note the apostrophe in front of the slice notation)

Of course, unlike 'inreverse', you wouldn't be limited to going backwards,

for oddelem in seqn'[1::2]:
. . .

Ok, take a deep breath, here comes some more ;-)

How about generator expressions/slices being able to be evaluated in parallel in a 'for' loop
(with '=' instead of 'in' to signify that we're taking elements from each of the
sequences in a tuple and making an intermediate data tuple to unpack for the assignment,
not unpacking the rhs tuple of sequences)? E.g.,

for oddelem, evenelem = seqn'[1::2], seqn'[0::2]:
. . .

This seems like a nice generalization that could use any sequences, not just generator slices.
I.e., like tuple assignment, but the 'for' makes the rhs get treated like the above was short for

for oddelem, evenelem in itertools.izip(seqn'[1::2], seqn'[0::2]):

Note that like tuple assignment, you could unpack as many sequences in parallel as you liked.
(In fact, the single-target case would work too, just by changing 'in' to '=' ;-)

If you like generator slices, it might be nice to have bare generator slices too, implicitly
producing a sequence of integers that might make sense in relation to the same notation used
on a sequence. E.g.,

'[:] <=> (i for i in xrange(0,sys.maxint,1))
'[start:stop:step] <=> (i for i in xrange(start,stop,step))
'[::-1] <=> (i for i in xrange(-1, -sys.maxint-1, -1))

etc. Thus plain enumerate could be respelled (taking seq via a full generator slice):

for i, elem = '[:], seq'[:]:
. . .

and the reversed order, enumerating with indices -1, -2, etc. would look like

for i, elem = '[::-1], seq'[::-1]:
. . .

Too much at once? ;-)

Discussions with Guido made it clear that inreverse() will not be extended
to cover all iterables. The proposal is about simplicity, expression, and
performance. As such, it would be counter-productive to take in a general
iterable, run it to completion, save the data in memory, and then iterate
over the data in reverse.

The same could apply to seq'[::-1]
I assume seq would be queried for __len__ and __getitem__ when about to create
the generator slice.

Regards,
Bengt Richter
Jul 18 '05 #17

P: n/a
Hi,

After 2 months of learning in Python, the most difficult part is the
object part. Though I can get most of my works done using very simple
classes, I don't think I get the essence of it. Right now I am thinking
of wrirting a class like:

class hittable(object):
def __init__(self):
self.table = [{}, {}, {}, {}]
def build(<some parameter>): #some modification to the dictionary
<some codes>
def search(self, aa):
if aa == 0:
<do modification "build" to self.table[0]>
if aa == 1:
<do modification "build" to self.table[1]>

I have trouble implementing the above in real code. I can only get
something by making a seperate function outside the class:
def build(dd, i): dd.setdefault(i, {})
return dd class hittable(object): def __init__(self):
self.table = [{}, {}, {}, {}] #0:peptides, 1: nucleotides,
2:metals, 3:others
def search(self, i):
self.table[i] = build(self.table[i], i) kk = hittable()
kk.search(3)
kk.table [{}, {}, {}, {3: {}}]

And couldn't find a way to integrate the "build" function into a class
method:
class hittable(object): def __init__(self):
self.table = [{}, {}, {}, {}]
def build(self, i):
self.table[i].setdefault(i, {})
def search(self, i):
self.table[i] = self.build(i) kk = hittable()
kk.search(3)
kk.table

[{}, {}, {}, None]

I think I can imagine the above code won't work, though couldn't find a
solution to it.

What am I missing here?

Thanks!

-shushien


Jul 18 '05 #18

P: n/a
Shu-Hsien Sheu wrote:
...
def build(self, i):
self.table[i].setdefault(i, {})
this method has no explicit 'return', so it returns None
def search(self, i):
self.table[i] = self.build(i)


So, you're assigning None to self.table[i].
>>> kk = hittable()
>>> kk.search(3)
>>> kk.table

[{}, {}, {}, None]

I think I can imagine the above code won't work, though couldn't find a
solution to it.

What am I missing here?


Just forget that wanton assignment in method search! Why would you
want to assign anything new to self.table[i], when self.build(i) has
just modified self.table[i] appropriately?! I.e., change search to:

def search(self, i):
self.build(i)

this doesn't have anything special to do with classes -- rather,
with the care and feeding of dictionaries, I'd say:-).
Alex

Jul 18 '05 #19

P: n/a
Alex Martelli <al***@aleax.it> wrote in message news:<EB***********************@news2.tin.it>...
math.sum would be arguably equivalent to sum as a built-in -- a
tad less immediately accessible, but perhaps superior in that it
immediately suggests it's about numbers only, so we'd avoid the
icky performance trap you now get with sum(manylists, []) {which
makes sum only 2/3 wonderful at best -- fine with numbers ONLY}.


Too late :-(

I see now that ``math.sum`` would have been a much better solution that a
new built-in. BTW, I remember this was discussed on the list, but
what was the argument against a polymorphic sum?

I mean, why it was not an option to implement a ``sum`` function
calling:

1. int.__add__,float.__add__ etc. if the arguments where numbers;
2. string.join if the arguments where lists;
3. something to avoid the performance trap of list.__add__;
4. __add__ for custom defined objects.

BTW, I think the answer should go in the FAQ, since everybody looking
at ``sum`` for the first time would imagine it is polymorphic. I remember
you said your first idea was to make it polymorphic, but this was
rejected due to performances reasons. But why making ``sum`` to
special case according to the arguments (it would be enough to
check the type of the first argument, then we would get an error
if we try to add incompatible types, just as in "".join(["a", 1]))
was a bad idea?

As you say, since ``sum`` is not polymorphic it makes more sense to put
it in the ``math`` module. Now it is too late, but it is unfortunate.
Michele
Jul 18 '05 #20

P: n/a
Hi Alex,

Great thanks! What u've suggested was exactly what I was looking for.
Too bad it's such a trivial question:((

I have another trivial question though. Why won't the following work?

class hittable(object):
def __init__(self):
self = [[], [], [], []]

-shuhsien
Just forget that wanton assignment in method search! Why would you
want to assign anything new to self.table[i], when self.build(i) has
just modified self.table[i] appropriately?! I.e., change search to:

def search(self, i):
self.build(i)

this doesn't have anything special to do with classes -- rather,
with the care and feeding of dictionaries, I'd say:-).

Jul 18 '05 #21

P: n/a
>class hittable(object):
def __init__(self):
self = [[], [], [], []]


Assignment to 'self' is never all that useful. You're just rebinding a
temporary object; this function is basically a no-op because the assignment to
'self' is discarded once the function exits. I know it's kind of confusing, but
you'll get used to the name binding rules before too long.

If you don't understand, it's the same reason this doesn't work:
x = 2
y = x
x = 3
if x == y:
print 'Hewwo world'
# Why doesn't this print?

The answer is that rebinding x doesn't rebind y, which is clearly a Good Thing
in genera as you can see by the example.

- Kef
Jul 18 '05 #22

P: n/a
Hi,

How do you turn a dictionary of <original_key>: <original_value> into
<original_value>: <original_key>, when there is no duplicate values?
For instance, I have a dictionary kk:
kk = {'abc': ['B', 'C', 'D', 'E'], 'def':['G', 'H']}

and I want a new dictionary newkk which looks like:
newkk = {'B':'abc', 'C':'abc', 'D':'abc', 'E':'abc', 'G':'def', 'H':'def'}

My codes are:
kk = {'abc': ['B', 'C', 'D', 'E'], 'def':['G', 'H']}
new = []
for i, item in kk.items(): for j in item:
new.append([j, i]) newkk = dict(new)
new [['B', 'abc'], ['C', 'abc'], ['D', 'abc'], ['E', 'abc'], ['G', 'def'],
['H', 'def']] newkk

{'C': 'abc', 'B': 'abc', 'E': 'abc', 'D': 'abc', 'G': 'def', 'H': 'def'}

Is there a better way of doing it?

-shuhsien
Jul 18 '05 #23

P: n/a

Shu-Hsien> How do you turn a dictionary of <original_key>:
Shu-Hsien> <original_value> into <original_value>: <original_key>, when
Shu-Hsien> there is no duplicate values?

Shu-Hsien> For instance, I have a dictionary kk:
Shu-Hsien> kk = {'abc': ['B', 'C', 'D', 'E'], 'def':['G', 'H']}

Shu-Hsien> and I want a new dictionary newkk which looks like:
Shu-Hsien> newkk = {'B':'abc', 'C':'abc', 'D':'abc', 'E':'abc', 'G':'def', 'H':'def'}

How about:
newkk = {}
for key in kk: ... val = kk[key]
... newkk.update(dict(zip(val, [key]*len(val))))
... newkk

{'C': 'abc', 'B': 'abc', 'E': 'abc', 'D': 'abc', 'G': 'def', 'H': 'def'}

Skip

Jul 18 '05 #24

P: n/a
Shu-Hsien Sheu wrote:
Hi,

How do you turn a dictionary of <original_key>: <original_value> into
<original_value>: <original_key>, when there is no duplicate values?


See http://members.tripod.com/~edcjones/MultiDict.py

Jul 18 '05 #25

P: n/a
On Thu, 30 Oct 2003 16:27:13 -0500, Shu-Hsien Sheu <sh**@bu.edu>
wrote:
Hi,

How do you turn a dictionary of <original_key>: <original_value> into
<original_value>: <original_key>, when there is no duplicate values?
For instance, I have a dictionary kk:
kk = {'abc': ['B', 'C', 'D', 'E'], 'def':['G', 'H']}

and I want a new dictionary newkk which looks like:
newkk = {'B':'abc', 'C':'abc', 'D':'abc', 'E':'abc', 'G':'def', 'H':'def'}


There will be loads of ways of doing this. I would probably use a list
comprehension such as...

result = dict( [(d, k) for k, l in <dict>.items () for d in l] )

Generator comprehensions may make this a tad neater in Python 2.4 ;-)
List comprehensions aren't to everyones taste, though, so some people
would probably find the following clearer...

result = {}

for k, l in <dict>.items () :
for d in l :
result [d] = k

The only flaw in your own code is that creating the list of lists in
'new' is redundant - it is more efficient to create the dictionary
directly.
--
Steve Horne

steve at ninereeds dot fsnet dot co dot uk
Jul 18 '05 #26

P: n/a
Shu-Hsien Sheu wrote:
How do you turn a dictionary of <original_key>: <original_value> into
<original_value>: <original_key>, when there is no duplicate values?
If, as per this spec, each key was mapped to one hashable value (but your
example below indicates this isn't the case!), then

newkk = dict([ (v,k) for k, v in kk.iteritems() ])

would be the solution. But for your example:
For instance, I have a dictionary kk:
kk = {'abc': ['B', 'C', 'D', 'E'], 'def':['G', 'H']}

and I want a new dictionary newkk which looks like:
newkk = {'B':'abc', 'C':'abc', 'D':'abc', 'E':'abc', 'G':'def', 'H':'def'}


you need a nested loop on each so-called value (==list of values), so:

newkk = dict([ (v,k) for k, vs in kk.iteritems() for v in vs ])
In python 2.4 (will be a while coming, so DON'T hold your breath) you can
spell this without those annoying extra square brackets in ([ ... ]) and
get a tiny speedup as well as marginally clearer syntax.
Alex

Jul 18 '05 #27

P: n/a
Shu-Hsien Sheu wrote:
Hi Alex,

Great thanks! What u've suggested was exactly what I was looking for.
Too bad it's such a trivial question:((
"The only bad question is the one not asked":-).

I have another trivial question though. Why won't the following work?
Works fine, actually, just probably doesn't do what you THINK it does:-).
class hittable(object):
def __init__(self):
self = [[], [], [], []]


this rebinds local variable self in the __init__ metod (to a list of
four lists), and that's all. Doesn't affect any INSTANCE of class
hittable, for example. What are you trying to do?! If you want
"hittable" to return a list of lists then it must be a function,
not a class, obviously:

def hittable(): return [[], [], [], []]

if you want each instance of class hittable to start out with
an ATTRIBUTE holding a list of four empty lists then SAY so:

class hittable(object):
def __init__(self):
self.putsomenamehere = [[], [], [], []]
Alex

Jul 18 '05 #28

P: n/a
Dear Kef,

I can totally understand the following. However, I cannot see clearly
how this is similiar to the self assignment?
If you don't understand, it's the same reason this doesn't work:
x = 2
y = x
x = 3
if x == y:
print 'Hello world'
# Why doesn't this print?


Dear Alex,

I think I can see your point. So, we can gerenally say that, only
instances of a class can have assignments; Class name are only a
"reference" thing and doesn't have any "exact" values bind to it. Sorry
my wording might be wrong, but it's the best that I could think of.

thanks!

-shuhsien
Jul 18 '05 #29

P: n/a
Shu-Hsien Sheu wrote:
...
I think I can see your point. So, we can gerenally say that, only
instances of a class can have assignments; Class name are only a
"reference" thing and doesn't have any "exact" values bind to it. Sorry
my wording might be wrong, but it's the best that I could think of.


No, this doesn't sound like anything having to do with Python.

Let's go back to your example. In it, you had a function:

def <function_name_doesnt_matter_WHICH!>(<argument_nam e_ditto>):
<argument_name_ditto> = [ [], [], [], [] ]

and that was all the function did.

NO MATTER WHAT the name of this function,
NO MATTER WHAT the name of its arguments,
NO MATTER WHERE the function is (inside a class statement,
outside of it, inside another function, on a partridge tree),
NO MATTER HOW you access and call it,

*this function will NEVER, EVER do ANYTHING observable from the outside*.

It's rebinding its local variable name which is its argument's name.

That's ALL it's doing. NOTHING ELSE, *EVER*, under ANY conditions.

Actions of a function regarding the bindings of the functions' own,
intrinsically INTERNAL, local variable names, are not per se ever
observable from the outside.

Therefore, this function's body might as well be:
pass
under ANY conditions.

This has nothing to do with classes. Class names can perfectly
well "have assignments" (for any reasonable readings of this "have").
Classes are objects just like any others and they're first class just
like class instances and the like. The issue has nothing at all to
do with this. It doesn't matter in the least that your function was
within a classbody and had the specialname __init__ and its only
argument had the conventionally-used name self -- this placement and
naming influences WHEN the function may be called, but your function
is STILL a "no-operation", totally independent of when or how it's
being called there's no way it's ever gonna DO anything.
Alex

Jul 18 '05 #30

P: n/a
Alex Martelli <al***@aleax.it> wrote in message news:<EB***********************@news2.tin.it>...
math.sum would be arguably equivalent to sum as a built-in -- a
tad less immediately accessible, but perhaps superior in that it
immediately suggests it's about numbers only, so we'd avoid the
icky performance trap you now get with sum(manylists, []) {which
makes sum only 2/3 wonderful at best -- fine with numbers ONLY}.


A recent post of yours made me realize that "sum" is NOT for
numbers only:
class C(object): def __add__(self,other):
return self
c1,c2,c3=C(),C(),C()
sum([c1,c2],c3)

<__main__.C object at 0x00F4DED0>

So, only summing strings is disallowed, all the rest is allowed, but
there can be performance traps, for instance in summing lists. So, if
'sum' is only good at summing numbers, why this is not enforced?
We are forbidden to sum strings, but then for the same reason we
should be forbidden to sum lists: there is some inconsistency here ...
care to explain the rationale for 'sum' again ?
Michele
Jul 18 '05 #31

P: n/a
Michele Simionato wrote:
...
So, only summing strings is disallowed, all the rest is allowed, but
there can be performance traps, for instance in summing lists. So, if
Right.
'sum' is only good at summing numbers, why this is not enforced?
How? Sure, we could specialcase lists and tuples and array.array's
and Numeric.array's and ... -- but that wouldn't really be right either:
e.g. with tuples you can't do much better _easily_ (turning each into
a list and doing a loop of extend isn't "easily" -- it's only warranted in
some cases). Basically, sum is fine whenever the alternative is
reduce(operator.__add__ ... or a loop of
for item in sequence: result = result + item
since it's defined to have exactly that semantics. Problem is mainly
with cases in which we'd rather do
for item in sequence: result += item
which is slightly different semantics when result is mutable; or more
specialized cases such as ''.join(sequence).

We are forbidden to sum strings, but then for the same reason we
should be forbidden to sum lists: there is some inconsistency here ...
care to explain the rationale for 'sum' again ?


sum is basically to sum numbers. But in Python there's no way to tell
precisely what IS "a number". We've special-cased strings because
we _know_ people desperately want to sum bunches of them all of
the time -- my original approach was to have sum detect the case and
delegate to ''.join, but Guido thought that weakened sum's specificity
to numbers and had me forbid it instead. I've recently proposed going
for the performance trap by redefining the semantics as a loop of += --
but Guido isn't happy with that (user-visible change, some way or other,
at least for sufficiently weird user-coded objects). I've also proposed
adding optional abstract baseclasses similar to basestring to give various
spots in Python a chance to detect "this user type is meant to be a
number [it inherits from basenumber] / a sequence [from basesequence] /
and so on", but again I've mostly seen reluctance about this on python-dev.
So the issue is a bit in the air (for Python 2.4).
Alex

Jul 18 '05 #32

This discussion thread is closed

Replies have been disabled for this discussion.