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?

3
  • Made your dictionary actually a python dict. Commented Jun 20, 2012 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. Commented Jun 20, 2012 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) Commented Jun 21, 2012 at 22:14

5 Answers 5

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))
Sign up to request clarification or add additional context in comments.

11 Comments

@Downvoters: Can you please explain the action?
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.
@FrancisW.Usher: Yes, I understand. But is there any harm in suggesting a more readable, concise, and a pythonic way of doing things?
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.)
@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.
|
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)))

Comments

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.

Comments

0

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

2 Comments

Read the question, this isn't actually what he is asking
Indeed, and if it so, your answer is the right one, so I'll leave that one here and upvote yours.
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]

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.