Python repeat function that calls functions - python

I have a program where the user should be able to pick and choose commands from a drop down list. In this list, there is also a repeat command, which does basically what a for loop does, so all the commands in between repeat and end repeat should be looped the number of times stated. See picture:
Now, I don't yet know how to programatically handle the repeat-functions. I know that python handles classes like objects, so maybe that can help, but I'm a bit lost.
At the moment I send a list of strings to the thread that handles execution of the commands, and that is parsed and each command is executed.
def command(self, item):
if item.startswith('Pan'):
... do stuff
elif item.startswith('...'):
... do something else
How would I rewrite this so that repeat is a callable function/method ?

Make a function multi_command which takes multiple commands, and executes them in order.
When this function encounters a "repeat", create a list of all the following commands up until you get the corresponding "end repeat". This new list is then a sub-set of your total list. Call multi_command with this list, and afterwards, skip to the command that comes after the "end repeat".
Psuedo-code:
def multi_commands(items):
highest_idx_already_done = 0
for idx, item in enumerate(items):
if highest_idx_already_done > idx:
continue
if item == "repeat":
num_repeats = ...
sub_items = []
for sub_item in items[idx+1:]:
if sub_item == "end repeat":
break
sub_items.append(sub_item[5:]) # Skip indentation
highest_idx_already_done = idx + len(sub_items)
for _ in range(num_repeats):
multi_commands(sub_items)
else:
command(item)

Related

Unable to add to blank list

I am working on a program that can be used to add a given value say a car dealership with the cars it owns:
for example
car_storage = []
def add_cars(dealership, car):
for items in car_storage:
for values in items:
if dealership in items:
#Adds car whether it exists or not in list
items.append(car)
return
#If the dealership does not exist it is created and added to the list
else:
items.append([dealership, car])
return
add_cars("Manchester", "Mini")
add_cars("London", "Toyota")
add_cars("London", "BMW")
add_cars("London", "BMW")
#Desired output
[["Nottingham", "Audi"],["Manchester","Mini"],["London", "Lorry", "BMW", "BMW"]]
However, my code never adds to the list, where am I going wrong?
The issue with your code as #juanpa.arrivillaga said is that the before you add the first item the list car_dealership is empty so nothing in your function executes.
Your approach however is almost correct, Python supports else keyword with for loops. What it means is, if the for loop ran without breaking (no break statements) the else statement is called. For example
for i in range(1, 4):
if i==2:
break
else: # Not executed as there is a break
# if we remove the break the else will run
print("No Break")
Now similarly in your code. you can use the same concept.
def add_cars(dealership, car):
for items in car_storage:
for values in items:
if dealership in items:
#Adds car whether it exists or not in list
items.append(car)
return
#If the dealership does not exist it is created and added to the list
else:
car_storage.append([dealership, car])
Which is very similar to your original code except the indentation level on the else part.
Now in this case since you're returning and not breaking you can even get away with removing the else entirely since the function will never reach there
def add_cars(dealership, car):
for items in car_storage:
for values in items:
if dealership in items:
#Adds car whether it exists or not in list
items.append(car)
return
#If the dealership does not exist it is created and added to the list
car_storage.append([dealership, car])

How to handle swapping variables in a pythonic way?

I often have the case where I use two variables, one of them being the "current" value of something, another one a "newly retrieved" one.
After checking for equality (and a relevant action taken), they are swapped. This is then repeated in a loop.
import time
import random
def get_new():
# the new value is retrieved here, form a service or whatever
vals = [x for x in range(3)]
return random.choice(vals)
current = None
while True:
# get a new value
new = get_new()
if new != current:
print('a change!')
else:
print('no change :(')
current = new
time.sleep(1)
This solution works but I feel that it is a naïve approach and I think I remember (for "write pythonic code" series of talks) that there are better ways.
What is the pythonic way to handle such mechanism?
Really, all you have is a simple iteration over a sequence, and you want to detect changes from one item to the next. First, define an iterator that provides values from get_new:
# Each element is a return value of get_new(), until it returns None.
# You can choose a different sentinel value as necessary.
sequence = iter(get_new, None)
Then, get two copies of the iterator, one to use as a source for current values, the other for new values.
i1, i2 = itertools.tee(sequence)
Throw out the first value from one of the iterators:
next(i2)
Finally, iterate over the two zipped together. Putting it all together:
current_source, new_source = tee(iter(get_new, None))
next(new_source)
for current, new in zip(current_source, new_source):
if new != current:
...
else:
...
time.sleep(1)
Using itertoolz.cons:
current_source, new_source = tee(iter(get_new, None))
for current, new in zip(cons(None, current_source), new_source)):
...

