Input on dictionnary with a counter - python-3.x

I'm new at Python, and i need your help for this.
I have a user input like :
5 72 245 62
And i need to split this integers into a dictionary like this :
{1=5;2=72;3=245;4=62}
I tried something like :
sequence = dict(x ,input().split())
Where x is incrementing counter.

If your desired end result is a Python dictionary, then I think you're pretty close.
You can actually use a python builtin to achieve this called enumerate:
>>> values = input().split()
1 2 3 4
>>> values
['1', '2', '3', '4']
>>>
>>> sequence = dict(enumerate(values))
>>> sequence
{0: '1', 1: '2', 2: '3', 3: '4'}
enumerate just goes through any iterable (such as a list of strings) and outputs the index of each item and the item itself in a tuple:
>>> for x in enumerate(values):
... print(x)
...
(0, '1')
(1, '2')
(2, '3')
(3, '4')
You can then call dict on an iterable of tuples (which is what enumerate produces) in order to turn them into a dictionary.
Of course, enumerate, like most things is zero-indexed, so you can also pass in a starting number if you would like to start a 1:
>>> sequence = dict(enumerate(values, 1))
>>> sequence
{1: '1', 2: '2', 3: '3', 4: '4'}
The problem with what you have
Let's say, as above, we have a list of strings. In order to match up numbers with each string in the list, we need something like the following:
>>> dict([(1, '1'), (2, '2')...])
Notice that I am passing one argument to dict: a list of tuples where each item in the list looks like (1, '1') and I have one container holding all of them.
Your attempt was the following:
>>> sequence = dict(x ,input().split())
This is interpreted probably something like (guessing on the x):
>>> dict(1, ['1', '2', '3'])
Which produces the following Traceback:
>>> dict(1, [1, 2, 3])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: dict expected at most 1 arguments, got 2
You are passing two arguments to dict, not one, which is what it expects.
It expects some kind of container with a bunch of things in it where the first element of each thing is mapped to the second element, such as the following:
>>> [(1, '1'), (2, '2')]

Related

issue in nested dictionary when try to iterate through value

Why below code giving me an error?
nested_dict = {'first':{'a':1},'second':{'b':2}}
{i for i in nested_dict.values()}
error:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
C:\Users\ABHINA~1\AppData\Local\Temp/ipykernel_12056/1167728052.py in <module>
----> 1 {i for i in nested_dict.values()}
C:\Users\ABHINA~1\AppData\Local\Temp/ipykernel_12056/1167728052.py in <setcomp>(.0)
----> 1 {i for i in nested_dict.values()}
TypeError: unhashable type: 'dict'
You are confused about notation.
Consider these two examples:
>>> {i for i in [('a', 1), ('b', 2)]}
{('a', 1), ('b', 2)}
>>>
>>> {k: v for k, v in [('a', 1), ('b', 2)]}
{'a': 1, 'b': 2}
Using single variable i is asking for a set result.
Each element of a set must be hashable, typically immutable, like tuple, str or int.
Using the key: value notation OTOH would be asking for a dict result.
Hi actually i tried this and it works:
nested_dict = {'first':{'a':1},'second':{'b':2}}
for i in nested_dict:
for j in nested_dict[i]:
print(j)
and also this works too :
nested_dict = {'first':{'a':1},'second':{'b':2}}
for i in nested_dict.values():
print(i)

How to handle list with nested lists and strings elements?

