Jumping around when assigning elements

M

Matthew Sims

Python Newbie here. This is my first time learning object-oriented
programming and trying to break out of the usual Korn/Perl/PHP style
of programming. Having some difficulty understand some items.

For lists, I understand this:
C=["need","some","help"]
print C[1]
some

But I can't seem to do this:
C[3]="here"

I know about C.append("here") but this brings me to my question...

Is there anyway to assign to an element that wasn't defined in the
beginning? Like if I wanted element 5 assigned but not element 4
without using "" or None?

I'm currently re-writing a Perl script into Python and with Perl I was
free to assign any element in the array without having to fill in the
previous elements. I can't seem to do that in Python...unless I'm
doing it wrong.

Thanks
--Matt
 
F

Francis Avila

Matthew Sims wrote in message
Python Newbie here. This is my first time learning object-oriented
programming and trying to break out of the usual Korn/Perl/PHP style
of programming. Having some difficulty understand some items.

For lists, I understand this:
C=["need","some","help"]
print C[1]
some

But I can't seem to do this:
C[3]="here"

I know about C.append("here") but this brings me to my question...

Is there anyway to assign to an element that wasn't defined in the
beginning? Like if I wanted element 5 assigned but not element 4
without using "" or None?

I assume you tried 'insert'?
C=["need","some","help"]
C.insert(10, 'here')
C
['need', 'some', 'help', 'here']

Ah, well that didn't work. Python has no (builtin) way to do what you want.
The reason isn't technical, but that the list needs *something* in that
intervening space, and Python isn't about to make rash presumptions about
what you want there ("explicit is better than implicit"). In choosing
between two behaviors for inserting to an index beyond the end of a list,
appending was deemed less surprising, less violent, and less likely to
introduce mysterious bugs than filling in the intermediate elements with
who-knows-what.

I can't even think of why one would want lists to have that sort of
behavior. It seems to me that if we're assigning to arbitrary elements and
we want the list to grow with it, that we should be using a different data
structure, like a dictionary with integer keys. The index of a list
generally has no semantic correlation to the data contained therein--it just
specifies order, and the data doesn't care about its own index. If we want
a semantic correlation, we make that correlation explicit by a dictionary's
key-value pair.

If you want to do this sort of thing (implicit assignment to intermediate
elements in list insertions), write a function to do it, or subclass list.
You can even modify the slice behavior itself (if you go the subclass
route), so that C[3] will silently act like perl instead of Python, but this
is *quite* unPythonic. (Other Python people reading your code will be
confused.)

If this sort of answer surprises you (coming from perl, it might...), do an
"import this" at an interactive prompt and read. :)
I'm currently re-writing a Perl script into Python and with Perl I was
free to assign any element in the array without having to fill in the
previous elements. I can't seem to do that in Python...unless I'm
doing it wrong.

Again, in general, Python has *no* implicit
assignments/declarations/modifications/etc. If you don't *explicitly* ask a
list to fill in its intermediate elements, Python won't do it. This is a
conscious design decision, and a great part of what makes Python Python.
For at least two senses of "great", IMO.

