Apply a function to a list of dictionaries in python - python

I would like to apply a function (called offline_optimize) to a list of inputs. Each item in the list is a dictionary with 14 key-value pairs. The list consists of 12 dictionaries.
# This works
results = [offline_optimize(**input) for input in inputs]
# This doesn't
results = map(offline_optimize, *inputs)
TypeError: offline_optimize() takes exactly 14 arguments (12 given)
inputs is a list of dictionaries. More precisely, the list consists of 12 dictionaries.
The function offline_optimize takes 14 arguments.
How can I use the map function on a list of dictionaries, when the dictionaries need to be unpacked (using the double-star syntax **) to 14 arguments to be accepted by the function offline_optimize?
I would like to avoid list comprehensions, if possible.

map(function, sequence[, sequence, ...]) -> list
Return a list of the results of applying the function to the items of
the argument sequence(s). If more than one sequence is given, the
function is called with an argument list consisting of the corresponding
item of each sequence, substituting None for missing values when not all
sequences have the same length. If the function is None, return a list of
the items of the sequence (or a list of tuples if more than one sequence).
I think the effect is best illustrated by mocking out inputs and offline_optimize:
import string
inputs = [dict([(s, i) for i, s in enumerate(string.letters[:14])])] * 12
def offline_optimize(*args, **kwargs):
return [("args", args),
("args_count", len(args)),
("kwargs", kwargs),
("kwargs_count", len(kwargs))]
What inputs look like:
>>> print len(inputs), len(inputs[0]), inputs[0]
12 14 {'A': 0, 'C': 2, 'B': 1, 'E': 4, 'D': 3, 'G': 6, 'F': 5, 'I': 8, 'H': 7, 'K': 10, 'J': 9, 'M': 12, 'L': 11, 'N': 13}
You do this:
>>> mapped = map(offline_optimize, *inputs)
>>> print len(mapped), mapped[0]
14 [('args', ('A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A')), ('args_count', 12), ('kwargs', {}), ('kwargs_count', 0)]
You want to do this:
>>> mapped = map(lambda x: offline_optimize(**x), inputs)
>>> print len(mapped), mapped[0]
12 [('args', ()), ('args_count', 0), ('kwargs', {'A': 0, 'C': 2, 'B': 1, 'E': 4, 'D': 3, 'G': 6, 'F': 5, 'I': 8, 'H': 7, 'K': 10, 'J': 9, 'M': 12, 'L': 11, 'N': 13}), ('kwargs_count', 14)]
You really want to use list comprehensions and to avoid functions with 14 keyword parameters.

Based on your error, I'm assuming that offline_optimize() has a function signature something like the following:
def offline_optimize(arg1, arg2, maybe_arg3=some_default_value, ...):
some_function(arg1)
some_function(arg2, maybe_arg3)
# etc.
Change your function definition to:
def offline_optimize(**kwargs):
some_function(kwargs.get('arg1'))
some_function(kwargs.get('arg2'), kwargs.get('maybe_arg3', some_default_value))
# etc.

Related

How can I add n times a value in a dictionary? [duplicate]

