PEP 322: Reverse Iteration (REVISED, please comment)

S

Shu-Hsien Sheu

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
 
K

KefX

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
 
S

Shu-Hsien Sheu

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])[['B', 'abc'], ['C', 'abc'], ['D', 'abc'], ['E', 'abc'], ['G', 'def'],
['H', 'def']]{'C': 'abc', 'B': 'abc', 'E': 'abc', 'D': 'abc', 'G': 'def', 'H': 'def'}

Is there a better way of doing it?

-shuhsien
 
S

Skip Montanaro

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:
... val = kk[key]
... newkk.update(dict(zip(val, [key]*len(val))))
... {'C': 'abc', 'B': 'abc', 'E': 'abc', 'D': 'abc', 'G': 'def', 'H': 'def'}

Skip
 
S

Stephen Horne

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.
 
A

Alex Martelli

Shu-Hsien Sheu said:
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
 
A

Alex Martelli

Shu-Hsien Sheu said:
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
 
S

Shu-Hsien Sheu

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
 
A

Alex Martelli

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_name_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
 
M

Michele Simionato

Alex Martelli said:
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:
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
 
A

Alex Martelli

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
 

Ask a Question

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

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

Ask a Question

Members online

Forum statistics

Threads
474,169
Messages
2,570,920
Members
47,462
Latest member
ChanaLipsc

Latest Threads

Top