I could never learn perl (despite many attempts), so I ask, what does perl
do to the intermediate elements? Is a perl array more like a dictionary
with implicit integer keys (so intermediate keys simply don't exist), or
does it fill in the intermediate elements with 0 or something like that? In
what perl idioms is this behavior useful? (I still mean to learn perl one
of these days, for shell scripting.)
 
M

Mike Rovner

Francis said:
Matthew Sims wrote in message

Again, in general, Python has *no* implicit
assignments/declarations/modifications/etc. If you don't
*explicitly* ask a list to fill in its intermediate elements, Python
won't do it. This is a conscious design decision, and a great part
of what makes Python Python. For at least two senses of "great", IMO.

As were stated here many times it's not a good idea to mechanicaly
translate perl to python, they are quite different in approaches
despite similar syntax and capabilities.
Better understand initial script logic and write python from scratch.
In your case it might be list, dictionary or even none of them, so
it's highly dependent on the task at hand.
I could never learn perl (despite many attempts), so I ask, what does
perl do to the intermediate elements? Is a perl array more like a
dictionary with implicit integer keys (so intermediate keys simply
don't exist), or does it fill in the intermediate elements with 0 or
something like that? In what perl idioms is this behavior useful?
(I still mean to learn perl one of these days, for shell scripting.)

perl doesn't assigns them, they are 'undef', so it's more like python's dict

Mike
 
P

Peter Otten

Matthew said:
Is there anyway to assign to an element that wasn't defined in the
beginning? Like if I wanted element 5 assigned but not element 4
without using "" or None?

You can write your own class:

class GrowingList(list):
def __init__(self, seq, default=None):
list.__init__(self, seq)
self.default = default
def __setitem__(self, index, value):
if index >= len(self):
self.extend([self.default]*(index - len(self)))
self.append(value)
else:
list.__setitem__(self, index, value)


g = GrowingList(["alpha", "beta", "gamma"])
g[7] = "omega"
print g

This is a partial implementation, only g[index] = value will work.
I'm currently re-writing a Perl script into Python and with Perl I was
free to assign any element in the array without having to fill in the
previous elements. I can't seem to do that in Python...unless I'm
doing it wrong.

As already pointed out, a mechanical translation will yield substandard
results. As you did not describe the problem you are solving with the
"autogrowing" list, there is little chance for us to come up with a better
or at least more idiomatic approach.

Peter

PS: Welcome to the worst programming language - except all others :)
 
C

Cameron Laird

Matthew said:
Is there anyway to assign to an element that wasn't defined in the
beginning? Like if I wanted element 5 assigned but not element 4
without using "" or None?

You can write your own class:

class GrowingList(list):
def __init__(self, seq, default=None):
list.__init__(self, seq)
self.default = default
def __setitem__(self, index, value):
if index >= len(self):
self.extend([self.default]*(index - len(self)))
self.append(value)
else:
list.__setitem__(self, index, value)


g = GrowingList(["alpha", "beta", "gamma"])
g[7] = "omega"
print g

This is a partial implementation, only g[index] = value will work.
I'm currently re-writing a Perl script into Python and with Perl I was
free to assign any element in the array without having to fill in the
previous elements. I can't seem to do that in Python...unless I'm
doing it wrong.

As already pointed out, a mechanical translation will yield substandard
results. As you did not describe the problem you are solving with the
"autogrowing" list, there is little chance for us to come up with a better
or at least more idiomatic approach.
.
.
.
We've probably convinced Mr. Sims that Python (and
object-oriented programming) is really, really hard,
particularly compared to Perl: we have to write
hairy Class definitions just to do the simplest
things.

I think there's a fair probability this is all a
misunderstanding, and that what Mr. Sims truly wants,
although he doesn't realize it, is simply a dictionary,
rather than a list. Mr. Sims, how would you feel if I
said you should be using a Perl "hash" rather than an
"array"?
 
M

Matthew Sims

Francis Avila said:
Matthew Sims wrote in message
Python Newbie here. This is my first time learning object-oriented
programming and trying to break out of the usual Korn/Perl/PHP style
of programming. Having some difficulty understand some items.

For lists, I understand this:
C=["need","some","help"]
print C[1]
some

But I can't seem to do this:
C[3]="here"

I know about C.append("here") but this brings me to my question...

Is there anyway to assign to an element that wasn't defined in the
beginning? Like if I wanted element 5 assigned but not element 4
without using "" or None?

I assume you tried 'insert'?
C=["need","some","help"]
C.insert(10, 'here')
C
['need', 'some', 'help', 'here']

Ah, well that didn't work. Python has no (builtin) way to do what you want.
The reason isn't technical, but that the list needs *something* in that
intervening space, and Python isn't about to make rash presumptions about
what you want there ("explicit is better than implicit"). In choosing
between two behaviors for inserting to an index beyond the end of a list,
appending was deemed less surprising, less violent, and less likely to
introduce mysterious bugs than filling in the intermediate elements with
who-knows-what.

First off...I still have a lot to learn. :)
I can't even think of why one would want lists to have that sort of
behavior. It seems to me that if we're assigning to arbitrary elements and
we want the list to grow with it, that we should be using a different data
structure, like a dictionary with integer keys. The index of a list
generally has no semantic correlation to the data contained therein--it just
specifies order, and the data doesn't care about its own index. If we want
a semantic correlation, we make that correlation explicit by a dictionary's
key-value pair.