Can I use list comprehension syntax to create a dictionary?
For example, by iterating over pairs of keys and values:
d = {... for k, v in zip(keys, values)}
Use a dict comprehension (Python 2.7 and later):
{key: value for (key, value) in iterable}
Alternatively for simpler cases or earlier version of Python, use the dict constructor, e.g.:
pairs = [('a', 1), ('b', 2)]
dict(pairs) #=> {'a': 1, 'b': 2}
dict([(k, v+1) for k, v in pairs]) #=> {'a': 2, 'b': 3}
Given separate arrays of keys and values, use the dict constructor with zip:
keys = ['a', 'b']
values = [1, 2]
dict(zip(keys, values)) #=> {'a': 1, 'b': 2}
2) "zip'ped" from two separate iterables of keys/vals
dict(zip(list_of_keys, list_of_values))
In Python 3 and Python 2.7+, dictionary comprehensions look like the below:
d = {k:v for k, v in iterable}
For Python 2.6 or earlier, see fortran's answer.
In fact, you don't even need to iterate over the iterable if it already comprehends some kind of mapping, the dict constructor doing it graciously for you:
>>> ts = [(1, 2), (3, 4), (5, 6)]
>>> dict(ts)
{1: 2, 3: 4, 5: 6}
>>> gen = ((i, i+1) for i in range(1, 6, 2))
>>> gen
<generator object <genexpr> at 0xb7201c5c>
>>> dict(gen)
{1: 2, 3: 4, 5: 6}
Create a dictionary with list comprehension in Python
I like the Python list comprehension syntax.
Can it be used to create dictionaries too? For example, by iterating
over pairs of keys and values:
mydict = {(k,v) for (k,v) in blah blah blah}
You're looking for the phrase "dict comprehension" - it's actually:
mydict = {k: v for k, v in iterable}
Assuming blah blah blah is an iterable of two-tuples - you're so close. Let's create some "blahs" like that:
blahs = [('blah0', 'blah'), ('blah1', 'blah'), ('blah2', 'blah'), ('blah3', 'blah')]
Dict comprehension syntax:
Now the syntax here is the mapping part. What makes this a dict comprehension instead of a set comprehension (which is what your pseudo-code approximates) is the colon, : like below:
mydict = {k: v for k, v in blahs}
And we see that it worked, and should retain insertion order as-of Python 3.7:
>>> mydict
{'blah0': 'blah', 'blah1': 'blah', 'blah2': 'blah', 'blah3': 'blah'}
In Python 2 and up to 3.6, order was not guaranteed:
>>> mydict
{'blah0': 'blah', 'blah1': 'blah', 'blah3': 'blah', 'blah2': 'blah'}
Adding a Filter:
All comprehensions feature a mapping component and a filtering component that you can provide with arbitrary expressions.
So you can add a filter part to the end:
>>> mydict = {k: v for k, v in blahs if not int(k[-1]) % 2}
>>> mydict
{'blah0': 'blah', 'blah2': 'blah'}
Here we are just testing for if the last character is divisible by 2 to filter out data before mapping the keys and values.
In Python 2.7, it goes like:
>>> list1, list2 = ['a', 'b', 'c'], [1,2,3]
>>> dict( zip( list1, list2))
{'a': 1, 'c': 3, 'b': 2}
Zip them!
Python version >= 2.7, do the below:
d = {i: True for i in [1,2,3]}
Python version < 2.7(RIP, 3 July 2010 - 31 December 2019), do the below:
d = dict((i,True) for i in [1,2,3])
To add onto #fortran's answer, if you want to iterate over a list of keys key_list as well as a list of values value_list:
d = dict((key, value) for (key, value) in zip(key_list, value_list))
or
d = {(key, value) for (key, value) in zip(key_list, value_list)}
Just to throw in another example. Imagine you have the following list:
nums = [4,2,2,1,3]
and you want to turn it into a dict where the key is the index and value is the element in the list. You can do so with the following line of code:
{index:nums[index] for index in range(0,len(nums))}
Here is another example of dictionary creation using dict comprehension:
What i am tring to do here is to create a alphabet dictionary where each pair; is the english letter and its corresponding position in english alphabet
>>> import string
>>> dict1 = {value: (int(key) + 1) for key, value in
enumerate(list(string.ascii_lowercase))}
>>> dict1
{'a': 1, 'c': 3, 'b': 2, 'e': 5, 'd': 4, 'g': 7, 'f': 6, 'i': 9, 'h': 8,
'k': 11, 'j': 10, 'm': 13, 'l': 12, 'o': 15, 'n': 14, 'q': 17, 'p': 16, 's':
19, 'r': 18, 'u': 21, 't': 20, 'w': 23, 'v': 22, 'y': 25, 'x': 24, 'z': 26}
>>>
Notice the use of enumerate here to get a list of alphabets and their indexes in the list and swapping the alphabets and indices to generate the key value pair for dictionary
Hope it gives a good idea of dictionary comp to you and encourages you to use it more often to make your code compact
This code will create dictionary using list comprehension for multiple lists with different values that can be used for pd.DataFrame()
#Multiple lists
model=['A', 'B', 'C', 'D']
launched=[1983,1984,1984,1984]
discontinued=[1986, 1985, 1984, 1986]
#Dictionary with list comprehension
keys=['model','launched','discontinued']
vals=[model, launched,discontinued]
data = {key:vals[n] for n, key in enumerate(keys)}
#Convert dict to dataframe
df=pd.DataFrame(data)
display(df)
enumerate will pass n to vals to match each key with its list
Try this,
def get_dic_from_two_lists(keys, values):
return { keys[i] : values[i] for i in range(len(keys)) }
Assume we have two lists country and capital
country = ['India', 'Pakistan', 'China']
capital = ['New Delhi', 'Islamabad', 'Beijing']
Then create dictionary from the two lists:
print get_dic_from_two_lists(country, capital)
The output is like this,
{'Pakistan': 'Islamabad', 'China': 'Beijing', 'India': 'New Delhi'}
Adding to #Ekhtiar answer, if you want to make look up dict from list, you can use this:
names = ['a', 'b', 'd', 'f', 'c']
names_to_id = {v:k for k, v in enumerate(names)}
# {'a': 0, 'b': 1, 'c': 2, 'd': 3, 'f': 4}
Or in rare case that you want to filter duplicate, use set first (best in list of number):
names = ['a', 'b', 'd', 'f', 'd', 'c']
sorted_list = list(set(names))
sorted_list.sort()
names_to_id = {v:k for k, v in enumerate(sorted_list)}
# {'a': 0, 'b': 1, 'c': 2, 'd': 3, 'f': 4}
names = [1,2,5,5,6,2,1]
names_to_id = {v:k for k, v in enumerate(set(names))}
# {1: 0, 2: 1, 5: 2, 6: 3}
>>> {k: v**3 for (k, v) in zip(string.ascii_lowercase, range(26))}
Python supports dict comprehensions, which allow you to express the creation of dictionaries at runtime using a similarly concise syntax.
A dictionary comprehension takes the form {key: value for (key, value) in iterable}. This syntax was introduced in Python 3 and backported as far as Python 2.7, so you should be able to use it regardless of which version of Python you have installed.
A canonical example is taking two lists and creating a dictionary where the item at each position in the first list becomes a key and the item at the corresponding position in the second list becomes the value.
The zip function used inside this comprehension returns an iterator of tuples, where each element in the tuple is taken from the same position in each of the input iterables. In the example above, the returned iterator contains the tuples (“a”, 1), (“b”, 2), etc.
Output:
{'i': 512, 'e': 64, 'o': 2744, 'h': 343, 'l': 1331, 's': 5832, 'b': 1, 'w': 10648, 'c': 8, 'x': 12167, 'y': 13824, 't': 6859, 'p': 3375, 'd': 27, 'j': 729, 'a': 0, 'z': 15625, 'f': 125, 'q': 4096, 'u': 8000, 'n': 2197, 'm': 1728, 'r': 4913, 'k': 1000, 'g': 216, 'v': 9261}
Yes, it's possible. In python, Comprehension can be used in List, Set, Dictionary, etc.
You can write it this way
mydict = {k:v for (k,v) in blah}
Another detailed example of Dictionary Comprehension with the Conditional Statement and Loop:
parents = [father, mother]
parents = {parent:1 - P["mutation"] if parent in two_genes else 0.5 if parent in one_gene else P["mutation"] for parent in parents}
You can create a new dict for each pair and merge it with the previous dict:
reduce(lambda p, q: {**p, **{q[0]: q[1]}}, bla bla bla, {})
Obviously this approaches requires reduce from functools.
Assuming blah blah blah is a two-tuples list:
Let's see two methods:
# method 1
>>> lst = [('a', 2), ('b', 4), ('c', 6)]
>>> dict(lst)
{'a': 2, 'b': 4, 'c': 6}
# method 2
>>> lst = [('a', 2), ('b', 4), ('c', 6)]
>>> d = {k:v for k, v in lst}
>>> d
{'a': 2, 'b': 4, 'c': 6}
this approach uses iteration over the given date using a for loop.
Syntax: {key: value for (key, value) in data}
Eg:
# create a list comprehension with country and code:
Country_code = [('China', 86), ('USA', 1),
('Ghana', 233), ('Uk', 44)]
# use iterable method to show results
{key: value for (key, value) in Country_code}

