recursively add to python dictionary from tuple - python-3.x

I'm attempting to take the tuple ('a', 'b', 'c') and create a layered dictionary like this:
{'a': {'b': {'c': {}}}}. I'm using recursion to do this. When I print out the dictionary after each stage of my script (many prints simply for debugging purposes) it shows that the dictionary is being created correctly, but then it is taken apart and left incorrect. The dictionary I'm left with is {'c': {}}. I must be doing something improper with the recursion part. Any help will be much appreciate. Here is my code:
def incr_dict(dct, tpl):
if len(tpl) == 0:
dct = dct
print(dct)
print('1')
else:
dct = {tpl[-1]:dct}
print(dct)
print('2')
incr_dict(dct, tpl[0:-1])
print(dct)
print('3')
return dct
dct = {}
tpl = ('a', 'b', 'c')
dct=incr_dict(dct, tpl)
print(dct)
print('4')

You're ALMOST there!! Change the incr_dict(dct, tpl[0:-1]) line to read return incr_dict(dct, tpl[0:-1]). I believe that will fix the problem.
When using recursion, it is important to return the recursive call -- otherwise the 'higher levels' of the recursion can't make use of the new information. By returning the recursion, the execution will continue to recurse until the terminating condition is met, and then the computed values will begin to be returned up the chain until they are finally returned from the first invocation of the function.
The final code should look as follows:
def incr_dict(dct, tpl):
if len(tpl) == 0:
dct = dct
else:
dct = {tpl[-1]:dct}
return incr_dict(dct, tpl[0:-1])
return dct
dct = {}
tpl = ('a', 'b', 'c')
dct=incr_dict(dct, tpl)
print(dct)
I removed some of the debugging statements for clarity.

Related

Python list comprehension ignore None results?

I have the following toy example function and list comprehension:
def foo(lst):
if lst:
return lst[0] + lst[1]
[foo(l) for l in [[], [1,2], [1,4]]]
The result is:
[None, 3, 4]
How can I avoid the Nones, I would like to avoid calling if foo(l) is not None inside the list comp.
Please advise.
If you want to avoid calling the function more than once, you can make a generator that yields based on the result of the function. It's a little more code, but avoids making a list with a bunch of None values which have to be filtered later, and also avoids calling the function twice in the list comprehension:
def expensive_function(lst):
# sometimes returns None...hard to know when without calling
if lst:
return lst[0] + lst[1]
def gen_results(l):
for a in l:
res = expensive_function(a)
if res:
yield res
inp = [[], [1,2], [1,4]]
list(gen_results(inp))
# [3, 5]
Also, since generators are lazy, you don't need to make a list if you don't need a list.

Why is output only odd or even numbers from a Python iterator?

I have a problem using an iterator in Python. I want to print only the numbers 1 to 20, but my code gives me the odd or even numbers only. Why is this occurring?
I also tried by tracking it, but still I don't get the answer I expect.
class mynumbers:
def __iter__(self):
self.a = 1
return self
def __next__(self):
#print(a) for tracking purpose
if(self.a<=20):
x = self.a
self.a +=1
#print('loop1')
return x
else:
#print('loop2')
raise StopIteration
myclass = mynumbers()
myiter = iter(myclass)
for y in myiter:
print(next(myiter))
This program prints even numbers. If I change the value a to 0 then it gives odd numbers. But I want all numbers like 1,2,3,...20.
In the code
for y in myiter:
print(next(myiter))
There's no need to call next(), which is causing every other element to be skipped. Just use:
for y in myiter:
print(y)
and next() is called under the hood for you.
A case when you might use next() directly is when you're outside of a loop construct:
>>> from itertools import combinations
>>> it = combinations("abcd", 2)
>>> next(it)
('a', 'b')
>>> next(it)
('a', 'c')
>>> # some time later...
... next(it)
('a', 'd')
>>> # some time later, exhaust the rest:
... list(it)
[('b', 'c'), ('b', 'd'), ('c', 'd')]
For each iteration, the __next__ function prints the next number & then increments it.
This skips the starting number & it's types (odd/ even)
What you need to do is, either print/ return the element from the __iter__ function or increment it within the __iter__ and print it using the current method.

Define function that reads from Lists of lists