If you want to do this sort of thing (implicit assignment to intermediate
elements in list insertions), write a function to do it, or subclass list.
You can even modify the slice behavior itself (if you go the subclass
route), so that C[3] will silently act like perl instead of Python, but this
is *quite* unPythonic. (Other Python people reading your code will be
confused.)

My guess is that this is what makes OO programming different than
modular.
If this sort of answer surprises you (coming from perl, it might...), do an
"import this" at an interactive prompt and read. :)

The Zen of Python, by Tim Peters...heh
Again, in general, Python has *no* implicit
assignments/declarations/modifications/etc. If you don't *explicitly* ask a
list to fill in its intermediate elements, Python won't do it. This is a
conscious design decision, and a great part of what makes Python Python.
For at least two senses of "great", IMO.

I could never learn perl (despite many attempts), so I ask, what does perl
do to the intermediate elements? Is a perl array more like a dictionary
with implicit integer keys (so intermediate keys simply don't exist), or
does it fill in the intermediate elements with 0 or something like that? In
what perl idioms is this behavior useful? (I still mean to learn perl one
of these days, for shell scripting.)

Here's how the program basically worked. It's for a Tape Library with
a robot arm in the middle. There are 40 slots and each is represented
as an element in the array. If a tape was in one of the slots, that
element # corresponds to the slot # and the tape ID would show in that
element. All other elements represented blank slots. This allowed me
to control the robot arm to the exact slot a tape resides since all I
had to do was look at any elements that had tape IDs assigned.

So far the only way I could get this to work in Python was to insert
"" to represent an empty slot. But Python counts "" when I want to see
how many tapes are currently taking up slots "len(SLOTS)". It would
always show 40. So I now know that I need to write code to tell Python
not to count "". Not too difficult.

I guess I'm realizing how I need to approach Python as my first OO.
I'm re-writing the script as sort of a training. The program works
flawlessly in Perl, I'm just doing it to learn. So far it has taken
less lines of code in Python to perform the same tasks in Perl...up
til this point. ;)

Anyways, I appreciate the responses. I have a better understand of
Python.

--Matt
 
A

Andrew Bennetts

]
So far the only way I could get this to work in Python was to insert
"" to represent an empty slot. But Python counts "" when I want to see
how many tapes are currently taking up slots "len(SLOTS)". It would
always show 40. So I now know that I need to write code to tell Python
not to count "". Not too difficult.

I guess I'm realizing how I need to approach Python as my first OO.
I'm re-writing the script as sort of a training. The program works
flawlessly in Perl, I'm just doing it to learn. So far it has taken
less lines of code in Python to perform the same tasks in Perl...up
til this point. ;)

It sounds like your problem isn't OO, but the wrong choice of datastructure.
As someone else in this thread has already said, you want a dictionary
(known as a 'hash' in perl), not a list. Among other things, len(some_dict)
will give you the result you expect.

-Andrew.
 
S

Shalabh Chaturvedi

For lists, I understand this:
C=["need","some","help"]
print C[1]
some

But I can't seem to do this:
C[3]="here"

I know about C.append("here") but this brings me to my question...

Is there anyway to assign to an element that wasn't defined in the
beginning? Like if I wanted element 5 assigned but not element 4
without using "" or None?

You could try using a dictionary instead of a list. You could even
initialize the dict from the list:

l = ["just", "a", "suggestion"]
d = {}
for k,v in enumerate(l):
d[k] = v

d[10] = "here"

Of course, to get at the values now, you would need to iterate over
d.values(). Note that you won't necessarily get the values in sorted
order of keys. You would have to do that explicitly if you want.
 
F

Francis Avila

Matthew Sims wrote in message
"Francis Avila" <[email protected]> wrote in message

Here's how the program basically worked. It's for a Tape Library with
a robot arm in the middle. There are 40 slots and each is represented
as an element in the array. If a tape was in one of the slots, that
element # corresponds to the slot # and the tape ID would show in that
element. All other elements represented blank slots. This allowed me
to control the robot arm to the exact slot a tape resides since all I
had to do was look at any elements that had tape IDs assigned.

Ah, I see. Well, that makes more sense. In this case it's *not* so
clear-cut whether to use a list or a dict (or whatever). A list is a
reasonable approach.