What's the pythonic way to remove element from a list of dict if ONE of the values is not unique?

What would be the pythonic way to remove elements that are not uniques for certain keys?
Let's say one has a list of dicts such as:
[
{'a': 1, 'b': 'j'},
{'a': 2, 'b': 'j'},
{'a': 3, 'b': 'i'}
]
The expected output would remove the second element, because the key b equals to j in more than one element. Thus:
[
{'a': 1, 'b': 'j'},
{'a': 3, 'b': 'i'}
]
This is what I have tried:
input = [
{'a': 1, 'b': 'j'},
{'a': 2, 'b': 'j'},
{'a': 3, 'b': 'i'}
]
output = []
for input_element in input:
if not output:
output.append(input_element)
else:
for output_element in output:
if input_element['b'] != output_element['b']:
output.append(input_element)
Would the solution be simpler if that'd be a list of tuples, such as:
[(1, 'j'), (2, 'j'), (3, 'i')]
# to produce
[(1, 'j'), (3, 'i')]
Here is an approach using any() and list-comprehension:
Code:
l=[
{'a': 1, 'b': 'j'},
{'a': 2, 'b': 'j'},
{'a': 3, 'b': 'i'}
]
new_l = []
for d in l:
if any([d['b'] == x['b'] for x in new_l]):
continue
new_l.append(d)
print(new_l)
Output:
[{'a': 1, 'b': 'j'}, {'a': 3, 'b': 'i'}]
You could define a custom container class which implements the __eq__ and __hash__ magic methods. That way, you can use a set to remove "duplicates" (according to your criteria). This doesn't necessarily preserve order.
from itertools import starmap
from typing import NamedTuple
class MyTuple(NamedTuple):
a: int
b: str
def __eq__(self, other):
return self.b == other.b
def __hash__(self):
return ord(self.b)
print(set(starmap(MyTuple, [(1, 'j'), (2, 'j'), (3, 'i')])))
Output:
{MyTuple(a=3, b='i'), MyTuple(a=1, b='j')}
>>>
I suggest this implementation:
_missing = object()
def dedupe(iterable, selector=_missing):
"De-duplicate a sequence based on a selector"
keys = set()
if selector is _missing: selector = lambda e: e
for e in iterable:
if selector(e) in keys: continue
keys.add(selector(e))
yield e
Advantages:
Returns a generator:
It iterates the original collection just once, lazily. That could be useful
and/or performatic in some scenarios, specially if you will chain
additional query operations.
input = [{'a': 1, 'b': 'j'}, {'a': 2, 'b': 'j'}, {'a': 3, 'b': 'i'}]
s = dedupe(input, lambda x: x['b'])
s = map(lambda e: e['a'], s)
sum(s) # Only now the list is iterated. Result: 4
Accepts any kind of iterable:
Be it a list, set, dictionary or a custom iterable class. You can construct whatever collection type out of it, without iterating multiple times.
d = {'a': 1, 'b': 1, 'c': 2}
{k: v for k, v in dedupe(d.items(), lambda e: e[1])}
# Result (dict): {'a': 1, 'c': 2}
{*dedupe(d.items(), lambda e: e[1])}
# Result (set of tuples): {('a', 1), ('c', 2)}
Takes an optional selector function (or any callable):
This gives you flexibility to re-use this function in many different contexts, with any custom logic or types. If the selector is absent, it compares the whole elements.
# de-duping based on absolute value:
(*dedupe([-3, -2, -2, -1, 0, 1, 1, 2, 3, 3], abs),)
# Result: (-3, -2, -1, 0)
# de-duping without selector:
(*dedupe([-3, -2, -2, -1, 0, 1, 1, 2, 3, 3]),)
# Result: (-3, -2, -1, 0, 1, 2, 3)
def drop_dup_key(src, key):
''' src is the source list, and key is a function to obtain the key'''
keyset, result = set(), []
for elem in src:
keyval = key(elem)
if keyval not in keyset:
result.append(elem)
keyset.add(keyval)
return result
Use it like this:
drop_dup_key(in_list, lambda d: return d.get('b'))
The comparison of tuples to dictionaries isn't quite accurate since the tuples only contain the dictionary values, not the keys, and I believe you are asking about duplicate key:value pairs.
Here is a solution which I believe solves your problem, but might not be as pythonic as possible.
seen = set()
kept = []
for d in x:
keep = True
for k, v in d.items():
if (k, v) in seen:
keep = False
break
seen.add((k, v))
if keep:
kept.append(d)
print(kept)
Output:
[{'a': 1, 'b': 'j'}, {'a': 3, 'b': 'i'}]