I want to flat a list that has nested and not nested elements. From this solution I've tried and it works if all elements within mylist were lists, but
in mylist I have simple text strings and nested lists.
My list is like this:
mylist = [
'tz',
'7',
['a', 'b', 'c'],
[['2'], ['4', 'r'], ['34']],
[['7'], ['3', ['2', ['1']]], ['9']],
[['11',['7','w']], 'U1', ['0']]
]
And my current code is this, getting the error below:
import collections#.abc
def flatten(l):
for el in l:
if isinstance(el, collections.Iterable) and not isinstance(el, (str, bytes)):
yield from flatten(el)
else:
yield el
mylist1=[list(flatten(sublist)) if type(sublist) is list else sublist for sublist in mylist]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <listcomp>
File "<stdin>", line 3, in flatten
TypeError: isinstance() arg 2 must be a type or tuple of types
>>>
My expected output would be like this
mylist1 = [
'tz',
'7',
['a', 'b', 'c'],
['2', '4', 'r','34'],
['7','3','2','1','9'],
['11','7','w','U1','0']
]
What is missing to fix this? thanks.
UPDATE
Now I'm getting this error with code suggested by #Alok
>>> for item in mylist:
... # if the item in mylist is a list, pass it to your flatten method else
... # add it to the final list
... if isinstance(item, list):
... final_list.append(list(flatten(item)))
... else:
... final_list.append(item)
...
Traceback (most recent call last):
File "<stdin>", line 5, in <module>
File "<stdin>", line 3, in flatten
TypeError: isinstance() arg 2 must be a type or tuple of types
The problem is with some instances which I will point out:
Python 3.x is not following collections.Iterable anymore, you need to import the items from collections.abc and always do try-catch for any import error
try:
from collections.abc import Iterable
except ImportError:
from collections import Iterable
Not utilizing your flatten method appropriately, the method, returns the data, but you have to store it in the form of a list and append it to the final answer list YOUR SUB ARRAYS ONLY BE PASSED INTO THIS METHOD
data.append(list(flatten(item)))
FINAL SOLUTION:
try:
from collections.abc import Iterable
except ImportError:
from collections import Iterable
mylist = [
'tz',
'7',
['a', 'b', 'c'],
[['2'], ['4', 'r'], ['34']],
[['7'], ['3', ['2', ['1']]], ['9']],
[['11',['7','w']], 'U1', ['0']]
]
def flatten(l):
for el in l:
if isinstance(el, Iterable) and not isinstance(el, (str, bytes)):
yield from flatten(el)
else:
yield el
final_list = []
for item in mylist:
# if the item in mylist is a list, pass it to your flatten method else
# add it to the final list
if isinstance(item, type([])): final_list.append(list(flatten(item)))
else: final_list.append(item)
print(final_list)
OUTPUT
['tz', '7', ['a', 'b', 'c'], ['2', '4', 'r', '34'], ['7', '3', '2', '1', '9'], ['11', '7', 'w', 'U1', '0']]
I hope you can achieve your desired output in this way.

Why does list.__iadd__ work with other iterable object types when __add__ does not?

I recently came across a python FIFO buffer example, which initialized the buffer as an empty list despite the intended input and output being of type bytes or bytearray. To my surprise this was not a mistake, and I have been left stumped as to why list.__iadd__ works in this case, but not list.__add__.
Below is code demonstrating this discrepancy (python version 3.7.4).
>>> buffer = []
>>> some_bytes = b'12345'
>>> some_bytearray = bytearray(some_bytes)
>>> buffer + some_bytes
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate list (not "bytes") to list
>>> buffer += some_bytes
>>> buffer
[49, 50, 51, 52, 53]
>>> buffer + some_bytearray
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate list (not "bytearray") to list
>>> buffer += some_bytearray
>>> buffer
[49, 50, 51, 52, 53, 49, 50, 51, 52, 53]
Edit: this same functionality also applies to other iterate types such as str, dict and tuple. Does __iadd__ attempt to mutate the argument if __add__ raises a TypeError?
List concatenation (adding) requires that both variables in the operation are lists - this (I guess) allows us to be sure of the type of object that will result from the operation. Otherwise we predict cannot the type of object that resulted from
some_list + some_set
On the other hand, extending the a list (somelist += ...) only requires that the object on the right hand side be an iterable, so that each of its elements can be added to the original list.
>>> L = []
>>> L += {1, 2, 3}
>>> L
[1, 2, 3]
>>> L += ('a', 'b', 'c')
>>> L
[1, 2, 3, 'a', 'b', 'c']
>>> L += ['x', 'y', 'z']
>>> L
[1, 2, 3, 'a', 'b', 'c', 'x', 'y', 'z']
In short, concatenation returns a new object, so the left and right and sides of the expression need to be of the same type. In-place addition mutates the original object, so the object on the left hand side need only be an iterable, its type is irrelevant.
The answer appears to be in:
a = bytearray()
help(a)
....
| __add__(self, value, /)
| Return self+value.
...
| __iadd__(self, value, /)
| Implement self+=value.
...
Thus, iadd changes the class while add does not.

populating a dictionary of lists behavior [duplicate]

