Trying all possible combinations in a dynamic order - python

Basically I'm looking for an implementation of itertools.product that allows me to change the order in which the combinations are generated.
Example: If I use itertools.product('AB', 'xy') it generates the combinations in this exact order:
[('A', 'x'), ('A', 'y'), ('B', 'x'), ('B', 'y')]
I need an implementation that responds to requests like "Please change A to B next", for example like this:
>>> generator = DynamicOrderProduct({'var1': 'AB', 'var2': 'xy'})
>>> generator.first()
{'var1': 'A', 'var2': 'x'}
>>> generator.change('var1')
{'var1': 'B', 'var2': 'x'}
>>> generator.change('var2')
{'var1': 'B', 'var2':, 'y'}
>>> generator.change('var2') # here it can't generate a new combination by
# changing var2, so it changes var1 instead
{'var1': 'A', 'var2': 'y'}
>>> generator.change('var2')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
Ideally, the generator would accept a list of variables like this:
generator.change(['var1', 'var2'])
It should then attempt to change the value of var1, and if that isn't possible, change the value of var2 instead, and so on.
How would I go about implementing this? Is there something in the standard lib that can help me?

Alright, I've managed to write an iterator that does what I want. It's the ugliest piece of code I've ever written, but it gets the job done.
I'm still hoping for a better solution though - this implementation keeps a set of all returned combinations, which can grow to use quite a bit of memory.
class DynamicOrderProduct:
"""
Given a dict of {variable: [value1,value2,value3,...]}, allows iterating
over the cartesian product of all variable values.
Each step in the iteration returns a mapping of {variable: value}.
To start the iteration, retrieve the first mapping by calling .first().
To retrieve subsequent mappings, call
.next(order_in_which_to_change_variable_values). This function's
parameter should be a list of variables sorted by which variable's value
should change next. If possible, the first variable in the list will
change value. If not, the 2nd variable in the list will change value
instead, and so on. Raises StopIteration if all combinations are
exhausted.
Example:
possible_values = {'B': [0,1], # B can take the value 0 or the value 1
'L': [1,2,3]}
iterator = DynamicOrderProduct(possible_values)
print(iterator.first())
import random
variables = list(possible_values.keys())
while True:
order = random.sample(variables, len(variables))
print('advancing variables in this order:', order)
try:
print(iterator.next(order))
except StopIteration:
break
You may also pass an incomplete list of variables to the .next function.
If no new combination of the given variables is possible, StopIteration is
raised. For example:
iterator = DynamicOrderProduct({var1: [1],
var2: [1,2]})
iterator.first() # this returns {var1: 1, var2: 1}
iterator.next([var1]) # raises StopIteration
Also, you may pass multiple lists to .next in order to change the value of
multiple variables. StopIteration will be raised only if no variable can
change value.
iterator = DynamicOrderProduct({var1: [1,2],
var2: [1,2]})
iterator.first() # this returns {var1: 1, var2: 1}
iterator.next([var1], [var2]) # returns {var1: 2, var2: 2}
"""
def __init__(self, possible_variable_values):
self.possible_variable_values = {k:tuple(v) for k,v in \
possible_variable_values.items()}
self.variable_order = list(possible_variable_values)
self.exhausted_combinations = set()
def first(self):
self.mapping = {var:vals[0] for var,vals in \
self.possible_variable_values.items()}
t = tuple(self.mapping[var] for var in self.variable_order)
self.exhausted_combinations.add(t)
return self.mapping
def next(self, *orders):
def advance(order, index, maxindex=2147483648):
while True: # loop to reduce recursion
try:
variable = order[index]
except IndexError:
raise StopIteration
value = self.mapping[variable]
valindex = self.possible_variable_values[variable].index(value)
start_index = valindex
while True: # change the value until we find a new combination
valindex += 1
try:
possible_values = self.possible_variable_values
value = possible_values[variable][valindex]
except IndexError:
valindex = 0
value = self.possible_variable_values[variable][0]
self.mapping[variable] = value
# if we've tried all values but none of them
# worked, try to change the next variable's
# value instead
if valindex == start_index:
if index+1 >= maxindex:
raise StopIteration
# instead of recursing, update our own parameters and
# start a new iteration
index += 1
break
t = tuple(self.mapping[var] for var in self.variable_order)
# if this combination isn't new, try
# changing the previous variables' values
if t in self.exhausted_combinations:
if index == 0:
continue
try:
return advance(order, 0, index)
except StopIteration:
continue
return t
total_order = []
fail = True
for order in orders:
# each iteration may also change the previous
# iterations' variables
total_order = order + total_order
try:
t = advance(total_order, 0)
except StopIteration:
fail = True
else:
fail = False
if fail:
raise StopIteration
self.exhausted_combinations.add(t)
return self.mapping

