Enigmatic Naming of Lists in Python - python

I am writing a program in python and I am using a dictionary. I need to create an empty list for each key in the dictionary, and each list needs to have the same name as the key. The keys are user entered, which is why I can't just make a list in the conventional way.
This was the first thing I tried
a = {"x":1, "y":2, "z"3}
list(a.keys())
a.pop() = [] # This raises an error
I also tried to make the lists when the dictionary was being created, but this did not work either:
a = {}
b = input("INPUT1")
c = input("INPUT2")
a[b] = c
b = []
This created a list with the name "b" rather than whatever the user entered. Please help!!

This really doesn't make much sense. Why do your lists need 'names'? For that matter, what do you mean by 'names'?
Secondly, your code doesn't do anything. a.keys() is already a list. Calling list on it doesn't do anything else. But in any case, whatever the result of that line is, it is immediately thrown away, as you don't store the result anywhere.
pop doesn't work on dictionaries, it works on lists. And what does it mean to set the result of pop to a list? Are you trying to dynamically create a set of local variables with the names of each key in the dictionary? If so, why? Why not simply create another dictionary with the keys of the first, and each value as a new list? That can be done in one command:
b = dict((k, []) for k in b.keys())
(Note that dict.fromkeys() won't work here, as that would cause each element to share the same list.)

Use raw_input instead. input expects a valid Python expression and WILL evaluate whatever the user inputs. So if the user inputs a word, it'll evaluate that word and try to find an object with its name.
EDIT: for further clarification, input("INPUT1") is the equivalent of doing eval(raw_input("INPUT1")), for instance.

Related

Appending to multiple lists in a function - python

I have code that gets information from iTunes based on a search term that returns a list of dictionaries with various media types in it. I would like to group this information by whether or not it is a movie, song, or other media type. I want to do this within a function for later use, and was thinking the best way to do this would be to append each dictionary to a list based on what type it is. I am getting empty lists, even though the input I am putting in the function is a list of 100 dictionaries. Is there a better way to do this? This is my code -
def make_object(list_from_req):
movie_lst = []
song_lst = []
media_other_lst = []
for d in list_from_req:
if d['kind'] == 'song':
song_lst.append
elif d['kind'] == 'feature-movie':
movie_lst.append
else:
media_other_lst.append
return movie_lst, song_lst, media_other_lst
movie_lst.append by itself doesn’t do anything: it refers to the idea of adding to that particular list (which is sometimes very useful!) but it doesn’t use it. You have to pass arguments with () to call a function (even if there are 0 arguments).

Creating empty lists with the name of the elements of another list

Say we have a list as my_list=["a","b","c"]. What I want to do is to create empty lists as
a=[]
b=[]
c=[]
so that I can append some elements into them according to their names.
Programmatically making variables is a very bad idea. Make a dictionary instead with those names as keys:
my_lists = {key:[] for key in my_list}
Then you can append to them like this:
my_lists['a'].append(some_data)
This also gives you the advantage of easily being able to loop through them if you need to.
Here you go, it uses the function exec to execute the command as if it were typed by you but you use i as a dynamic variable name
for i in my_list:
exec(i+'=[]')
Keep in mind this isn't very safe to do

How can I initialize and increment an undefined value within a list in Python 3?

What I have is a dictionary of words and I'm generating objects that contain
(1) Original word (e.g. cats)
(2) Alphabetized word (e.g. acst)
(3) Length of the word
Without knowing the length of the longest word, is it possible to create an array (or, in Python, a list) such that, as I scan through the dictionary, it will append an object with x chars into a list in array[x]?
For example, when I encounter the word "a", it will append the generated object to the list at array[1]. Next, for aardvark, if will append the generated object to the list at array[8], etc.
I thought about creating an array of size 1 and then adding on to it, but I'm not sure how it would work.
Foe example: for the first word, a, it will append it to the list stored in array[1]. However, for next word, aardvark, how am I supposed to check/generate more spots in the list until it hits 8? If I append to array, I need give the append function an arg. But, I can't give it just any arg since I don't want to change previously entered values (e.g. 'a' in array[1]).
I'm trying to optimize my code for an assignment, so the alternative is going through the list a second time after I've determined the longest word. However, I think it would be better to do it as I alphabetize the words and create the objects such that I don't have to go through the lengthy dictionary twice.
Also, quick question about syntax: listOfStuff[x].append(y) will initialize/append to the list within listOfStuff at the value x with the value y, correct?
Store the lengths as keys in a dict rather than as indexes in a list. This is really easy if you use a defaultdict from the collections module - your algorithm will look like this:
from collections import defaultdict
results = defaultdict(list)
for word in words:
results[len(word)].append(word)
This ties in to your second question: listOfStuff[x].append(y) will append to a list that already exists at listofStuff[x]. It will not create a new one if that hasn't already been initialised to a (possibly empty) list. If x isn't a valid index to the list (eg, x=3 into a listOfStuff length 2), you'll get an IndexError. If it exists but there is something other than another list there, you will probably get an AttributeError.
Using a dict takes care of the first problem for you - assigning to a non-existent dict key is always valid. Using a defaultdict extends this idea to also reading from a non-existent key - it will insert a default value given by calling the function you give the defaultdict when you create it (in this case, we gave it list, so it calls it and gets an empty list) into the dict the first time you use it.
If you can't use collections for some reason, the next best way is still to use dicts - they have a method called setdefault that works similarly to defaultdicts. You can use it like this:
results = {}
for word in words:
results.setdefault(len(word), []).append(word)
as you can see, setdefault takes two arguments: a key and a default value. If the key already exists in the dict, setdefault just returns its current value as if you'd done results[key]. If that would be an error, however, it inserts the second argument into the dictionary at that key, and then returns it. This is a little bit clunkier to use than defaultdict, but when your default value is an empty list it is otherwise the same (defaultdict is better to use when your default is expensive to create, however, since it only calls the factory function as needed, but you need to precompute it to pass into setdefault).
It is technically possible to do this with nested lists, but it is ugly. You have to:
Detect the case that the list isn't big enough
Figure out how many more elements the list needs
Grow the list to that size
the most Pythonic way to do the first bit is to catch the error (something you could also do with dicts if setdefault and defaultdict didn't exist). The whole thing looks like this:
results = []
for word in words:
try:
results[len(word)]
except IndexError:
# Grow the list so that the new highest index is
# len(word)
new_length = len(word) + 1
difference = len(results) - new_length
results.extend([] for _ in range(difference))
finally:
results[len(word)].append(word)
Stay with dicts to avoid this kind of mess. lists are specifically optimised for the case that the exact numeric index of any element isn't meaningful outside of the list, which doesn't meet your use case. This type of code is really common when you have a mismatch between what your code needs to do and what the data structures you're using are good at, and it is worth learning as early as possible how to avoid it.

A list of lists of lists in Python

I'm working with the Flask framework in Python, and need to hand off a list of lists to a renderer.
I step through a loop and create a list, sort it, append it to another list, then call the render function with the masterlist, like so:
for itemID in itemsArray:
avgQuantity = getJitaQuantity(itemID)
lowestJitaSell = getJitaLowest(itemID)
candidateArray = findLowestPrices(itemID, lowestJitaSell, candidateArray, avgQuantity)
candidateArray.sort()
multiCandidateArray.append(candidateArray)
renderPage(multiCandidateArray)
My problem is that I need to clear the candidateArray and create a new one each time through the loop, but it looks like the candidateArray that I append to the multiCandidateArray is actually a pointer, not the values themselves.
When I do this:
for itemID in itemsArray:
avgQuantity = getJitaQuantity(itemID)
lowestJitaSell = getJitaLowest(itemID)
candidateArray = findLowestPrices(itemID, lowestJitaSell, candidateArray, avgQuantity)
candidateArray.sort()
multiCandidateArray.append(candidateArray)
**del candidateArray[:]**
renderPage(multiCandidateArray)
I end up with no values.
Is there a way to handle this situation that I'm missing?
I would probably go with something like:
for itemID in itemsArray:
avgQuantity = getJitaQuantity(itemID)
lowestJitaSell = getJitaLowest(itemID)
candidateArray = findLowestPrices(itemID, lowestJitaSell, candidateArray, avgQuantity)
multiCandidateArray.append(sorted(candidateArray))
No need to del anything here, and sorted returns a new list, so even if FindLowestPrices is for some reason returning references to the same list (which is unlikely), then you'll still have unique lists in the multiCandidateArray (although your unique lists could hold references to the same objects).
Your code already creates a new one each time through the loop.
candidateArray = findLowestPrices(...)
This assigns a new list to the variable, candidateArray. It should work fine.
When you do this:
del candidateArray[:]
...you're deleting the contents of the same list you just appended to the master list.
Don't think about pointers or variables; just think about objects, and remember nothing in Python is ever implicitly copied. A list is an object. At the end of the loop, candidateArray names the same list object as multiCandidateArray[-1]. They're different names for the same thing. On the next run through the loop, candidateArray becomes a name for a new list as produced by findLowestPrices, and the list at the end of the master list is unaffected.
I've written about this before; the C way of thinking about variables as being predetermined blocks of memory just doesn't apply to Python at all. Names are moved onto values, rather than values being copied into some fixed number of buckets.
(Also, nitpicking, but Python code generally uses under_scores and doesn't bother with types in names unless it's really ambiguous. So you might have candidates and multi_candidates. Definitely don't call anything an "array", since there's an array module in the standard library that does something different and generally not too useful. :))

Python if in Statement Specifics

In short i have a list of items. lets say they have a name of Object1, Object2,....Object10,....Object20 And so on.
This list depending on use input changes starting points, for the example lets say the list packs in the names of all objects from Object18 up to Object28
Im using a statement to select these items from the stored list that goes:
for i in nuke.allNodes():
if i.name() in hiddenLists:
i.setSelected(True)
else:
i.setSelected(False)
Which works generally... trouble is because "in" (for inside the list) doesnt specify i want it to have to match an entire entry of the list, Instead of JUST selecting Object 18-28 it selects Object1 Object2 And Object 18-28 (reason being of course, Object18 and so on begin with Object1, and the 20s with a 2)
I cant pad the strings due to the fact that these are set names a program creates and have to stay the same. my only question is, is there a better operator than in that makes it have to match exactly rather than see Object1 within 'Object18'?
Looks like hiddenLists is a string (str) entered by the user. Use the split method on that string to make it a list first. Then the "in" clause will do what you want.
For instance, if the user enters a comma-separated list:
hiddenLists = [x.strip() for x in hiddenLists.split(",")]
if i.name() in hiddenLists:
...

Categories