How to combine multiple try and except in Python - 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

Related

Can I shorten the process of adding multiple variables in Python?

I have a list and from that list every variable uses one index for a value. Example:
val = [2, 4, 8, 6]
var1 = val[0]
var2 = val[1]
var3 = val[2]
var4 = val[3]
Can I put this into a loop somehow? Because I have 20 values so it is long to write 20 variables.
P.S of course, the values from added variables must be usable. And the format I'm using those variables looks like this:
D = {u'label1': var1, u'label2: var2...}
For your specific issue you could use your dict directly from the list
D = {u'label0' : var[0], u'label1' : val[1],...}
and create the dict as
D = dict(("var{}".format(i),v) for i,v in enumerate(val))
Then, you refer to it as values["var1"] for example, where you can put as key the name you like, label_ for instance.
Try it,
label_dict = {}
for i in range(len(val)):
label_dict['label' + str(i+1)] = val[i]

Python list of global variables not updated

I have few global variables and I have a list. Within a function I am using the list and updating the values as below , but the global variables doesn't seem to be updated.
a = "hello"
b ="how"
c = "are you"
data = ([a,"abc","xyz"],[b,"pqr","mno"],[c,"test","quest"])
def checklist():
global data , a, b, c
for values in data:
values[0] = values[1]
checklist()
print a + ":" + b + ":"+ c
Now when i expect the global variables to be updated which is not happening, I still see old variables, could some one explain how to update global variables from the list.
The loop in data changes data's value, which won't change other variable.
When you run values[0] = values[1], it means values[0] repoints to another object, but a will stays the same.
In [52]: a = '12'
In [53]: li = [a, 'b', 'c']
In [54]: id(li[0])
Out[54]: 140264171560632
In [55]: id(a)
Out[55]: 140264171560632
In [56]: li[0] = 'a'
In [57]: li
Out[57]: ['a', 'b', 'c']
In [58]: a
Out[58]: '12'
In [60]: id(li[0])
Out[60]: 140264267728616
In [61]: id(a)
Out[61]: 140264171560632
You intend the values in data to be changes in the for loop?
The reason that's not happening is that you're changing value, not the actual data.
a,b,c = "hello", "how", "are you"
data = ([a,"abc","xyz"],[b,"pqr","mno"],[c,"test","quest"])
def checklist():
global data , a, b, c
for values in data:
values[0] = values[1]
checklist()
print a + ":" + b + ":"+ c
hello:how:are you
print(data)
(['abc', 'abc', 'xyz'], ['pqr', 'pqr', 'mno'], ['test', 'test', 'quest'])
You are wrong if you want do something like this, edit a variable that you inserted in a list.
values[0] = values[1]
Set in the position 0 of values -> values[1]. But no modify a-b-c!!

Not able to perform for loop in python as expected

Below is the code:
var1 =[]
var5 =[]
var6 = []
var7 = []
var2 = [1, 2, 3, 4, 5,6]
length = len(var2) - 1 # To use it for loop range
var3 = ['a', 'b', 'c', 'd', 'e', 'f']
var4 = ['active','active','active','active','active','active']
for i in (0, length):
with open("listsfile.txt") as f:
for line in f:
if len(line.split(" ")) >= 4: # if strings more than or equal to 4 in line
var5 = line.split(" ")
var5 = filter(None, var5) # filter if any empty element
if len(var5) >=3: # if list has more than or equal to 3 elements
cmp1 = str(var2[i])
cmp2 = str(var5[0])
print cmp1, cmp2
if cmp1 == cmp2: # if first string in a line is matched with list var2 then enter
print "xyz"
var6 = var5[0], var5[1], var5[2]
var7 = var2[i],var3[i],var4[i]
if var6 == var7:
print "Matched"
else:
print "Not Matched"
In above code i am not able to run 6 times in for loop "for i in (0, length):"
Actually I have to run var2 list length times(from 0 it should begin). But it is running only one time.
Please help.
For python 3 "range" seems to do the trick. Also: var5 is now a filter, needs to be converted to list using list().
var1 =[]
var5 =[]
var6 = []
var7 = []
var2 = [1, 2, 3, 4, 5,6]
length = len(var2) - 1 # To use it for loop range
var3 = ['a', 'b', 'c', 'd', 'e', 'f']
var4 = ['active','active','active','active','active','active']
for i in range(0, length):
with open("listsfile.txt") as f:
for line in f:
if len(line.split(" ")) >= 4: # if strings more than or equal to 4 in line
var5 = line.split(" ")
var5 = list(filter(None, var5)) # filter if any empty element
if len(var5) >=3: # if list has more than or equal to 3 elements
cmp1 = str(var2[i])
cmp2 = str(var5[0])
print (cmp1, cmp2)
if cmp1 == cmp2: # if first string in a line is matched with list var2 then enter
print ("xyz")
var6 = var5[0], var5[1], var5[2]
var7 = var2[i],var3[i],var4[i]
if var6 == var7:
print ("Matched")
else:
print ("Not Matched")

Trying all possible combinations in a dynamic order

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

Python list set value at index if index does not exist

Is there a way, lib, or something in python that I can set value in list at an index that does not exist?
Something like runtime index creation at list:
l = []
l[3] = 'foo'
# [None, None, None, 'foo']
And more further, with multi dimensional lists:
l = []
l[0][2] = 'bar'
# [[None, None, 'bar']]
Or with an existing one:
l = [['xx']]
l[0][1] = 'yy'
# [['xx', 'yy']]
There isn't a built-in, but it's easy enough to implement:
class FillList(list):
def __setitem__(self, index, value):
try:
super().__setitem__(index, value)
except IndexError:
for _ in range(index-len(self)+1):
self.append(None)
super().__setitem__(index, value)
Or, if you need to change existing vanilla lists:
def set_list(l, i, v):
try:
l[i] = v
except IndexError:
for _ in range(i-len(l)+1):
l.append(None)
l[i] = v
Not foolproof, but it seems like the easiest way to do this is to initialize a list much larger than you will need, i.e.
l = [None for i in some_large_number]
l[3] = 'foo'
# [None, None, None, 'foo', None, None None ... ]
If you really want the syntax in your question, defaultdict is probably the best way to get it:
from collections import defaultdict
def rec_dd():
return defaultdict(rec_dd)
l = rec_dd()
l[3] = 'foo'
print l
{3: 'foo'}
l = rec_dd()
l[0][2] = 'xx'
l[1][0] = 'yy'
print l
<long output because of defaultdict, but essentially)
{0: {2: 'xx'}, 1: {0: 'yy'}}
It isn't exactly a 'list of lists' but it works more or less like one.
You really need to specify the use case though... the above has some advantages (you can access indices without checking whether they exist first), and some disadvantages - for example, l[2] in a normal dict will return a KeyError, but in defaultdict it just creates a blank defaultdict, adds it, and then returns it.
Other possible implementations to support different syntactic sugars could involve custom classes etc, and will have other tradeoffs.
You cannot create a list with gaps. You could use a dict or this quick little guy:
def set_list(i,v):
l = []
x = 0
while x < i:
l.append(None)
x += 1
l.append(v)
return l
print set_list(3, 'foo')
>>> [None, None, None, 'foo']

Categories