count characters of a string using function

Write a function named count_letters that takes as a parameter a string and returns a dictionary that tabulates how many of each letter is in that string. The string can contain characters other than letters, but only the letters should be counted. The string could even be the empty string. Lower-case and upper-case versions of a letter should be part of the same count. The keys of the dictionary should be the upper-case letters. If a letter does not appear in the string, then it would not get added to the dictionary. For example, if the string is
"AaBb"
then the dictionary that is returned should contain these key-value pairs:
{'A': 2, 'B': 2}
def count_letters(string):
"""counts all the letters in a given string"""
your_dict = dict()
for x in string:
x = x.upper() # makes lowercase upper
if x not in your_dict:
your_dict[x]= 1
else:
your_dict[x] += 1
return your_dict
I am getting the following error when I go to upload:
Test Failed: {'Q': 1, 'U': 3, 'I': 3, 'S': 6, ' ': 3, 'C[48 chars]': 1} != {'S': 6, 'U': 3, 'I': 3, 'T': 3, 'O': 3, 'C[32 chars]': 1}
+ {'C': 2, 'D': 2, 'E': 2, 'I': 3, 'O': 3, 'P': 1, 'Q': 1, 'S': 6, 'T': 3, 'U': 3}
- {' ': 3,
- '?': 1,
- 'C': 2,
- 'D': 2,
- 'E': 2,
- 'I': 3,
- 'O': 3,
- 'P': 1,
- 'Q': 1,
- 'S': 6,
- 'T': 3,
- 'U': 3}
Try something like this. Feel free to adjust it to your requirements:
import collections
def count_letters(string):
return collections.Counter(string.upper())
print(count_letters('Google'))
Output: Counter({'G': 2, 'O': 2, 'L': 1, 'E': 1})
For documentation of the Counter dict subclass in collections module, check this.
Update without using collections module:
def count_letters(string):
your_dict={}
for i in string.upper():
if i in your_dict:
your_dict[i] += 1
else:
your_dict[i] = 1
return your_dict
Output: {'G': 2, 'O': 2, 'L': 1, 'E': 1}
This solution does use collections, but unlike with Counter we aren’t getting the entire solution from a single library function. I hope it’s permitted, and if it isn’t, that it will at least be informative in some way.
import collections as colls
def count_letters(str_in):
str_folded = str_in.casefold()
counts = colls.defaultdict(int)
for curr_char in str_folded:
counts[curr_char] += 1
return counts
defaultdict is extremely practical. As the name indicates, when we try to index a dictionary with a key that doesn’t exist, it creates a default value for that key and carries out our original operation. In this case, since we declare that our defaultdict will use integers for its keys, the default value is 0.
str.casefold() is a method designed specifically for the complex problem that is case-insensitive comparison. While it is unlikely to make a difference here, it’s a good function to know.
Let me know if you have any questions :)
Without using collections, here is a solution:
def count_letters(string):
string = string.upper()
counts = {}
for a in set(string):
counts[a] = string.count(a)
return counts
This function iterates over set(string), which is equal to all the letters used in your word, without duplicates, and in uppercase. Then it counts how many times each letter appears in your string, and adds it to your counts dictionary.
I hope this answers your question. :)

