Python using braces inside list brackets - python

Im very new to Python and finding it very different to anything ive encountered before coming from the PHP realm.
Background:
Ive searched SO and learned that the differences between:
x = [] and x{} is that the brackets will create a list and the curly braces is for creating a series. What I could not find however is an explination for the following
Question
Why does this piece of code use braces inside a list with brackets like so:
context.modules[(stock, length)] = 0
Why the braces inside the list?
And then as a "bonus help" if I may why set it to 0 (although that's probably out of scope of question)
Fullcode:
context.modules = {}
for stock in context.stock_list:
for length in context.channels:
context.modules[(stock, length)] = 0

This is a dictionary, as you understand:
In [463]: dct = {'a' : 1}
In [464]: dct['a']
Out[464]: 1
It has one key-value entry. The key is a str type. strs are allowed to be keys for a dictionary because they can be hashed because they are immutable and their objects yield a unique, unchanging hash value which the dictionary uses to index the object for fast access.
Similarly, ints, floats, bools, basically anything that is immutable may be a key. This includes tuples, but not structures such as lists, sets and dicts.
Now, here's a tuple:
In [466]: x = (1, 2)
x can be used as the key in a dictionary.
In [469]: d = {x : 5}
In [470]: d
Out[470]: {(1, 2): 5}
To access the value associated with the tuple, you'd index with it:
In [471]: d[(1, 2)]
Out[471]: 5
This is the basic meaning behind that syntax you ask about. It is interesting to note that the parenthesis are optional:
In [472]: d[1, 2]
Out[472]: 5
This is because the parenthesis do not demarcate a tuple, but the comma , does.

Your context.modules is not a list, it's a python dictionary (map in most other languages)
So, when you're doing this:
context.modules[(stock, length)] = 0
You're basically creating a key, value pair in the dictionary, with key as a tuple of (stock, length) and value as 0.
Considering
stock = 2 and length = 5
your context.modules after the above assignment, will look like this:
>>> context.modules
{(2, 5): 0}

Related

Creating a list by iterating over a dictionary

I defined a dictionary like this (list is a list of integers):
my_dictionary = {'list_name' : list, 'another_list_name': another_list}
Now, I want to create a new list by iterating over this dictionary. In the end, I want it to look like this:
my_list = [list_name_list_item1, list_name_list_item2,
list_name_list_item3, another_list_name_another_list_item1]
And so on.
So my question is: How can I realize this?
I tried
for key in my_dictionary.keys():
k = my_dictionary[key]
for value in my_dictionary.values():
v = my_dictionary[value]
v = str(v)
my_list.append(k + '_' + v)
But instead of the desired output I receive a Type Error (unhashable type: 'list') in line 4 of this example.
You're trying to get a dictionary item by it's value whereas you already have your value.
Do it in one line using a list comprehension:
my_dictionary = {'list_name' : [1,4,5], 'another_list_name': [6,7,8]}
my_list = [k+"_"+str(v) for k,lv in my_dictionary.items() for v in lv]
print(my_list)
result:
['another_list_name_6', 'another_list_name_7', 'another_list_name_8', 'list_name_1', 'list_name_4', 'list_name_5']
Note that since the order in your dictionary is not guaranteed, the order of the list isn't either. You could fix the order by sorting the items according to keys:
my_list = [k+"_"+str(v) for k,lv in sorted(my_dictionary.items()) for v in lv]
Try this:
my_list = []
for key in my_dictionary:
for item in my_dictionary[key]:
my_list.append(str(key) + '_' + str(item))
Hope this helps.
Your immediate problem is that dict().values() is a generator yielding the values from the dictionary, not the keys, so when you attempt to do a lookup on line 4, it fails (in this case) as the values in the dictionary can't be used as keys. In another case, say {1:2, 3:4}, it would fail with a KeyError, and {1:2, 2:1} would not raise an error, but likely give confusing behaviour.
As for your actual question, lists do not attribute any names to data, like dictionaries do; they simply store the index.
def f()
a = 1
b = 2
c = 3
l = [a, b, c]
return l
Calling f() will return [1, 2, 3], with any concept of a, b, and c being lost entirely.
If you want to simply concatenate the lists in your dictionary, making a copy of the first, then calling .extend() on it will suffice:
my_list = my_dictionary['list_name'][:]
my_list.extend(my_dictionary['another_list_name'])
If you're looking to keep the order of the lists' items, while still referring to them by name, look into the OrderedDict class in collections.
You've written an outer loop over keys, then an inner loop over values, and tried to use each value as a key, which is where the program failed. Simply use the dictionary's items method to iterate over key,value pairs instead:
["{}_{}".format(k,v) for k,v in d.items()]
Oops, failed to parse the format desired; we were to produce each item in the inner list. Not to worry...
d={1:[1,2,3],2:[4,5,6]}
list(itertools.chain(*(
["{}_{}".format(k,i) for i in l]
for (k,l) in d.items() )))
This is a little more complex. We again take key,value pairs from the dictionary, then make an inner loop over the list that was the value and format those into strings. This produces inner sequences, so we flatten it using chain and *, and finally save the result as one list.
Edit: Turns out Python 3.4.3 gets quite confused when doing this nested as generator expressions; I had to turn the inner one into a list, or it would replace some combination of k and l before doing the formatting.
Edit again: As someone posted in a since deleted answer (which confuses me), I'm overcomplicating things. You can do the flattened nesting in a chained comprehension:
["{}_{}".format(k,v) for k,l in d.items() for v in l]
That method was also posted by Jean-François Fabre.
Use list comprehensions like this
d = {"test1":[1,2,3,],"test2":[4,5,6],"test3":[7,8,9]}
new_list = [str(item[0])+'_'+str(v) for item in d.items() for v in item[1]]
Output:
new_list:
['test1_1',
'test1_2',
'test1_3',
'test3_7',
'test3_8',
'test3_9',
'test2_4',
'test2_5',
'test2_6']
Let's initialize our data
In [1]: l0 = [1, 2, 3, 4]
In [2]: l1 = [10, 20, 30, 40]
In [3]: d = {'name0': l0, 'name1': l1}
Note that in my example, different from yours, the lists' content is not strings... aren't lists heterogeneous containers?
That said, you cannot simply join the keys and the list's items, you'd better cast these value to strings using the str(...) builtin.
Now it comes the solution to your problem... I use a list comprehension
with two loops, the outer loop comes first and it is on the items (i.e., key-value couples) in the dictionary, the inner loop comes second and it is on the items in the corresponding list.
In [4]: res = ['_'.join((str(k), str(i))) for k, l in d.items() for i in l]
In [5]: print(res)
['name0_1', 'name0_2', 'name0_3', 'name0_4', 'name1_10', 'name1_20', 'name1_30', 'name1_40']
In [6]:
In your case, using str(k)+'_'+str(i) would be fine as well, but the current idiom for joining strings with a fixed 'text' is the 'text'.join(...) method. Note that .join takes a SINGLE argument, an iterable, and hence in the list comprehension I used join((..., ...))
to collect the joinands in a single argument.

how to convert a set in python into a dictionary

I am new to python and trying to convert a Set into a Dictionary. I am struggling to find a way to make this possible. Any inputs are highly appreciated. Thanks.
Input : {'1438789225', '1438789230'}
Output : {'1438789225':1, '1438789230':2}
Use enumerate() to generate a value starting from 0 and counting upward for each item in the dictionary, and then assign it in a comprehension:
input_set = {'1438789225', '1438789230'}
output_dict = {item:val for val,item in enumerate(input_set)}
Or a traditional loop:
output_dict = {}
for val,item in enumerate(input_set):
output_dict[item] = val
If you want it to start from 1 instead of 0, use item:val+1 for the first snippet and output_dict[item] = val+1 for the second snippet.
That said, this dictionary would be pretty much the same as a list:
output = list(input_set)
My one-liner:
output = dict(zip(input_set, range(1, len(s) + 1)))
zip mixes two lists (or sets) element by element (l1[0] + l2[0] + l1[1] + l2[1] + ...).
We're feeding it two things:
the input_set
a list from 1 to the length of the set + 1 (since you specified you wanted to count from 1 onwards, not from 0)
The output is a list of tuples like [('1438789225', 1), ('1438789230', 2)] which can be turned into a dict simply by feeding it to the dict constructor... dict.
But like TigerhawkT3 said, I can hardly find a use for such a dictionary. But if you have your motives there you have another way of doing it. If you take away anything from this post let it be the existence of zip.
an easy way of doing this is by iterating on the set, and populating the result dictionary element by element, using a counter as dictionary key:
def setToIndexedDict(s):
counter = 1
result = dict()
for element in s:
result[element] = counter #adding new element to dictionary
counter += 1 #incrementing dictionary key
return result
My Python is pretty rusty, but this should do it:
def indexedDict(oldSet):
dic = {}
for elem,n in zip(oldSet, range(len(oldSet)):
dic[elem] = n
return dic
If I wrote anything illegal, tell me and I'll fix it. I don't have an interpreter handy.
Basically, I'm just zipping the list with a range object (basically a continuous list of numbers, but more efficient), then using the resulting tuples.
Id got with Tiger's answer, this is basically a more naive version of his.

Python build one dictionary from a list of keys, and a list of lists of values

So I have a list of keys:
keys = ['id','name', 'date', 'size', 'actions']
and I also have a list of lists of vales:
values=
[
['1','John','23-04-2015','0','action1'],
['2','Jane','23-04-2015','1','action2']
]
How can I build a dictionary with those keys matched to the values?
The output should be:
{
'id':['1','2'],
'name':['John','Jane'],
'date':['23-04-2015','23-04-2015'],
'size':['0','1'],
'actions':['action1','action2']
}
EDIT:
I tried to use zip() and dict(), but that would only work if the list of values had 1 list, i.e. values = [['1','John','23-04-2015','0','action1']]
for list in values:
dic = dict(zip(keys,list))
I also thought about initialising a dic with the keys, then building the list of values on my own, but I felt that there had to be an easier way to do it.
dic = dict.fromkeys(keys)
for list in values:
ids = list[0]
names = list[1]
dates = list[2]
sizes = list[3]
actions = list[4]
and then finally
dic['id'] = ids
dic['name'] = names
dic['date'] = dates
dic['size'] = sizes
dic['action'] = actions
This seemed really silly and I was wondering what a better way of doing it would be.
>>> keys = ['id','name', 'date', 'size', 'actions']
>>> values = [['1','John','23-04-2015','0','action1'], ['2','Jane','23-04-2015','1','action2']]
>>> c = {x:list(y) for x,y in zip(keys, zip(*values))}
>>> c
{'id': ['1', '2'], 'size': ['0', '1'], 'actions': ['action1', 'action2'], 'date': ['23-04-2015', '23-04-2015'], 'name': ['John', 'Jane']}
>>> print(*(': '.join([item, ', '.join(c.get(item))]) for item in sorted(c, key=lambda x: keys.index(x))), sep='\n')
id: 1, 2
name: John, Jane
date: 23-04-2015, 23-04-2015
size: 0, 1
actions: action1, action2
This uses several tools:
c is created with a dictionary comprehension. Comprehensions are a different way of expressing an iterable like a dictionary or a list. Instead of initializing an empty iterable and then using a loop to add elements to it, a comprehension moves these syntactical structures around.
result = [2*num for num in range(10) if num%2]
is equivalent to
result = []
for num in range(10):
if num%2: # shorthand for "if num%2 results in non-zero", or "if num is not divisible by 2"
result.append(2*num)
and we get [2, 6, 10, 14, 18].
zip() creates a generator of tuples, where each element of each tuple is the corresponding element of one of the arguments you passed to zip().
>>> list(zip(['a','b'], ['c','d']))
[('a', 'c'), ('b', 'd')]
zip() takes multiple arguments - if you pass it one large list containing smaller sublists, the result is different:
>>> list(zip([['a','b'], ['c','d']]))
[(['a', 'b'],), (['c', 'd'],)]
and generally not what we want. However, our values list is just such a list: a large list containing sublists. We want to zip() those sublists. This is a great time to use the * operator.
The * operator represents an "unpacked" iterable.
>>> print(*[1,2,3])
1 2 3
>>> print(1, 2, 3)
1 2 3
It is also used in function definitions:
>>> def func(*args):
... return args
...
>>> func('a', 'b', [])
('a', 'b', [])
So, to create the dictionary, we zip() the lists of values together, then zip() that with the keys. Then we iterate through each of those tuples and create a dictionary out of them, with each tuple's first item being the key and the second item being the value (cast as a list instead of a tuple).
To print this, we could make a large looping structure, or we can make generators (quicker to assemble and process than full data structures like a list) and iterate through them, making heavy use of * to unpack things. Remember, in Python 3, print can accept multiple arguments, as seen above.
We will first sort the dictionary, using each element's position in keys as the key. If we use something like key=len, that sends each element to the len() function and uses the returned length as the key. We use lambda to define an inline, unnamed function, that takes an argument x and returns x's index in the list of keys. Note that the dictionary isn't actually sorted; we're just setting it up so we can iterate through it according to a sort order.
Then we can go through this sorted dictionary and assemble its elements into printable strings. At the top level, we join() a key with its value separated by ': '. Each value has its elements join()ed with ', '. Note that if the elements weren't strings, we would have to turn them into strings for join() to work.
>>> list(map(str, [1,2,3]))
['1', '2', '3']
>>> print(*map(str, [1,2,3]))
1 2 3
The generator that yields each of these join()ed lines is then unpacked with the * operator, and each element is sent as an argument to print(), specifying a separator of '\n' (new line) instead of the default ' ' (space).
It's perfectly fine to use loops instead of comprehensions and *, and then rearrange them into such structures after your logic is functional, if you want. It's not particularly necessary most of the time. Comprehensions sometimes execute slightly faster than equivalent loops, and with practice you may come to prefer the syntax of comprehensions. Do learn the * operator, though - it's an enormously versatile tool for defining functions. Also look into ** (often referred to with "double star" or "kwargs"), which unpacks dictionaries into keyword arguments and can also be used to define functions.

How to get dictionary objects from a list of tuple objects?

I have following list of tuple objects :
z = [set([33, u'11:22:33:44:55:ff']), set([12, u'11:22:33:44:54:ce'])]
I want to have following list of dictionary objects out of it :
d = [{33 : '11:22:33:44:55:ff'}, {12, '11:22:33:44:54:ce'}]
Please see -- I want to use first element of set, as dictionary key.
Can someone suggest me a code snippet, for how to perform this ?
Edit
Sorry it is a list of two element set objects (and not list of tuple objects).
Sets are unordered. There is no "first" element of the set.
You will have to enforce some ordering on them - checking the type works:
dict(sorted(x,key=lambda k:isinstance(k,int),reverse=True) for x in z)
Out[33]: {12: u'11:22:33:44:54:ce', 33: u'11:22:33:44:55:ff'}
You have a list of sets, not list of tuples. If you want to use tuples, your list z should be:
z = [tuple([33, u'11:22:33:44:55:ff']), tuple([12, u'11:22:33:44:54:ce'])]
Then the following code should work
d = dict()
for item in z:
d[item[0]] = item[1]

Storing and updating lists in Python dictionaries: why does this happen?

I have a list of data that looks like the following:
// timestep,x_position,y_position
0,4,7
0,2,7
0,9,5
0,6,7
1,2,5
1,4,7
1,9,0
1,6,8
... and I want to make this look like:
0, (4,7), (2,7), (9,5), (6,7)
1, (2,5), (4,7), (9,0), (6.8)
My plan was to use a dictionary, where the value of t is the key for the dictionary, and the value against the key would be a list. I could then append each (x,y) to the list. Something like:
# where t = 0, c = (4,7), d = {}
# code 1
d[t].append(c)
Now this causes IDLE to fail. However, if I do:
# code 2
d[t] = []
d[t].append(c)
... this works.
So the question is: why does code 2 work, but code 1 doesn't?
PS Any improvement on what I'm planning on doing would be of great interest!! I think I will have to check the dictionary on each loop through the input to see if the dictionary key already exists, I guess by using something like max(d.keys()): if it is there, append data, if not create the empty list as the dictionary value, and then append data on the next loop through.
Let's look at
d[t].append(c)
What is the value of d[t]? Try it.
d = {}
t = 0
d[t]
What do you get? Oh. There's nothing in d that has a key of t.
Now try this.
d[t] = []
d[t]
Ahh. Now there's something in d with a key of t.
There are several things you can do.
Use example 2.
Use setdefault. d.setdefault(t,[]).append(c).
Use collections.defaultdict. You'd use a defaultdict(list) instead of a simple dictionary, {}.
Edit 1. Optimization
Given input lines from a file in the above form: ts, x, y, the grouping process is needless. There's no reason to go from a simple list of ( ts, x, y ) to a more complex
list of ( ts, (x,y), (x,y), (x,y), ... ). The original list can be processed exactly as it arrived.
d= collections.defaultdict(list)
for ts, x, y in someFileOrListOrQueryOrWhatever:
d[ts].append( (x,y) )
Edit 2. Answer Question
"when initialising a dictionary, you need to tell the dictionary what the key-value data structure will look like?"
I'm not sure what the question means. Since, all dictionaries are key-value structures, the question's not very clear. So, I'll review the three alternatives, which may answer the question.
Example 2.
Initialization
d= {}
Use
if t not in d:
d[t] = list()
d[t].append( c )
Each dictionary value must be initialized to some useful structure. In this case, we check to see if the key is present; when the key is missing, we create the key and assign an empty list.
Setdefault
Initialization
d= {}
Use
d.setdefault(t,list()).append( c )
In this case, we exploit the setdefault method to either fetch a value associated with a key or create a new value associated with a missing key.
default dict
Initialization
import collections
d = collections.defaultdict(list)
Use
d[t].append( c )
The defaultdict uses an initializer function for missing keys. In this case, we provide the list function so that a new, empty list is created for a missing key.
I think you want to use setdefault. It's a bit weird to use but does exactly what you need.
d.setdefault(t, []).append(c)
The .setdefault method will return the element (in our case, a list) that's bound to the dict's key t if that key exists. If it doesn't, it will bind an empty list to the key t and return it. So either way, a list will be there that the .append method can then append the tuple c to.
dict=[] //it's not a dict, it's a list, the dictionary is dict={}
elem=[1,2,3]
dict.append(elem)
you can access the single element in this way:
print dict[0] // 0 is the index
the output will be:
[1, 2, 3]
In the case your data is not already sorted by desired criteria, here's the code that might help to group the data:
#!/usr/bin/env python
"""
$ cat data_shuffled.txt
0,2,7
1,4,7
0,4,7
1,9,0
1,2,5
0,6,7
1,6,8
0,9,5
"""
from itertools import groupby
from operator import itemgetter
# load the data and make sure it is sorted by the first column
sortby_key = itemgetter(0)
data = sorted((map(int, line.split(',')) for line in open('data_shuffled.txt')),
key=sortby_key)
# group by the first column
grouped_data = []
for key, group in groupby(data, key=sortby_key):
assert key == len(grouped_data) # assume the first column is 0,1, ...
grouped_data.append([trio[1:] for trio in group])
# print the data
for i, pairs in enumerate(grouped_data):
print i, pairs
Output:
0 [[2, 7], [4, 7], [6, 7], [9, 5]]
1 [[4, 7], [9, 0], [2, 5], [6, 8]]

Categories