Heres a quick example:-
setup = [['dog','red','big','ears'],
['cat','blue','small','tail']]
def do_it(dummy_parameter):
if do_it [0][0] == 'dog':
print 'dog'
elif do_it [0][0] == 'cat'
print 'cat'
do_it(setup)
Basically looking to go through a list of up to four lists, make an action depending on each of the list contents.. It a bit vague but any assistance would be appreciated! :)
Getting the error
TypeError: 'function' object has no attribute '__getitem__'
Here is an example how to get all the values from a list and list of list :)
with recursion :)
test = [['a', 'b', 'c'],['d', 'e'], 'f']
def separate(item):
try:
for i in item:
if type(i) == list:
separate(i)
else:
print(i)
except IndexError:
pass
separate(test)

Python check variable against multiple lists

So I have 3 lists of data, I need to test if any of the data I get from the json response is in any of the lists, I'm probably being stupid about it but I'm trying to learn and can't seem to get it to work right.
list1 = ['a', 'b', 'c']
list2 = ['a1', 'b1', 'c1']
list2 = ['a2', 'b2', 'c2']
#block of code...
#block of code...
content = json.loads(response.read().decode('utf8'))
data = content
for x in data:
#if x['name'] in list1: #This works fine the line below does not.
if x['name'] in (list1, list2, list3):
print("something")
I suggest something simple and straight-forward:
if (x['name'] in list1 or
x['name'] in list2 or
x['name'] in list3):
...
As a pythinc way for such tasks you can use any for simulating the OR operand and all for and operand.
So her you can use a generator expression within any() :
if any(x['name'] in i for i in (list1, list2, list3))
What about concatenating the lists?
if x['name'] in [list1 + list2 + list3]:

Counter class extension

I am having a problem finding an elegant way to create a Counter() class that can:
Feed in arbitrary number of keys and return a nested dictionary based on this list of keys.
Increment for this nested dictionary is arbitrary as well.
For example:
counter = Counter()
for line in fin:
if a:
counter.incr(key1, 1)
else:
counter.incr(key2, key3, 2)
print counter
Ideally I am hoping to get the result looks like: {key1 : 20, {key2 : {key3 : 40}}}. But I am stuck in creating this arbitrary nested dictionary from list of keys. Any help is appreciated.
you can subclass dict and create your own nested structure.
here's my attempt at writing such class :
class Counter(dict):
def incr(self, *args):
if len(args) < 2:
raise TypeError, "incr() takes at least 2 arguments (%d given)" %len(args)
curr = self
keys, count = args[:-1], args[-1]
for depth, key in enumerate(keys, 1):
if depth == len(keys):
curr[key] = curr.setdefault(key, 0) + count
else:
curr = curr.setdefault(key, {})
counter = Counter()
counter.incr('key1', 1)
counter.incr('key2', 'key3', 2)
counter.incr('key1', 7)
print counter #{'key2': {'key3': 2}, 'key1': 8}
There are two possibilities.
First, you can always fake the nested-keys thing by using a flat Counter with a "key path" made of tuples:
counter = Counter()
for line in fin:
if a:
counter.incr((key1,), 1)
else:
counter.incr((key2, key3), 2)
But then you'll need to write a str-replacement—or, better, a wrapper class that implements __str__. And while you're at it, you can easily write an incr wrapper that lets you use exactly the API you wanted:
def incr(self, *args):
super().incr(args[:-1], args[-1])
Alternatively, you can build your own Counter-like class on top of a nested dict. The code for Counter is written in pure Python, and the source is pretty simple and readable.
From, your code, it looks like you don't have any need to access things like counter[key2][key3] anywhere, which means the first is probably going to be simpler and more appropriate.
The only type of value that can exist in a Counter object is an int, you will not be able to represent a nested dictionary with a Counter.
Here is one way to do this with a normal dictionary (counter = {}). First, to update increment the value for a single key:
counter[key1] = counter.setdefault(key1, 0) + 1
Or for an arbitrary list of keys to create the nested structure:
tmp = counter
for key in key_list[:-1]:
tmp = tmp.setdefault(key, {})
tmp[key_list[-1]] = tmp.setdefault(key_list[-1], 0) + 1
I would probably turn this into the following function:
def incr(counter, val, *keys):
tmp = counter
for key in keys[:-1]:
tmp = tmp.setdefault(key, {})
tmp[keys[-1]] = tmp.setdefault(keys[-1], 0) + val
Example:
>>> counter = {}
>>> incr(counter, 1, 'a')
>>> counter
{'a': 1}
>>> incr(counter, 2, 'a')
>>> counter
{'a': 3}
>>> incr(counter, 2, 'b', 'c', 'd')
>>> counter
{'a': 3, 'b': {'c': {'d': 2}}}
>>> incr(counter, 3, 'b', 'c', 'd')
>>> counter
{'a': 3, 'b': {'c': {'d': 5}}}

Resources