Declaring multiple variables using the same function in Python - python

Long-hand this is how it would look like:
class TestClass(object):
def f(num):
"""In general, a complicated function."""
return num
self.a = f(1)
self.b = f(2)
self.c = f(3)
self.d = f(4)
self.e = f(5)
I'm thinking dictionary methods could help, but how?

As you said you better to use a dictionary.And as a more pythonic way you can use a dictionary comprehension.You can use enumerate to create a sequence of keys for your dictionary based on your items index. :
>>> my_dict = {'a{}'.format(i):f(j) for i,j in enumerate([3,4,5,1,2])}
{'a1': 4, 'a0': 3, 'a3': 1, 'a2': 5, 'a4': 2}
And for accessing to each value you can use a simple indexing :
>>> my_dict['a3']
1
Also if you want to use custom names for your keys you can use zip function to zip the variable names with values the use if within a dict comprehension:
>>> var_names=['a','b','c','d','e']
>>> values=[1,2,3,4,5]
>>>
>>> my_dict = {i:f(j) for i,j in zip(var_names,values)}
>>> my_dict
{'a': 1, 'c': 3, 'b': 2, 'e': 5, 'd': 4}

You're going in the wrong direction - if you want to assign several references based on the same function, you should be storing them in a data structure like a list instead of in discrete, manually-entered variables. You can unpack them like that later if you want, but you should start with a data structure. It then becomes easier to map() each value in an iterable to this function, and then turn it into a list.
def f(num):
"""In general, a complicated function."""
return num
my_numbers = list(map(f, range(1, 6)))
Your numbers were a tidy range this time so I just used a range() object, but you can use any iterable you like, such as [1, 2, 3] or (4, 2, 3).

Related

Extending a dictionary by adding to tupled value