Filter item from a dict and process a subset

I have a list of dictionaries and I want to select the items corresponding to the keys that are in a a separate list (note: we can assume that each dict contains the keys). Furthermore I want to process a subset of the selected items.
As an example, given:
keys_of_interest = ['a', 'b']
all_items =[
{'a' : value_a1, 'b' : value_b1, 'c' : ...},
{'a' : value_a2, 'b' : value_b2, 'c' : ...},
...
]
The result I want is obtained by extracting the values corresponding to a and b and apply fun only to a, i.e.:
[
[fun(value_a1), value_b1],
[fun(value_a2), value_b2],
...
]
The key-extraction part can be accomplished via:
from operator import itemgetter
[itemgetter(*keys_of_interest)(el) for el in all_items]
Is there any (elegant) way to combine itemgetter with a function
If elegance is the priority, you can transform the dicts first, and then use itemgetter without further tweaking. To do this you need to separate the keys for which you want to apply a function from those for which you just want the original values -- which I think is a good thing to do in any case.
First, set up
keys_of_interest = ['a', 'b', 'c', 'd']
keys_for_func = ['a', 'b'] # with this approach you can apply a func to multiple keys
func = lambda x: x ** 2 # for demo
all_dicts = [{'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5},
{'a': 6, 'b': 7, 'c': 8, 'd': 9, 'e': 10}]
Then this is what I'd do:
transformed = ({k: func(v) if k in keys_for_func else v for k, v in d.items()}
for d in all_dicts)
result = list(map(itemgetter(*keys_of_interest), transformed))
Result:
[(1, 4, 3, 4), (36, 49, 8, 9)]
There probably isn't a super tidy way to do this. One way might be:
getter = itemgetter(*keys_of_interest[1:])
def process_dict(d):
return_value = [fun(d[keys_of_interest[0]])]
return_value.extend(getter(d))
return return_value
result = [process_dict(d) for d in all_items]
If you're targeting a recent-enough version of python, this can be done similarly with a bit less subscripting...
first_key, *other_keys = keys_of_interest
getter = itemgetter(*other_keys)
def process_dict(d):
return_value = [fun(d[first_key])]
return_value.extend(getter(d))
return return_value
Is this too inelegant?
from operator import itemgetter
result = [(f(x), *others) for x, *others in [
itemgetter(*keys_of_interest)(el) for el in all_items]
]
Or in older versions:
from operator import itemgetter
result = [(f(x[0]), x[1:]) for x in [
itemgetter(*keys_of_interest)(el) for el in all_items]
]
Python 3.5+ solution:
setup:
>>> from operator import itemgetter
>>> keys_of_interest = ['a', 'b', 'c'] # 3 keys to show unpacking at work
>>> all_items = [{'a': 1, 'b': 2, 'c': 3, 'd': 4}, {'a': 5, 'b': 6, 'c': 7, 'd': 8}]
>>> fun = str # some silly demo function
Apply map and the Python 3.5 unpacking:
>>> list(map(lambda x: (fun(x[0]), *x[1:]),
... (itemgetter(*keys_of_interest)(el) for el in all_items)))
[('1', 2, 3), ('5', 6, 7)]

Creating instance names from a list (Python)

I've got a function that builds a random number of object instances. For the sake of demonstrating the general idea, we're going to pretend that this is an algorithm to build a series of nethack-like rooms.
The requirements are such that I won't know how many instances there will be in advance; these are generated randomly and on-the-fly.
As a brief note: I am fully aware that the following code is nonfunctional, but it should (hopefully!) demonstrate my aims.
import random
class Levelbuild(object):
def __init__(self):
self.l1 = dict({0:'a',1:'b',2:'c',3:'d',4:'e',5:'f',6:'g',7:'h',8:'i'})
# Pick a random number between 4 and 9.
for i in range(random.randint(4,9)):
self.l1[i] = Roombuilder()
If we assume that the chosen random integer is 5, the ideal result would be 5 Roombuilder() instances; labeled a, b, c, d, and e, respectively.
Is there a simple way of doing this? Is there a way to do this period?
--Edit--
A giant "thank you" to Nick ODell for his answer. This isn't a complete copy/paste-- but it's a variation that works for what I need;
import random
class Room(object):
def __init__(self):
self.size = (5,5)
class Level(object):
def __init__(self):
roomnames = ['a','b','c','d','e','f','g','h','i']
self.rooms = {}
for i in range(random.randint(4, 9)):
self.rooms[roomnames[i]] = Room()
Rather than build each "room" by hand, I can now...
test = Level()
print test.rooms['a'].size
>>> (5,5)
import string
import random
class Levelbuild(object):
def __init__(self,min_room_count,max_room_count):
rooms_temp = [new RoomBuilder() for i in range(random.randint(min_room_count,max_room_count))]
self.l1 = dict(zip(string.ascii_lowercase, rooms_temp))
Note: This will fail silently if given more than 26 rooms.
You're pretty close, actually. You don't need a dict; just use a list. (And, by the way, {...} is already a dict, so wrapping it in dict() doesn't do anything.)
roomnames = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
self.rooms = []
for i in range(random.randint(4, 9)):
self.rooms.append(Roombuilder(roomnames[i]))
For what it's worth, putting builder in the name of a class is kind of funky. The objects are rooms, right? So the type should just be Room.
As another answer for a more general solution (mainly as a companion to Nick ODell's answer, if you want to handle any number of names, it's a pretty simply solution with an infinite generator:
import string
import itertools
def generate_names(characters):
for i in itertools.count(1):
for name in itertools.product(characters, repeat=i):
yield "".join(name)
You can then use it as you would any other generator:
>>>print(dict(zip(generate_names(string.ascii_lowercase), range(30))))
{'aa': 26, 'ac': 28, 'ab': 27, 'ad': 29, 'a': 0, 'c': 2, 'b': 1, 'e': 4, 'd': 3, 'g': 6, 'f': 5, 'i': 8, 'h': 7, 'k': 10, 'j': 9, 'm': 12, 'l': 11, 'o': 14, 'n': 13, 'q': 16, 'p': 15, 's': 18, 'r': 17, 'u': 20, 't': 19, 'w': 22, 'v': 21, 'y': 24, 'x': 23, 'z': 25}
If you need to generate actual names, then you have a few choices. If you need a real word, pick from a dictionary file (most Linux users have one in /usr/share/dict). If you need word-like things, then I have actually written a Python script to generate such 'words' using Markov Chains, which is available under the GPL. Naturally, you'd have to check these for uniqueness.

Categories