Related

How to combine multiple try and except in Python

I have a list that will sometimes hold a single value, sometimes 2 or 3 values. I am assigning each of the values to a variable. If there is no value for the assigned position of the list, I want it to be a empty variable. Is there any way I can make this more efficient?
split = line.split(',')
try:
var1 = split[0]
except IndexError:
var1 = None
try:
var2 = split[1]
except IndexError:
var2 = None
try:
var3 = split[2]
except IndexError:
var3 = None
split = line.split(',')
variables = [None, None, None]
for i in range(len(split)):
variables[i] = split[i]
var1, var2, var3 = variables
Edit (after #ekhumoro):
split = line.split(',')
variables = [None, None, None]
variables[:len(split)] = split
var1, var2, var3 = variables
You can use generator expression (or list comprehension) with conditional check to see if the index exists in the list. If exists, return element at that index, else return None. Here I am limiting my generator expression to return n (which is 3 in your case) values, and you can unpack and save these values in n variables.
For example:
my_list = [1] # List with just one element
n = 3 # Number of variables in which you want to fill the values
var1, var2, var3 = (my_list[i] if i < len(my_list) else None for i in range(n))
print(var1, var2, var3)
# prints: (1, None, None)
Refer Generator Expressions and List Comprehensions for more details.
Depending on what you're actually trying to accomplish*, it might be better to not use individual variables. For example, you could use dict.get(), which returns None if the given key is not in the dict.
for line in 'a,b,c', 'd,e', 'f':
split = line.split(',')
d = {i: v for i, v in enumerate(split)}
print(d, ' -> ', d.get(0), d.get(1), d.get(2))
Output:
{0: 'a', 1: 'b', 2: 'c'} -> a b c
{0: 'd', 1: 'e'} -> d e None
{0: 'f'} -> f None None
You could also consider using unpacking:
for line in 'a,b,c', 'd,e', 'f':
var1, *others = line.split(',')
print(var1, others)
Output:
a ['b', 'c']
d ['e']
f []
By the way, note that split[0] will always exist, so I'm not sure why you're expecting an IndexError. For example:
>>> ''.split(',')
['']
* See some related ideas at How do I create variable variables? and more broadly, how to avoid the XY problem

NameError: name 'd' is not defined in param_check

I'm trying to solve the following sets of coding challenges, but generate one error after another.
def checkingIfIn(a, direction=True,
d={'apple': 2, 'pear': 1, 'fruit': 19, 'orange': 5,
'banana': 3, 'grapes': 2, 'watermelon': 7}):
if direction == True:
if a in d is True:
return d[a]
else:
return False
else:
if a not in d:
return True
else:
return d[a]
Call the function so that it returns False and assign that function
call to the variable c_false
Call the fucntion so that it returns True and assign it to the
variable c_true
Call the function so that the value of fruit is assigned to the
variable fruit_ans
Call the function using the first and third parameter so that the
value 8 is assigned to the variable param_check
c_false = checkingIfIn('wwww',direction=True)
c_true = checkingIfIn('bbbbb',direction=False)
fruit_ans = checkingIfIn('fruit',True)
param_check = checkingIfIn('apple',True,(d(['pear']['watermelon'])))
print(c_false)
print(c_true)
print(fruit_ans)
print(param_check)
You're mixing stuff up.
1)
First off, the method is not d.keys[a], it's d.keys()
if a in d.keys()
2)
In your else case you're also being incoherent because you're writing
if a not in d
when it should be
if a not in d.keys()
3)
for fruit_ans you're assigning d='apple'. Now d is a string, not a dictionary, so of course it won't work (string don't have keys)
You're actually doing something useless by even passing something to d. You should be passing just
checkingIfIn('apple',True)
And this will check if key apple is contained in your dictionary d.
Otherwise you need to pass a proper dictionary to d
checkingIfIn('apple',True, d={'this':1,'is':2,'aProper':3,'dictionary':4})
4)
You're assigning an integer to d for the param_check. Which is the same error you made in point 3, passing something that is not a dictionary to d
5) Your comments for each function call are quite hard to figure out. It's not clear what you even want to achieve with this method but it's clearly in dire need to be reworked
EDIT:
This is what you get by using your method properly
if __name__ == "__main__":
#Call the function so that it returns False and
#assign that function call to the variable c_false
c_false = checkingIfIn('apple',direction=False)
#Call the fucntion so that it returns True and assign it to the
#variable c_true
c_true = checkingIfIn('apple',direction=True)
#Call the function so that the value of fruit is assigned to the
#variable fruit_ans
fruit_ans = checkingIfIn('apple',True,d={'apple':1})
#Call the function using the first and third parameter so that the
#value 8 is assigned to the variable param_check
param_check = checkingIfIn('apple',direction=True)
print(c_false)
print(c_true)
print(fruit_ans)
print(param_check)
And the output is as follows
2
apple
apple
apple
Which clearly isn't what you're trying to get, but again, your code and your comments aren't exactly easy to understand
def checkingIfIn(a, direction = True, d= {'apple': 2, 'pear': 1, 'fruit': 19, 'orange': 5, 'banana': 3, 'grapes': 2, 'watermelon': 7}):
if direction == True:
if a in d:
return d[a]
else:
return False
else:
if a not in d:
return True
else:
return d[a]
Call the function so that it returns False and assign that function call to the variable c_false
Call the fucntion so that it returns True and assign it to the variable c_true
Call the function so that the value of fruit is assigned to the variable fruit_ans
Call the function using the first and third parameter so that the value 8 is assigned to the variable param_check
c_false = checkingIfIn('wwww',direction=True)
c_true = checkingIfIn('bbbbb',direction=False)
fruit_ans = checkingIfIn('fruit',True)
param_check = checkingIfIn('apple',True,('pear')+('watermelon'))
print(c_false)
print(c_true)
print(fruit_ans)
print(param_check)