My attempt to programmatically create a dictionary of lists is failing to allow me to individually address dictionary keys. Whenever I create the dictionary of lists and try to append to one key, all of them are updated. Here's a very simple test case:
data = {}
data = data.fromkeys(range(2),[])
data[1].append('hello')
print data
Actual result: {0: ['hello'], 1: ['hello']}
Expected result: {0: [], 1: ['hello']}
Here's what works
data = {0:[],1:[]}
data[1].append('hello')
print data
Actual and Expected Result: {0: [], 1: ['hello']}
Why is the fromkeys method not working as expected?
When [] is passed as the second argument to dict.fromkeys(), all values in the resulting dict will be the same list object.
In Python 2.7 or above, use a dict comprehension instead:
data = {k: [] for k in range(2)}
In earlier versions of Python, there is no dict comprehension, but a list comprehension can be passed to the dict constructor instead:
data = dict([(k, []) for k in range(2)])
In 2.4-2.6, it is also possible to pass a generator expression to dict, and the surrounding parentheses can be dropped:
data = dict((k, []) for k in range(2))
Try using a defaultdict instead:
from collections import defaultdict
data = defaultdict(list)
data[1].append('hello')
This way, the keys don't need to be initialized with empty lists ahead of time. The defaultdict() object instead calls the factory function given to it, every time a key is accessed that doesn't exist yet. So, in this example, attempting to access data[1] triggers data[1] = list() internally, giving that key a new empty list as its value.
The original code with .fromkeys shares one (mutable) list. Similarly,
alist = [1]
data = dict.fromkeys(range(2), alist)
alist.append(2)
print(data)
would output {0: [1, 2], 1: [1, 2]}. This is called out in the dict.fromkeys() documentation:
All of the values refer to just a single instance, so it generally doesn’t make sense for value to be a mutable object such as an empty list.
Another option is to use the dict.setdefault() method, which retrieves the value for a key after first checking it exists and setting a default if it doesn't. .append can then be called on the result:
data = {}
data.setdefault(1, []).append('hello')
Finally, to create a dictionary from a list of known keys and a given "template" list (where each value should start with the same elements, but be a distinct list), use a dictionary comprehension and copy the initial list:
alist = [1]
data = {key: alist[:] for key in range(2)}
Here, alist[:] creates a shallow copy of alist, and this is done separately for each value. See How do I clone a list so that it doesn't change unexpectedly after assignment? for more techniques for copying the list.
You could use a dict comprehension:
>>> keys = ['a','b','c']
>>> value = [0, 0]
>>> {key: list(value) for key in keys}
{'a': [0, 0], 'b': [0, 0], 'c': [0, 0]}
This answer is here to explain this behavior to anyone flummoxed by the results they get of trying to instantiate a dict with fromkeys() with a mutable default value in that dict.
Consider:
#Python 3.4.3 (default, Nov 17 2016, 01:08:31)
# start by validating that different variables pointing to an
# empty mutable are indeed different references.
>>> l1 = []
>>> l2 = []
>>> id(l1)
140150323815176
>>> id(l2)
140150324024968
so any change to l1 will not affect l2 and vice versa.
this would be true for any mutable so far, including a dict.
# create a new dict from an iterable of keys
>>> dict1 = dict.fromkeys(['a', 'b', 'c'], [])
>>> dict1
{'c': [], 'b': [], 'a': []}
this can be a handy function.
here we are assigning to each key a default value which also happens to be an empty list.
# the dict has its own id.
>>> id(dict1)
140150327601160
# but look at the ids of the values.
>>> id(dict1['a'])
140150323816328
>>> id(dict1['b'])
140150323816328
>>> id(dict1['c'])
140150323816328
Indeed they are all using the same ref!
A change to one is a change to all, since they are in fact the same object!
>>> dict1['a'].append('apples')
>>> dict1
{'c': ['apples'], 'b': ['apples'], 'a': ['apples']}
>>> id(dict1['a'])
>>> 140150323816328
>>> id(dict1['b'])
140150323816328
>>> id(dict1['c'])
140150323816328
for many, this was not what was intended!
Now let's try it with making an explicit copy of the list being used as a the default value.
>>> empty_list = []
>>> id(empty_list)
140150324169864
and now create a dict with a copy of empty_list.
>>> dict2 = dict.fromkeys(['a', 'b', 'c'], empty_list[:])
>>> id(dict2)
140150323831432
>>> id(dict2['a'])
140150327184328
>>> id(dict2['b'])
140150327184328
>>> id(dict2['c'])
140150327184328
>>> dict2['a'].append('apples')
>>> dict2
{'c': ['apples'], 'b': ['apples'], 'a': ['apples']}
Still no joy!
I hear someone shout, it's because I used an empty list!
>>> not_empty_list = [0]
>>> dict3 = dict.fromkeys(['a', 'b', 'c'], not_empty_list[:])
>>> dict3
{'c': [0], 'b': [0], 'a': [0]}
>>> dict3['a'].append('apples')
>>> dict3
{'c': [0, 'apples'], 'b': [0, 'apples'], 'a': [0, 'apples']}
The default behavior of fromkeys() is to assign None to the value.
>>> dict4 = dict.fromkeys(['a', 'b', 'c'])
>>> dict4
{'c': None, 'b': None, 'a': None}
>>> id(dict4['a'])
9901984
>>> id(dict4['b'])
9901984
>>> id(dict4['c'])
9901984
Indeed, all of the values are the same (and the only!) None.
Now, let's iterate, in one of a myriad number of ways, through the dict and change the value.
>>> for k, _ in dict4.items():
... dict4[k] = []
>>> dict4
{'c': [], 'b': [], 'a': []}
Hmm. Looks the same as before!
>>> id(dict4['a'])
140150318876488
>>> id(dict4['b'])
140150324122824
>>> id(dict4['c'])
140150294277576
>>> dict4['a'].append('apples')
>>> dict4
>>> {'c': [], 'b': [], 'a': ['apples']}
But they are indeed different []s, which was in this case the intended result.
You can use this:
l = ['a', 'b', 'c']
d = dict((k, [0, 0]) for k in l)
You are populating your dictionaries with references to a single list so when you update it, the update is reflected across all the references. Try a dictionary comprehension instead. See
Create a dictionary with list comprehension in Python
d = {k : v for k in blah blah blah}
You could use this:
data[:1] = ['hello']