Dictionary Output Issue

I am trying to understand my below code a little better as far as why certain output is occurring.
stuff = {'purple':[0, 106, 506, 'SI', 'Lauren'], 'blue':'Cornflower', 'yo':'green'}
stuff_keys_sorted = sorted(stuff.keys())
print sorted(stuff.keys())
for k in stuff_keys_sorted:
if type(stuff[k]) == type(['hello', 'goodbye']):
for item in stuff[k]:
print item
print k
The current output is:
0
106
506
SI
Lauren
yo
I understand why everything is happening up to the last line of " yo". Why would "yo" be the only option to print out, shouldn't my code just print any key in the dictionary?
You put the print k statement outside of the loop. The for loop is finished by the time Python reaches that statement, and only the last value for k is then printed.
If you wanted to print each key, you'd need to make it part of the loop:
for k in stuff_keys_sorted:
# ...
print k
Some other notes on your code:
You don't have to call .keys(), stuff_keys_sorted = sorted(stuff) is enough to get a sorted sequence of dictionary keys.
To test for a specific type, use isinstance() rather than using type(..) == type(..):
if isinstance(stuff[k], list):
Even if you did need to use type(), you wouldn't need a list with contents; type([]) would have been sufficient. But so would using type(..) is list (as the outcome of type([]) is be list, and there is only one copy of every Python built-in type so using is is going to be a faster test).

Why do these .append() statements overwrite each other?

I have a function as follows:
def control(qstat):
gatnum = int(input("What number of control gates is this control qubit a part of?"))
global qstatnum
qstatnum = {}
qstatnum[gatnum] = []
qstatnum[gatnum].append(qstat) #seems to be a problem
return qstat
However, there is a problem. Let's say I run it once. There will be one item in the list. Then, I run it a second time, with an item distinguishable from the second supposed to be added to the list. When I print qstatnum[gatnum], the list contains only the second item, leading me to believe that the .append() statement is somehow incorrectly written and overwriting any previous additions to the list.
Is this a correct diagnosis? Why would this be? Any help would be appreciated. Thanks!
Each time you call the function, you are creating a new qstatnum dict, so the solution is to create the dictionary outside the function:
qstatnum = {}
def control(qstat):
gatnum = int(input("What number of control gates is this control qubit a part of?"))
try:
qstatnum[qstat].append(gatnum)
except:
qstatnum[qstat] = [gatnum]
return qstat
You need a try: except: block to verify if the key already exists in the dictionary, if it doesn't exists, just add the first value, else use append.
#DanD. approach seems to be shorter, please take a look:
qstatnum = {}
def control(qstat):
gatnum = int(input("What number of control gates is this control qubit a part of?"))
qstatnum.setdefault(qstat, []).append(gatnum)
return qstat
Every time the method is called, qstatnum is set to empty. So basically you are appending to nothing every time.

List comprehension break down, deconstruction and/or disassemble

In web2py I have been trying to break down this list comprehension so I can do what I like with the categories it creates. Any ideas as to what this breaks down to?
def menu_rec(items):
return [(x.title,None,URL('shop', 'category',args=pretty_url(x.id, x.slug)),menu_rec(x.children)) for x in items or []]
In addition the following is what uses it:
response.menu = [(SPAN('Catalog', _class='highlighted'), False, '',
menu_rec(db(db.category).select().as_trees()) )]
So far I've come up with:
def menu_rec(items):
for x in items:
return x.title,None,URL('shop', 'category',args=pretty_url(x.id, x.slug)),menu_rec(x.children))
I've got other variations of this but, every variation only gives me back 1(one) category, when compared to the original that gives me all the categories.
Can anyone see where I'm messing this up at? Any and all help is appreciated, thank you.
A list comprehension builds a list by appending:
def menu_rec(items):
result = []
for x in items or []:
url = URL('shop', 'category', args=pretty_url(x.id, x.slug))
menu = menu_rec(x.children) # recursive call
result.append((x.title, None, url, menu))
return result
I've added two local variables to break up the long line somewhat, and to show how it recursively calls itself.
Your version returned directly out of the for loop, during the first iteration, and never built up a list.
You don't want to do return. Instead append to a list and then return the list:
def menu_rec(items):
result = []
for x in items:
result.append(x.title,None,URL('shop', 'category',args=pretty_url(x.id, x.slug)),menu_rec(x.children)))
return result
If you do return, it will return the value after only the first iteration. Instead, keep adding it to a list and then return that list at the end. This will ensure that your result list only gets returned when all the values have been added instead of just return one value.

Categories