I have a list of dictionaries that is initialised by a call to a function with:
new = {'device_id':name,'device_mac':mac, 'device_ttl':ttl}
dev[0] = new
Thereafter, new entries are appended with:
dev.append(new)
Each dictionary has a time to live (TTL). Once that is reached the dictionary is removed:
for i in dev:
if (i['device_ttl'] == 0):
dev.remove(i)
This all seems fine until the list is completely empty. If I then try and add a new entry with:
dev[0] = new
again, I get a 'list index out of range' error.
I've tried changing the original initialisation with an append to an empty list, but that bombs out immediately with a KeyError: device_id.
The entire function that adds entries is:
# Adds a new device to the list of known devices.
def add_device(dev, num, name, mac, ttl):
new = {'device_id':name,'device_mac':mac, 'device_ttl':ttl}
if (num == 0):
#dev.append(new)
#dev = new
dev[0] = new
else:
dev.append(new)
return (num + 1)
The essential part of the main routine is:
devices = [{}] # Empty list.
num_devices = 0
# Code that determines whether to add to the list or not
num_devices = add_device(devices, num_devices,\
name_long, mac_address, ttl_n)
I don't understand why initialising by appending to an empty list is problematic, nor why what does work to initialise, doesn't work when the list is empty. It is created as empty in the first place!
Is there a better way to initialise or append to an empty list?
The answer was to not declare an empty list of dictionaries ,but just an empty list. A number of respondents pointed out the error but did not elucidate an answer.
Declaring an empty list of dictionaries with:
devices=[{}]
Creates an empty list with an empty dictionary. Assigning the first dictionary with:
dev[0] = new
just replaced the empty dictionary. Hence why the same assignment doesn't work when the list is emptied.
The correct initialisation was to just declare:
devices = []
Adding any dictionary after that, including the first one, is by using an append in the function:
dev.append(new)
Thanks for all of the pointers. I found the answer by switching IDE from Atom to Idle for testing. I have to use Atom as it is the only IDE with the PyCom plugin. Idle flagged the error almost immediately.
Related
Okay so this is just a rough bit of code I made when trying to make a Guess Who(TM) for class challenge and I wanted to make a random character generator function (its only a proof of concept and I would expand it complexity later! Please don't judge!). However the character's template feature list seems to be appended every iteration (and so skewing my other loops) when it aught not to. It should be adding an item to the end of each new generated list - not the template. Yet the template variable is not appended to in the code, only a temporary copy is/should be. Here's the code:
tempfeatures = characters = []
for i in range(len(characternames)):
tempfeatures = []
charactername = characternames[random.randint(0,len(characternames)-1)]
characternames.remove(charactername)
a = features
tempfeatures = a
### "Debug bit" ###
print(features)
print("loooooop")
for y in range(len(features)):
print(len(features))
temp = random.randint(0,1)
if temp == 1:
tempfeatures[y][1] = True
else:
tempfeatures[y][1] = False
tempfeatures.append(["Dead",True])
characters.append([charactername,tempfeatures])
print(characters)
Thank you!
Apparently the tempfeature variable is "call by reference" and not "call by value". - thanks python.
So when duplicating lists, one must use this on the end of the variable name
tempfeature = feature[:]
(the [:] bit)
Thanks all for your comments!
This is called a shallow copy, it just referenciates the list to another variable, as seen here:
https://docs.python.org/2/library/copy.html
You need to make and intependent copy, or a deep copy, as: tempfeature = list(feature) so changing tempfeature won't interfere with feature
Trying to get something to work where I randomize 4 objects in an array and randomly select one of those. I need to be able to get the original index number for that chosen object back. Any idea on how I should write this as short as possible?
arrayRandomSongs = []
arrayChosen = []
trackChosen = ""
def randomizeArray(self):
del self.arrayRandomSongs[:] # wipes array of all contents without making a new one
self.arrayRandomSongs = self.arraySongs[:]
random.shuffle(self.arrayRandomSongs)
def chooseListing(self):
del self.arrayChosen[:] # same here
for i in xrange(4):
self.arrayChosen.append(self.arrayRandomSongs[i])
del self.arrayRandomSongs[0:3]
def chooseTrack(self):
self.trackChosen = random.choice(self.arrayChosen)
As you can see I would like to select the arayChosen index number for the trackChosen object, but since it's randomized I don't see how I could do that.
You will have to keep track of indexes before randomizing. Then access the index value from the tracking list after randomizing and selecting an element from the randomized list.
For getting index of an element in list you can do <list>.index(<element>).
Explanation:
Create a copy of arrayRandomSongs before shuffling its elements.
original_arrayRandomSongs = arrayRandomSongs[:]
After getting the value of trackChosen by doing random.choice, use that value to get its index in original list by doing
original_arrayRandomSongs.index(self.trackChosen)
Well you could do something like this
list = [4,1,3,2]
list_with_indices = [(num,i) for i, num in enumerate(list)]
shuffle(list_with_indices)
essentially you keep track of the original index.
This has taken me over a day of trial and error. I am trying to keep a dictionary of queries and their respective matches in a search. My problem is that there can be one or more matches. My current solution is:
match5[query_site] will already have the first match but if it finds another match it will append it using the code below.
temp5=[] #temporary variable to create array
if isinstance(match5[query_site],list): #check if already a list
temp5.extend(match5[query_site])
temp5.append(match_site)
else:
temp5.append(match5[query_site])
match5[query_site]=temp5 #add new location
That if statement is literally to prevent extend converting my str element into an array of letters. If I try to initialize the first match as a single element array I get None if I try to directly append. I feel like there should be a more pythonic method to achieve this without a temporary variable and conditional statement.
Update: Here is an example of my output when it works
5'flank: ['8_73793824', '6_133347883', '4_167491131', '18_535703', '14_48370386']
3'flank: X_11731384
There's 5 matches for my "5'flank" and only 1 match for my "3'flank".
So what about this:
if query_site not in match5: # here for the first time
match5[query_site] = [match_site]
elif isinstance(match5[query_site], str): # was already here, a single occurrence
match5[query_site] = [match5[query_site], match_site] # make it a list of strings
else: # already a list, so just append
match5[query_site].append(match_site)
I like using setdefault() for cases like this.
temp5 = match5.setdefault(query_site, [])
temp5.append(match_site)
It's sort of like get() in that it returns an existing value if the key exists but you can provide a default value. The difference is that if the key doesn't exist already setdefault inserts the default value into the dict.
This is all you need to do
if query_site not in match5:
match5[query_site] = []
temp5 = match5[query_site]
temp5.append(match_site)
You could also do
temp5 = match5.setdefault(query_site, [])
temp5.append(match_site)
Assuming match5 is a dictionary, what about this:
if query_site not in match5: # first match ever
match5[query_site] = [match_site]
else: # entry already there, just append
match5[query_site].append(temp5)
Make the entries of the dictionary to be always a list, and just append to it.
def mutations (list_a,string1,name,list_b):
""" (list of str, str, list of str, list of str) -> NoneType
"""
dna=list_a
for i in range(len(list_b)):
strand=dna[:dna.index(list_b[i])]
string1=string1[string1.index(list_b[i]):]
dna[strand+string1]
>>>dna=['TGCAGAATTCGGTT','ACGTCCCGGGTTGC']
>>>mutations(dna,'CCCGGGGAATTCTCGC',['EcoRI','SmaI'],['GAATTC','CCCGGG'])
>>>mutated
>>>['TGCAGAATTCTCGC','ACGTCCCGGGGAATTCTCGC']
It's suppose to modify the first parameter. So basically im trying to modify list_a and making it change to ['TGCAGAATTCTCGC','ACGTCCCGGGGAATTCTCGC'] however, i get an error saying
strand=dna[:dna.index(string1[i])].
ValueError: 'GAATTC' is not in list
Also, is there a way if the sequence does not exist, it doesn't modify the function?
Well if I understand you correctly, you want to check each element in list_a if it contains its corresponding element from list_b. If so, you want to modify the element from list_a by replacing the rest of the string (including the list_b element) with part of a control string that does also contain the element from list_b, right?!
Ideally you would put this in your question!!
A way of doing this would be as follow:
def mut(list_a, control, list_b):
check_others = Falsee
for i in range(len(list_a)): # run through list_a (use xrange in python 2.x)
if i == len(list_b): # if we are at the end of list_b we run will
# check all other elements if list_b (see below)
check_others = True
if not check_others: # this is the normal 1 to 1 match.
if list_b[i] in list_a[i]: # if the element from list_b is in it
# correct the element
list_a[i] = list_a[i][:list_a[i].index(list_b[i])] +\
control[control.index(list_b[i]):]
else: # this happens if we are at the end of list_b
for j in xrange(len(list_b)): # run through list_b for the start
if list_b[j] in list_a[i]:
list_a[i] = list_a[i][:list_a[i].index(list_b[j])] +\
control[control.index(list_b[j]):]
break # only the first match with an element in list_b is used!
As described in the comments, dna is a list, not a string, so finding a substring won't quite work how you want.
dna=list_a is unnecessary, and dna[strand+string1] doesn't modify a list, so not sure what you were trying to accomplish there.
All in all, I know the following code doesn't get the output you are expecting (or maybe it does), but hopefully it sets you on the more correct path.
(I removed name because it was not used)
def mutations (mutated,clean,recognition):
""" (list of str, str, list of str) -> NoneType
"""
# Loop over 'mutated' list. We need the index to update the list
for i,strand in enumerate(mutated):
# Loop over 'recognition' list
for rec in recognition:
# Find the indices in the two strings
strand_idx = strand.find(rec)
clean_idx = clean.find(rec)
# Check that 'rec' existed in both strings
if strand_idx > 0 and clean_idx > 0:
# both are found, so get the substrings
strand_str = strand[:strand_idx]
clean_str = clean[clean_idx:]
# debug these values
print(rec, (strand_idx, strand_str,), (clean_idx, clean_str, ))
# updated 'mutated' like this
mutated[i] = strand_str+clean_str
And, the output. (The first dna element changed, the second did not)
dna=['TGCAGAATTCGGTT','ACGTCCCGGGTTGC']
mutations(dna,'CCCGGGGAATTCTCGC',['GAATTC','CCCGGG'])
print(dna) # ['TGCAGAATTCTCGC', 'ACGTCCCGGGTTGC']
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.