(I'm just using things to represent the format my dictionary is like, this is not how it actually looks)
The current dictionary I have is in the format of :
dict={"a":(1,2,3),"b":(2,3,4),"c":(3,4,5)}
dict2={1:(1,1,2),2:(2,2,3),3:(3,3,4)}
I am trying to add on to it. I have a function that calculated an average and now I want to create a second function that would pull that average from the first function and add it to the tuple in the values of my dictionary. So if the average was 4 for "a", then I would want to add it on for it to look like this {"a":(1,2,3,4)} etc.
def func1(dict, dict2, number):
if number in dict:
for num in dict.values():
#return sum after iteration of sum through num[0] to num[2]
def func 2 (dict1, dict2)
if number in dict:
new_val_to_add= func1(dict, dict2, number)
From here I'm not sure where I would go with adding the returned value to the tuple and be able to print back dict with the added value. I was thinking of maybe converting the tuple in the first dictionary into a list and then append to that as I iterate through each key in the dictionary, and finally convert it back into a tuple. Would this be the right way of going about this?
If you want the values to be mutable, why don't you use lists instead of tuples?
You can combine two tuples using +, though:
>>> (1, 2, 3) + (4,)
(1, 2, 3, 4)
I'm having trouble following your example code, but with a dictionary following your description you can do it like this:
>>> d1 = {'a': (1, 2, 3)}
>>> d1['a'] += (4,)
>>> d1
{'a': (1, 2, 3, 4)}
Or, using a list instead:
>>> d1 = {'a': [1, 2, 3]}
>>> d1['a'].append(4)
>>> d1
{'a': [1, 2, 3, 4]}

Extending list copy in one line

When I execute the following:
a = {'a': 1, 'b':3, 'c': 5}
b = [2,4,6]
c = list(b)
c.extend(a.values())
print c
It prints out [2, 4, 6, 1, 5, 3] as I expected, but when I try to do the list copy and extension in one line:
a = {'a': 1, 'b':3, 'c': 5}
b = [2,4,6]
d = list(b).extend(a.values())
print d
It prints None. Why are these different?
Because list.extend() does not return the modified list but operate on the list itself.
I guess you may want to reuse d. If you want to create a new d to hold the result after extending and do it in one line, try:
a = {'a': 1, 'b':3, 'c': 5}
b = [2,4,6]
d = list(b) + list(a.values())
print d
Two points to note:
dictionary.value() returns a view object but not a plain list
values / keys in a default Python dictionary is randomly ordered
What you assign to d variable is the result of the list(b).extend function. This method does not return anything - it extends existing mutable object. list(b) is modified but since you did not save it anywhere the statement won't have any effect.
You don't even need to make the variable d. extend() will extend the list in place so just use this:
a = {'a': 1, 'b':3, 'c': 5}
b = [2,4,6]
b.extend(a.values())
print b
This will give you the desired output: [2, 4, 6, 5, 3, 1]
EDIT:
According to the OP he wanted b to remain unchanged. All you need to do is make a copy of b and then extend that. You can do that like this:
a = {'a': 1, 'b':3, 'c': 5}
b = [2,4,6]
c = b[:]
c.extend(a.values())
print c

Python 2.7.9 dictionary find and delete

Python 2.7.9 dictionary question:
I have a dictionary in Python that contain lists that have been appended previously, and these lists are mapped, e.g. 1=>10.2, 2=>10.33
How may I find a single value within the dictionary and delete it?
E.g. find 'a'=2 and delete 'a' and corresponding 'b' value:
myDictBefore = {'a': [1, 2, 3], 'b': [10.2, 10.33, 10.05]}
myDictAfter = {'a': [1, 3], 'b': [10.2, 10.05]}
I suspect I should find 'a' value and get the index and then
delete myDict['a'][index]
and myDict['b'][index] - though I'm unsure how to do this.
How about:
idx = myDictBefore['a'].index(2)
myDictBefore['a'].pop(idx)
myDictBefore['b'].pop(idx)
If this comes up more often, you might as well write a general function for it:
def removeRow(dct, col, val):
'''remove a "row" from a table-like dictionary containing lists,
where the value of that row in a given column is equal to some
value'''
idx = dct[col].index(val)
for key in dct:
dct[key].pop(idx)
which you could then use like this:
removeRow(myDictBefore, 'a', 2)
You could define a function that does it.
def remove(d, x):
index = d['a'].index(x) # will raise ValueError if x is not in 'a' list
del d['a'][index]
del d['b'][index]
myDict = {'a': [1, 2, 3], 'b': [10.2, 10.33, 10.05]}
remove(myDict, 2)
print(myDict) # --> {'a': [1, 3], 'b': [10.2, 10.05]}

how to use a sentinel list in a comprehension?

I have a list
In [4]: a = [1, 2, 3, 3, 2, 4]
from which I would like to remove duplicates via a comprehension using a sentinel list (see below why):
In [8]: [x if x not in seen else seen.append(x) for x in a]
Out[8]: [1, 2, 3, 3, 2, 4]
It seems that seen is not taken into account (neither updated, not checked). Why is it so?
As for the reason why using a convoluted method: The list I have is of the form
[{'a': 3, 'b': 4}, {'a': 10, 'b': 4}, {'a': 5, 'b': 5}]
and I want to remove duplicates based on the value of a specific key (b in the case above, to leave [{'a': 3, 'b': 4}, {'a': 5, 'b': 5}] (I do not care which dict is removed). The idea would be to build a sentinel list with the values of b and keep only the dicts without b equal to any element in that sentinel list.
Since x is not in seen, you are never adding it to seen either; the else branch is not executed when x not in seen is true.
However, you are using a conditional expression; it always produces a value; either x or the result of seen.append() (which is None), so you are not filtering, you are mapping here.
If you wanted to filter, move the test to an if section after the for loop:
seen = set()
[x for x in a if not (x in seen or seen.add(x))]
Since you were using seen.append() I presume you were using a list; I switched you to a set() instead, as membership tests are way faster using a set.
So x is excluded only if a) x in seen is true (so we have already seen it), or seen.append(x) returned a true value (None is not true). Yes, this works, if only a little convoluted.
Demo:
>>> a = [1, 2, 3, 3, 2, 4]
>>> seen = set()
>>> [x for x in a if not (x in seen or seen.add(x))]
[1, 2, 3, 4]
>>> seen
set([1, 2, 3, 4])
Applying this to your specific problem:
>>> a = [{'a': 3, 'b': 4}, {'a': 10, 'b': 4}, {'a': 5, 'b': 5}]
>>> seen = set()
>>> [entry for entry in a if not (entry['b'] in seen or seen.add(entry['b']))]
[{'a': 3, 'b': 4}, {'a': 5, 'b': 5}]
You never execute the else part of the if, because you do not update when you match the first time. You could do this:
[seen.append(x) or x for x in lst if x not in seen]
This way the or returns the last value (and executes the update using append (which always returns None, to let the or continue looking for truth-y value).
Maybe you can use the fact that dict keys are a set for this. If you want to prioritize the last items use reversed (last item is prioritized here):
>>> lst = [{'a': 3, 'b': 4}, {'a': 10, 'b': 4}, {'a': 5, 'b': 5}]
>>> filtered = {item['b']: item for item in reversed(lst)}
>>> filtered.values()
[{'a': 3, 'b': 4}, {'a': 5, 'b': 5}]
This uses 'b' as the key to map a value to, so only a single elemnt can be mapped to a value of 'b', which effectively creates a set over 'b'.
note: this will return the values in random order. To fix it nicely, for big datasets, I'd create another mapping, of each object to it's index in the original list (O(n)), and use that mapping as a sorting function of the final result (O(n*log(n))). That's beyond the scope of this answer.
I'm always queasy making use of operator precedence as execution flow control. I feel that the below is marginally more explicit and palatable, although it does carry the additional cost of tuple creation.
b_values = set()
[(item, b_values.add(item['b']))[0] for item in original_list
if item['b'] not in b_values]
But really when you're maintaining/updating some sort of state, I think the best format is the simple for-loop:
output_list = []
b_values = set()
for item in original_list:
if item['b'] not in b_values:
output_list.append(item)
b_values.add(item['b'])

Flatten a dictionary of dictionaries (2 levels deep) of lists

I'm trying to wrap my brain around this but it's not flexible enough.
In my Python script I have a dictionary of dictionaries of lists. (Actually it gets a little deeper but that level is not involved in this question.) I want to flatten all this into one long list, throwing away all the dictionary keys.
Thus I want to transform
{1: {'a': [1, 2, 3], 'b': [0]},
2: {'c': [4, 5, 1], 'd': [3, 8]}}
to
[1, 2, 3, 0, 4, 5, 1, 3, 8]
I could probably set up a map-reduce to iterate over items of the outer dictionary to build a sublist from each subdictionary and then concatenate all the sublists together.
But that seems inefficient for large data sets, because of the intermediate data structures (sublists) that will get thrown away. Is there a way to do it in one pass?
Barring that, I would be happy to accept a two-level implementation that works... my map-reduce is rusty!
Update:
For those who are interested, below is the code I ended up using.
Note that although I asked above for a list as output, what I really needed was a sorted list; i.e. the output of the flattening could be any iterable that can be sorted.
def genSessions(d):
"""Given the ipDict, return an iterator that provides all the sessions,
one by one, converted to tuples."""
for uaDict in d.itervalues():
for sessions in uaDict.itervalues():
for session in sessions:
yield tuple(session)
...
# Flatten dict of dicts of lists of sessions into a list of sessions.
# Sort that list by start time
sessionsByStartTime = sorted(genSessions(ipDict), key=operator.itemgetter(0))
# Then make another copy sorted by end time.
sessionsByEndTime = sorted(sessionsByStartTime, key=operator.itemgetter(1))
Thanks again to all who helped.
[Update: replaced nthGetter() with operator.itemgetter(), thanks to #intuited.]
I hope you realize that any order you see in a dict is accidental -- it's there only because, when shown on screen, some order has to be picked, but there's absolutely no guarantee.
Net of ordering issues among the various sublists getting catenated,
[x for d in thedict.itervalues()
for alist in d.itervalues()
for x in alist]
does what you want without any inefficiency nor intermediate lists.
edit: re-read the original question and reworked answer to assume that all non-dictionaries are lists to be flattened.
In cases where you're not sure how far down the dictionaries go, you would want to use a recursive function. #Arrieta has already posted a function that recursively builds a list of non-dictionary values.
This one is a generator that yields successive non-dictionary values in the dictionary tree:
def flatten(d):
"""Recursively flatten dictionary values in `d`.
>>> hat = {'cat': ['images/cat-in-the-hat.png'],
... 'fish': {'colours': {'red': [0xFF0000], 'blue': [0x0000FF]},
... 'numbers': {'one': [1], 'two': [2]}},
... 'food': {'eggs': {'green': [0x00FF00]},
... 'ham': ['lean', 'medium', 'fat']}}
>>> set_of_values = set(flatten(hat))
>>> sorted(set_of_values)
[1, 2, 255, 65280, 16711680, 'fat', 'images/cat-in-the-hat.png', 'lean', 'medium']
"""
try:
for v in d.itervalues():
for nested_v in flatten(v):
yield nested_v
except AttributeError:
for list_v in d:
yield list_v
The doctest passes the resulting iterator to the set function. This is likely to be what you want, since, as Mr. Martelli points out, there's no intrinsic order to the values of a dictionary, and therefore no reason to keep track of the order in which they were found.
You may want to keep track of the number of occurrences of each value; this information will be lost if you pass the iterator to set. If you want to track that, just pass the result of flatten(hat) to some other function instead of set. Under Python 2.7, that other function could be collections.Counter. For compatibility with less-evolved pythons, you can write your own function or (with some loss of efficiency) combine sorted with itertools.groupby.
A recursive function may work:
def flat(d, out=[]):
for val in d.values():
if isinstance(val, dict):
flat(d, out)
else:
out+= val
If you try it with :
>>> d = {1: {'a': [1, 2, 3], 'b': [0]}, 2: {'c': [4, 5, 6], 'd': [3, 8]}}
>>> out = []
>>> flat(d, out)
>>> print out
[1, 2, 3, 0, 4, 5, 6, 3, 8]
Notice that dictionaries have no order, so the list is in random order.
You can also return out (at the end of the loop) and don't call the function with a list argument.
def flat(d, out=[]):
for val in d.values():
if isinstance(val, dict):
flat(d, out)
else:
out+= val
return out
call as:
my_list = flat(d)

Categories