Right off the bat, I can think of a few:

- What you're doing now, but initializing an empty list to fixed length, eg,
tapearray = [None]*40. Now just use it (no need to worry about magically
extending the list as you add to random slots), but make sure you don't
modify the list with insert, append, extend, pop, etc., etc.: only perform
operations on its members.

Python doesn't do the implicit thing, so you're often well off by
preinitalizing data structures, as here. I simply say [None]*40 and your
entire original question disappears, because you were looking at the problem
in a perlish manner of "how do I use this array in a manner that doesn't
require me to declare its total size before I use it." Explicit is better
than implicit.

- Using a dict with integer keys. Because your tape array won't change in
size, and iterating over the whole thing looks to be the most common
operation (as opposed to random access), dict might not be the obvious
choice. But dicts are more flexable, and make explicit the importance of
the index. You don't need to initalize your dict because it will add
non-existent keys on assignment, but it's probably less of a hassle to do so
anyway, because it *will* raise a KeyError if you try to read a key it
doesn't have. To get around this, you need to subclass dict and make a
(commonly reimplemented) DefaultDict subclass, or make sure you consistently
use the setdefault method of your dict. I say just preinitalize the blasted
thing: tapearray = dict([(i, None) for i in range(40)]).

- Using a list of lists, with zeroth element of each sublist the slot
number. Now you don't have to worry about preinitalizing the list or
keeping the tape slots in order. I can't think of any advantage to this
approach, unless you need faster iteration with variable total length, and
don't need the slots to be ordered. Probably just use a dict.

- Write a TapeSlot class, a TapeArray class, and a TapeArrayVisitor class.
(I'm kidding! Don't do this.)

Of course, you can mix-and-match as your requirements demand. If slots need
more status information, you could use a dict of lists or a list of lists.
If slots have a set of operations proper to them, you can use a list of
TapeSlot objects; or you could wrap the interface to the tapearray in a
TapeArray class, and make slot access a hidden implementation detail.
So far the only way I could get this to work in Python was to insert
"" to represent an empty slot. But Python counts "" when I want to see
how many tapes are currently taking up slots "len(SLOTS)". It would
always show 40. So I now know that I need to write code to tell Python
not to count "". Not too difficult.

Not at all:

len([tapeID for tapeID in tapearray if tapeID])

"If the item in tapearray is True (in a boolean context, '' is False) append
that item to a new list, then find the length of this new list."
I guess I'm realizing how I need to approach Python as my first OO.
I'm re-writing the script as sort of a training. The program works
flawlessly in Perl, I'm just doing it to learn. So far it has taken
less lines of code in Python to perform the same tasks in Perl...up
til this point. ;)

Unless you're doing relatively complex things that require a rich set of
data/semantics for tape slots and/or tape arrays, there's no reason to start
using objects to represent them: "Simple is better than complex." Some
people get drunk on OO. OO is useful, but can also add unnecessary
complexity and obscure what you're doing behind too many layers of
abstraction.

Finally, don't do line-of-code comparisons between Python and perl. Python
may be brief, but it is *not* terse, and will choose clarity over
more-magic-per-line. List comprehensions excepted. ;)

Ugh, I talk too much. Well, hope you like Python!
 
P

Peter Otten

Cameron said:
We've probably convinced Mr. Sims that Python (and
object-oriented programming) is really, really hard,
particularly compared to Perl: we have to write
hairy Class definitions just to do the simplest
things.

Posting a piece of ugly code to meet a specification that is incomplete or
even wrong and at the same time showing Python in bad light compared to
Perl... I have to be more careful with my posts.

Peter
 
P

Paul Rubin

For lists, I understand this:
C=["need","some","help"]
print C[1]
some

But I can't seem to do this:
C[3]="here"

I know about C.append("here") but this brings me to my question...

Is there anyway to assign to an element that wasn't defined in the
beginning? Like if I wanted element 5 assigned but not element 4
without using "" or None?

The simplest way is with a dictionary instead of a list:

D = {0:"need", 1:"some", 2:"help"}
print D[1]
some

Then you can do

D[3] = "here"
D[97] = "way over there"
D["banana"] = "yellow"

etc.
 

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
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top