0

Is it possible to create a variable name based on the value of a string?

I have a script that will read a file for blocks of information and store them in a dictionary. Each block's dictionary will then be appended to a 'master' dictionary. The number of blocks of information in a file will vary and uses the word 'done' to indicate the end of a block.

I want to do something like this:

master={}
block=0
for lines in file:
  if line != "done":
    $block.append(line)
  elif line == "done":
    master['$block'].append($block)
    block = block + 1

If a file had content like so:

eggs
done
bacon
done
ham
cheese
done

The result would be a dictionary with 3 lists:

master = {'0': ["eggs"], '1': ["bacon"], '2': ["ham", "cheese"]}

How could this be accomplished?

Gareth Latty
  • 86,389
  • 17
  • 178
  • 183
Rauffle
  • 957
  • 2
  • 8
  • 14
  • Made your dictionary actually a python dict. – Gareth Latty Jun 20 '12 at 18:55
  • It doesn't seem to me like you need a dictionary, since you're indexing your dictionary items sequentially using integers which start at 0. Use a list of lists instead. – machine yearning Jun 20 '12 at 19:03
  • Perhaps I shouldn't have included the example, but I'm still wondering if there's a way to name a variable based on a string's value (and not just a dictionary key) – Rauffle Jun 21 '12 at 22:14

5 Answers5

3

I would actually suggest you to use a list instead. Is there any specific point why would you need dicts that are array-ish?

In case you could do with an array, you can use this:

with open("yourFile") as fd:
    arr = [x.strip().split() for x in fd.read().split("done")][:-1]

Output:

[['eggs'], ['bacon'], ['ham', 'cheese']]

In case you wanted number-string indices, you could use this:

with open("yourFile") as fd:
    l = [x.strip().split() for x in fd.read().split("done")][:-1]
    print dict(zip(map(str,range(len(l))),l))
UltraInstinct
  • 43,308
  • 12
  • 81
  • 104
  • @Downvoters: Can you please explain the action? – UltraInstinct Jun 20 '12 at 19:02
  • Why did someone downvote this? It seems to me more correct than the other answers, since it not only answers the question, but it provides a more elegant data structure than the OP intended. – machine yearning Jun 20 '12 at 19:02
  • @Rauffle: I didnt get you. What?? – UltraInstinct Jun 20 '12 at 19:03
  • I agree with this, a list is a better data structure - I was actually composing a similar thought for my answer. That said, I find your second section ugly - if you want a dict follow my method, for the list, follow the first of Thrustmaster's. – Gareth Latty Jun 20 '12 at 19:04
  • @Thrustmaster: I would however suggest that you frame your answer code in terms of a `for` loop, since it seems like the OP is already somewhat comfortable working with this flow control syntax. More concise is not necessarily better, especially when it comes to list comprehensions and generator expressions. – machine yearning Jun 20 '12 at 19:07
  • @FrancisW.Usher I don't see that means you should abandon the more appropriate (and better in every way) list comprehension. – Gareth Latty Jun 20 '12 at 19:08
  • 1
    @FrancisW.Usher: Yes, I understand. But is there any harm in suggesting a more readable, concise, and a pythonic way of doing things? – UltraInstinct Jun 20 '12 at 19:09
  • 1
    As written, the list comprehension leaks a file ref, unfortunately. (Side note: I think calling `str` on the returns from `itertools.count` would be much cleaner than getNextNumber.) – DSM Jun 20 '12 at 19:10
  • 1
    @Friends: I didn't mean to suggest that you don't include your list comprehension and mention that it's the **right** way of doing things. However since this syntax is not generally familiar or intuitive for beginning programmers, I usually **additionally** provide the analogous `for` loop version, for beginners' reference. – machine yearning Jun 20 '12 at 19:11
  • @DSM In general, a much cleaner solution for the dictionary is a simple loop with a `defaultdict`. – Gareth Latty Jun 20 '12 at 19:15
  • @DSM: Thanks for pointing that out. I didnt think of that, while trying to make it compact. :) – UltraInstinct Jun 20 '12 at 19:21
2

You seem to be misunderstanding how dictionaries work. They take keys that are objects, so no magic is needed here.

We can however, make your code nicer by using a collections.defaultdict to make the sublists as required.

from collections import defaultdict

master = defaultdict(list)
block = 0
for line in file:
    if line == "done":
        block += 1
    else:
        master[block].append(line)

I would, however, suggest that a dictionary is unnecessary if you want continuous, numbered indices - that's what lists are for. In that case, I suggest you follow Thrustmaster's first suggestion, or, as an alternative:

from itertools import takewhile

def repeat_while(predicate, action):
    while True:
        current = action()
        if not predicate(current):
            break
        else:
            yield current

with open("test") as file:
    action = lambda: list(takewhile(lambda line: not line == "done", (line.strip() for line in file)))
    print(list(repeat_while(lambda x: x, action)))
Community
  • 1
  • 1
Gareth Latty
  • 86,389
  • 17
  • 178
  • 183
1

I think that split on "done" is doomed to failure. Consider the list:

eggs
done
bacon
done
rare steak
well done stake
done

Stealing from Thrustmaster (which I gave a +1 for my theft) I'd suggest:

>>> dict(enumerate(l.split() for l in open(file).read().split('\ndone\n') if l))
{0: ['eggs'], 1: ['bacon'], 2: ['ham', 'cheese']}

I know this expects a trailing "\n". If there is a question there you could use "open(file).read()+'\n'" or even "+'\n\ndone\n'" if the final done is optional.

Phil Cooper
  • 5,747
  • 1
  • 25
  • 41
0

Use setattr or globals().
See How do I call setattr() on the current module?

Community
  • 1
  • 1
Thomas Orozco
  • 53,284
  • 11
  • 113
  • 116
0

Here's your code again, for juxtaposition:

master={}
block=0
for lines in file:
  if line != "done":
    $block.append(line)
  elif line == "done":
    master['$block'].append($block)
    block = block + 1

As mentioned in the post by Thrustmaster, it makes more sense to use a nested list here. Here's how you would do that; I've changed as little as possible structurally from your original code:

master=[[]] # Start with a list containing only a single list
for line in file: # Note the typo in your code: you wrote "for lines in file"
  if line != "done":
    master[-1].append(line) # Index -1 is the last element of your list
  else: # Since it's not not "done", it must in fact be "done"
    master.append([])

The only thing here is that you'll end up with one extra list at the end of your master list, so you should add a line to delete the last, empty sublist:

del master[-1]
machine yearning
  • 9,889
  • 5
  • 38
  • 51