Keeping the order of an OrderedDict

I have an OrderedDict that I'm passing to a function. Somewhere in the function it changes the ordering, though I'm not sure why and am trying to debug it. Here is an example of the function and the function and output:
def unnest_data(data):
path_prefix = ''
UNNESTED = OrderedDict()
list_of_subdata = [(data, ''),] # (data, prefix)
while list_of_subdata:
for subdata, path_prefix in list_of_subdata:
for key, value in subdata.items():
path = (path_prefix + '.' + key).lstrip('.').replace('.[', '[')
if not (isinstance(value, (list, dict))):
UNNESTED[path] = value
elif isinstance(value, dict):
list_of_subdata.append((value, path))
elif isinstance(value, list):
list_of_subdata.extend([(_, path) for _ in value])
list_of_subdata.remove((subdata, path_prefix))
if not list_of_subdata: break
return UNNESTED
Then, if I call it:
from collections import OrderedDict
data = OrderedDict([('Item', OrderedDict([('[#ID]', '288917'), ('Main', OrderedDict([('Platform', 'iTunes'), ('PlatformID', '353736518')])), ('Genres', OrderedDict([('Genre', [OrderedDict([('[#FacebookID]', '6003161475030'), ('Value', 'Comedy')]), OrderedDict([('[#FacebookID]', '6003172932634'), ('Value', 'TV-Show')])])]))]))])
unnest_data(data)
I get an OrderedDict that doesn't match the ordering of my original one:
OrderedDict([('Item[#ID]', '288917'), ('Item.Genres.Genre[#FacebookID]', ['6003172932634', '6003161475030']), ('Item.Genres.Genre.Value', ['TV-Show', 'Comedy']), ('Item.Main.Platform', 'iTunes'), ('Item.Main.PlatformID', '353736518')])
Notice how it has "Genre" before "PlatformID", which is not the way it was sorted in the original dict. What seems to be my error here and how would I fix it?
It’s hard to say exactly what’s wrong without a complete working example. But based on the code you’ve shown, I suspect your problem isn’t with OrderedDict at all, but rather that you’re modifying list_of_subdata while iterating through it, which will result in items being unexpectedly skipped.
>>> a = [1, 2, 3, 4, 5, 6, 7]
>>> for x in a:
... print(x)
... a.remove(x)
...
1
3
5
7
Given your use, consider a deque instead of a list.