Change a list of list of str to list of list of int

I am trying to read a CSV file and have the data come back as a list of list of ints. The CSV file is 6 columns wide and 2 rows. Using Python 3.4.
It always comes out as a list of list of strs. Searching StackOverflow and Google shows 7 different ways to do this, none of which work. These are shown below the code as what I have tried.
import csv
b = []
with open('C:\Python34\DataforProgramstorun\\csv data6x2.csv') as f:
reader = csv.reader(f)
for row in reader :
b.append(row) # this gives me a list of list each element a csv str from print (b)
print (b)
The result is:
[['2', '5', '15', '17', '19', '20'], ['6', '8', '14', '18', '21', '30']]
I would like it to be:
[[2, 5, 15, 17, 19, 20], [6, 8, 14, 18, 21, 30]]
None of the following work:
[ int(x) for y in b for x in y.split() ] #builtins.AttributeError: 'list' object has no attribute 'split'
[int(x) for x in ' '.join(b).split ()] #builtins.TypeError: sequence item 0: expected str instance, list found
import itertools as it; new =list(it.imap(int,b)) #builtins.AttributeError: 'module' object has no attribute 'imap'
for i in range (0,len(b)): b[i] = int (b[i]) #builtins.TypeError: int() argument must be a string or a number, not 'list'
results = b; results = [int(i) for i in results] ##builtins.TypeError: int() argument must be a string or a number, not'list'
b = list(map(int,b)) #builtins.TypeError: int() argument must be a string or a number, not 'list'
[int(i) for i in b] #builtins.TypeError: int() argument must be a string or a number, not 'list'
>>> lst = [['2', '5', '15', '17', '19', '20'], ['6', '8', '14', '18', '21', '30']]
>>> [[int(x) for x in inner] for inner in lst]
[[2, 5, 15, 17, 19, 20], [6, 8, 14, 18, 21, 30]]
The problem with all your tried solutions is that you only go one level deep. So you do consider the outer list, but then try to work with the inner list directly, which usually fails. To solve this, you need to actually work on the inner lists directly. You could also solve it like this:
for i, sublist in enumerate(b):
b[i] = [int(x) for x in sublist]
And instead of [int(x) for x in sublist] you could also use one of the many other solution to convert all strings in a (sub)list to ints, for example list(map(int, sublist)).

Resources