Simplifying `if...elif..else` conditions

If I have multiple conditions (nested and/or otherwise) with boolean (either False or True) outputs; how could I further simplify the code and make it more efficient, comprehensive, and elegant?
For example, under such circumstances as follows:
if condition_1:
if condition_2:
# do one thing
pass
elif condition_3:
# do another thing
pass
else:
# do a third thing
pass
elif condition_2:
if condition_3:
# do a fourth thing
pass
and so on.
It's a exam project of mine, so not get too much help, I'll try and explain what my code should do.
I basically want to go through a dataset, looking for different things. Lets say its a dictionary, like this:
myDict = {'a': ['b', 'c'], 'b': ['c', 'd']}
If I go through the dict:
for item, element in myDict.items():
for letter in element:
if letter == 'd':
dflag = True
if letter == 'c':
cflag = True
if cflag:
if dflag:
print('cd!')
else:
print('only c')
Using 'if', 'elif', and 'else' is not bad if it is done efficiently. But in general, the answer to your question would really depend upon individual circumstances.
Having said that, however, one way to do it would be to put your conditions inside a dict (as highlighted by yourself in the tags).
Here are a few examples:
As a dict:
conditions = {
1: 'One',
2: 'Two',
3: 'Three',
4: 'Four',
5: lambda x: x**2 # Can be substituted with actual functions defined elsewhere.
}
x = 3
if x in conditions.keys():
print(conditions[x])
returns:
Three
or in the case of a function:
x = 5
if x in conditions.keys():
func = conditions[x]
print(func(x))
returns:
25
Using a function to resemble switch...case:
To make it even clearer, and have something like a switch...case statement, you can do this:
def switch(case):
conditions = {
1: 'One',
2: 'Two',
3: 'Three',
4: 'Four',
5: lambda x: x**2
}
return condition[case] if case in conditions else False
It is ran like so:
>>> print(switch(2))
2
or for a non-existent items:
>>> print(switch(6))
False
Implementation on your example:
switch...case function decorator (wrapper)
So to address the example you have added, we can do as follows:
First we need a general switch/case decorator:
def switch_case(switch):
def switcher(func):
def case(case):
return switch[case](case) if case in switch else None
return case
return switcher
Then we need a dictionary of our conditions, which are the one given in your example:
# Define the conditions in a dict.
conditions = {
'd': lambda x: True if 'd' else False, # You can say: True if 'a' or 'b' else False
'c': lambda x: True if 'c' else False
}
Now we Create a decorated switch-case function based on your conditions:
#switch_case(conditions)
def my_conditions(case):
return case
Then we specify the elements, or read them from a file, database or anything:
# Elements to be tested as string.
# It can be any flattened (not nested) iterable (str, tuple, list, numpy.array, etc.)
myDict = {'a': ['b', 'c'], 'b': ['c', 'd']}
elements = sum(myDict.values(), []) # Returns a flattened lists of values.
Evaluate the elements based on the conditions (generator object).
verdicts = map(my_conditions, elements)
Match the elements to the corresponding evaluation results (generator object).
status = zip(elements, verdicts)
Now we can positively regulate the outputs (discard None vlues) and create a dict in which the keys are the elements, and the values are the status of their conditions.
passed = {key+'flag': val for key, val in status if val is not None}
print(passed)
# output: {'cflag': True, 'dflag': True}
Add as variables to the namespace
At this point, you can use the dict as is; however, if you insist on adding it to the namespace, here is how:
# Rename values and add them to the name space.
locals().update(passed)
Test
Finally, let's test and ensure the values exist in the local namespace (notice that we haven't implemented any of these names before). So, if the condition returned a True value for the particular character in the sequence, a variable would have been created:
>>> print(dflag) # We had 'd' in `myDict` values.
True
On the other had, if the condition returned None, there will be no value in the namespace.
>>> print(aflag) # We didn't have 'a' in `myDict` values.
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-25-26f7e9594747> in <module>()
24
---> 25 print(aflag)
NameError: name 'aflag' is not defined
Note: Under the existing structure, if the condition returns False, a variable will be created in the namespace and assigned a value of False.
Hope this helps.
Your code here about as simple as you can get.
The one way you can condense is by changing the last branch from:
elif flag2:
if flag3:
do a fourth thing
to
elif flag2 and flag3:
do a fourth thing
Have you considered refactoring? A well written function should do one thing, the fact you have three flags indicates this block of code is going to be doing AT LEAST 3 things, which brings in a lot of headaches for testing, code readability etc.
Are you sure this couldn't be refactored into three or more methods, check for the flags at the beginning and launch the corresponding methods.
You can use an iterator over flags. A dictionary could work too, depending on what your flags are, but it wouldn't really help if your flags are something like x==1, y==10, z=='a' that could all evaluate to a True or False only (because the keys can only be unique). If your flags are in the form of a == b you'd probably have to go with some sort of iterator.
def f(): print('f')
def g(): print('g')
def h(): print('h')
y = 3
x = 2
flags = [
# Outer condition
(y==1, (
# Inner conditions and actions
(x==1, f), (x==2, g), (x==3, h)
)),
(y==3, (
(x==1, g), (x==2, f), (x==3, h)
))
]
# Using next ensures that the rest of the flags/actions aren't even evaluated,
# the same way an if/elif would work
_, inner_flags_and_actions = next(
(flag, inner) for (flag, inner) in flags if flag
)
_, action = next(
(flag, action) for (flag, action) in inner_flags_and_actions if flag
)
# By now you have reached the action you want to take.
action()
This prints: f
You can use a dict :
d = { (False, False, False) : f1,
(False, False, True) : f2,
(False, True, False) : f3,
(False, True, True) : f4
...
Then call d[(flag1,flag2,flag3)]()
This is if you need an industrial amount of if/elses, otherwise just try to make the right simplifications.
Of course, if you are testing agains the same entry variable, or applying the same function with different parameters as output, then you can simplify even more by replacing booleans and functions with actual data.

Is it posible to return on the line with exception?

I have a code which looks like this:
try:
a = form['a']
except KeyError:
pass
try:
b = form['b']
except KeyError:
pass
try:
c = form['c']
except KeyError:
pass
try:
d = form['d']
except KeyError:
pass
Is it posible to do something like this:
try:
a = form['a']
b = form['b']
c = form['c']
d = form['d']
except KeyError:
somekeyword
So if there is no key 'a' in form still working and try to find key 'b' and so on
UPD:
Sorry, my bad. I need to preserve all values not just one of them.
So I need the keyword or something like that that will help me continue try block even after exception raises.
If form is a dictionary you may also use .get(key, [default]) syntax:
>>> form = {'d':5}
>>> form.get('a', form.get('b', form.get('c', form['d'])))
5
You can make the function calls lazy by using some iterators:
>>> from itertools import dropwhile
>>> it = (form.get(k) for k in ['a', 'b', 'c', 'd'])
>>> next(dropwhile(lambda val: val is None, it))
5
edit: if you need all four:
>>> a, b, c, d = map(form.get, ['a', 'b', 'c', 'd'])
>>> a, b, c, d
(None, None, None, 5)
You could simply do this, assuming form supports full dict semantics, and that None is actually a valid value of the form:
sentinel = object()
user = sentinel
for key in "abcd":
user = form.get(key, user)
if user is sentinel: # I don't know if None is a va
whatever-you-mean-with-somekeyword

Categories