Still new to programming/scripting, and this one's been bothering me. I have a function that searches through a list of names, comparing it to a template list of names, and when it finds a match, it places it in my final list in the correct order. For some later functions to work correctly, I need to be able to append some of these names as arrays/lists with. I'm running into the problem that every time I need to add a list to the final list, as soon as I change the variable, the final list updates with it. How do I fix this?
light = ['template of names in here in correct order']
listUser = ['names gathered from user input']
for userChan in listUser:
for channelName in light:
#check if channelName is a list or string
if isinstance(channelName, basestring):
#search for matches in userchan
print channelName, 'is a string'
if channelName in userChan.lower():
matchFound = True
listLight.append(userChan)
else:
print channelName, 'is a list'
for piece in channelName:
print 'searching %s in %s' %(piece, userChan.lower())
if piece in userChan.lower():
print "found %s in %s" %(piece, userChan.lower())
lightMultList.append(piece)
matchFound = True
if len(lightMultList) == 2:
listLight.append(lightMultList)
del lightMultList[:]
So my problem is with the lightMultList. It's always going to be limited to 2 elements, but it changes. Hopefully this wasn't worded too horribly..
The problem is that you're only ever creating one lightMultList. You repeatedly clear it out (with del lightMultList[:]) and re-fill it, and append the same thing over and over to lightList.
The simple fix is to just create a new lightMultList each time. Which you can do by changing this line:
del lightMultList[:]
… to:
lightMultList = []
This kind of problem is often a result of trying to directly porting C or C++ code, or just thinking in C++. If you were expecting lightList.append(lightMultList) to call a "copy constructor", that's the root problem: there is no such thing in Python. Assigning a value to a variable, appending it to a list, etc., doesn't copy anything; it just binds another reference to the same value.
Also, a C++ programmer might try to optimize performance by avoiding the wasteful creation of all those temporary objects by trying to reuse the same one, but in Python, the cost of creating a new list is about the same as the cost of iterating one step over listUser in the first place. If it's slow enough to worry about, you're going to have to reorganize your code or move the whole thing to C or Cython anyway; this isn't going to help. (That being said, it's rarely a useful optimization in C++ either; the right thing to do there, on the rare occasions where it matters, is to construct the new vector in-place within the containing vector…)
Related
I know this is not good coding and I'm looking to improve it. I want to get the first name by default if no name is supplied. My hack still enters the for loop and if it would be a big one it would be innefficient. But if I do all the attributions that I'm doing in the for loop's inside if, again, in the outside if, I would duplicate code. And I wouldn't use a function just to set attributes so I'm not sure how to proceed here.
if not name:
name = self.data['big_list'][0]['name']
for i in range(len(self.data['big_list'])):
if self.data['big_list'][i]['name'] == name:
self.name = name
self.address = self.data['big_list'][i]['address']
...
return
if think there is a general bad practice or misunderstanding of class: there is a class right? and you check if your instance has the same name of one of the names in your list, and set itself.
-> in your instance it seems you have large data, containing a list of people AND attributes for one person. That does not sound correct.
Let say the class is called Person.
you can create an init() method, which takes one row of your data['big_list']. instead of setting each attribute in a loop.
you might also want to create a equals() method which checks if a person is the same as someone else. (check duplicates)
consider taking your large_data out of that class.
Could you provide us with a little more context?
Here are some comments (that might be insuficient because I do not really understand what you want to achieve with the program).
The purpose of the for loop seems to be to find an item of the list self.data['big_list'] that meets the condition self.data['big_list'][i]['name'] == name, get some data and then terminate. Each entry of self.data['big_list'] is a dict.
This is a good job for a technique called list comprehension, which is much faster than for-looping.
The expression
filtered = [item for item in self.data['big_list'][1:] if item['name'] == name]
results in a list of dicts that are not the first one and meet the name-condition. The used expression self.data['big_list'][1:] is all of self.data['big_list'] but the first one. This technique is called slicing of lists. I assume you are not interested in the first entry, because you got the name from this first entry and search for other entries with the same name (which your for-loop doesn't, btw).
There may be more than one entry in filtered, or, none. I assume you are only interested in the first match, because your program does return when a match happens. Therefore, the second part of the program would be
if len(filtered) > 0:
first_match = filtered[0]
self.name = name
self.address = first_match['address']
...
This way the structure of the program is more clear and other readers can better understand what the program does. Furthermore, it is much faster ;-).
I've discovered this wierd glitch in python. I was playing around with evolution simulations, so I made loads of creatures that each has it's own genes. As you can imagine, it's important to be able to mutate the genes and create new creatures with it. I saved each creatures genes in a dictionary, and each gene was a list within the dictionary (which might have had more lists in it).
The problem arose when trying to apply mutations to the genes. If I didn't make a copy of the gene dictionary any change to the dictionary would result in a similar change in all other creatures. (Not really a big problem, but I would be interested in hearing a reason for this. Do dictionaries and classes not play well together?)
So whats the problem? Just copy the genes, right? Well, the thing I discovered was that this problem also applied to the lists inside the dictionary. If I didn't want the changes to translate across all instances, I would have to copy the list (copyList = List[:]). This would happen even though I was making the changes to a COPY of the original f*cking gene dictionary. What's more, any list within a list would have to get the same treatment. What's even more, I still get this problem if I make the changes in a function outside the class which I call within the class.
This is quite annoying, but I think there may be other problems I am not aware of: other ways in which the genes of other instances may be corrupted. I've been having problems with my simulation because of this. Making copy after copy after copy results in messy code.
Does anybody know why this is happening? Is there a simple way to fix it? Below is some simplified code to illustrate the problem. I can't imagine what is going on.
tl;dr: Run the code below to see some wierd voodoo shit:
#DEMONSTRATION OF A F*CKING STUPID PHENOMENON WITH PYTHON CLASSES
class creature(object):
def __init__(self,genes):
self.genes = genes
def changeGenes(self,details):
genes = self.genes.copy()
n,i,s = details
try:
genes[n][i] = s
self.genes = genes
#WAY THAT WORKS
#g = genes[n][:]
#g[i] = s
#genes[n] = g
#self.genes = genes
except:
print('error: incorrect key or index')
def main():
active_objects = []
genes = {'1': [1,2,3],
'2': [1,2,3]}
for n in range(3):
active_objects.append(creature(genes))
while True:
print('\nPlease choose what to do: ')
print('1) Change genes of first creature')
print('2) Print all genes')
c = input('Your choice: ')
if c == '1':
n = input('Choose gene key: ')
i = int(input('Choose gene index: '))
s = input('Choose symbol to replace gene: ')
active_objects[0].changeGenes([n,i,s])
elif c == '2':
for item in active_objects:
print(item.genes)
elif c == '0':
print('Thank you! Come again!')
break
else:
print('error: invalid input')
main()
As you noticed, when two variables point to the same instance of a mutable object (in your case, a dict or a list) then modifications on this instance will be seen by all variables pointing to it.
The "variable" can also be a dict element, or a position in a list... Assignment in python does not copy objects (this would be slow) and uses references (ie, pointers) instead. Thus the same object instance can have references pointing to it in multiple places. This is usually desirable:
a = MyClass()
doSomethingTo( a )
You would expect a to be modified if doSomethngTo() actually does something to it. This occurs because a reference to the object a is passed as a parameter to the function, not a copy of the object.
One solution is to use the copy.deepcopy module.
You could also replace the lists with tuples, and create a new tuple instead of modifying the list. You would still need to copy the dict, though.
This is not a glitch but intended behavior. Dictionaries are references if you pass them to a new variable, the new variable will still reference the old dictionary (this behavior comes actually in handy if you want to change some value deep in a nested dictionary). If you want to have a new dictionary that resembles the old one you have to do make a deepcopy. .copy makes a shallow copy in which nested dictionary will be still treated as references.
Just try:
from copy import deepcopy
class creature(object):
def __init__(self, genes):
self.genes = deepcopy(genes)
and it it will work.
On that note, passing mutable references into functions is not a good idea either. Just because they can change at any time if referenced from somewhere else. You should look into immutable data structures. The collection module has some very interesting options.
I am starting out with Python now, working on the Learn Python The Hard Way, on exercise 36. I want to create a good game, and learn some more techniques before moving on with the next exercise. I haven't approached the subject of Classes and Object yet, yet I want to see if I can do something that would be a bit more complex than a standard first 'choose-your-own-adventure' game.
I want to collect four different keys in 'Ganon's Lair', and then use those four keys to open a door in the 'main-hall'. I already have quite some of it worked out (not elegantly), but I still need to figure out how to store keys without them getting erased. An unelegant way is to assign them as global variables, such as I do here.
def grass_room():
global key1
grass_instructions = """As you enter the lair of the grass room, you see
a spider hanging on the ceiling, with his
big eye focused on you. You could grab your 1.slingshot,
or perhaps 2.make a run for the green-coloured key under his
tail?"""
print grass_instructions
gohma_boss = raw_input("> ")
if gohma_boss == "1":
print "You shoot him in the eye, he falls down and dies. You grab the key, and return."
key1 = True
main_hall("not_empty")
else:
print die("You die.")
main_hall("not_empty")
Any suggestions for different ways to 'save' this key across functions, besides making them global?
If you want some variable or variables to be shared between functions, there are a few ways to do it:*
Pass each variable's value into every function as an argument, and return it from every function as part of a return tuple.
Wrap all of the values up in some structure, like a dict that you can look each thing up in by name, so you only have one value to pass and return.
Make the values attributes of an object, and turn the functions into methods of that object's type.
Use a closure, which I won't bother to explain because I'm sure you haven't learned about closures yet.
Use globals.
The Pythonic way to do this is definitely #3, but you haven't learned about classes yet. In that case, I'd just use #5, as you're already doing.
And when you learn about classes, coming back and modifying this script to use a class instead will be a great exercise.
* In fact, under the covers, options 3-5 are all pretty much syntactic sugar for option 2… but don't worry about that.
You could make your key datatype a list or a dict and pass the key list/dict into each key-changing function. Since lists and dicts are mutable, changes made to the key list/dict in one function will be seen in the calling scope and in any subsequent functions that key is passed to.
Let's say I want to partition a string. It returns a tuple of 3 items. I do not need the second item.
I have read that _ is used when a variable is to be ignored.
bar = 'asd cds'
start,_,end = bar.partition(' ')
If I understand it correctly, the _ is still filled with the value. I haven't heard of a way to ignore some part of the output.
Wouldn't that save cycles?
A bigger example would be
def foo():
return list(range(1000))
start,*_,end = foo()
It wouldn't really save any cycles to ignore the return argument, no, except for those which are trivially saved, by noting that there is no point to binding a name to a returned object that isn't used.
Python doesn't do any function inlining or cross-function optimization. It isn't targeting that niche in the slightest. Nor should it, as that would compromise many of the things that python is good at. A lot of core python functionality depends on the simplicity of its design.
Same for your list unpacking example. Its easy to think of syntactic sugar to have python pick the last and first item of the list, given that syntax. But there is no way, staying within the defining constraints of python, to actually not construct the whole list first. Who is to say that the construction of the list does not have relevant side-effects? Python, as a language, will certainly not guarantee you any such thing. As a dynamic language, python does not even have the slightest clue, or tries to concern itself, with the fact that foo might return a list, until the moment that it actually does so.
And will it return a list? What if you rebound the list identifier?
As per the docs, a valid variable name can be of this form
identifier ::= (letter|"_") (letter | digit | "_")*
It means that, first character of a variable name can be a letter or an underscore and rest of the name can have a letter or a digit or _. So, _ is a valid variable name in Python but that is less commonly used. So people normally use that like a use and throw variable.
And the syntax you have shown is not valid. It should have been
start,*_,end = foo()
Anyway, this kind of unpacking will work only in Python 3.x
In your example, you have used
list(range(1000))
So, the entire list is already constructed. When you return it, you are actually returning a reference to the list, the values are not copied actually. So, there is no specific way to ignore the values as such.
There certainly is a way to extract just a few elements. To wit:
l = foo()
start, end = foo[0], foo[-1]
The question you're asking is, then, "Why doesn't there exist a one-line shorthand for this?" There are two answers to that:
It's not common enough to need shorthand for. The two line solution is adequate for this uncommon scenario.
Features don't need a good reason to not exist. It's not like Guido van Rossum compiled a list of all possible ideas and then struck out yours. If you have an idea for improved syntax you could propose it to the Python community and see if you could get them to implement it.
I am trying to solve a interview question: how to convert a BST to a double linked list.
Below is my code. I am really confused about how python recursion works. Why after the convert2DoubleLst_recursive , the prev and head remain to be None, I have assigned a new value to these two variables, why after this method call, I cannot save the change. I remembered that python pass arguments by reference, but why here I cannot save the changes. Many thanks in advance.
def convert2_recursive(node):
prev,head=None,None
convert2DoubleLst_recursive(node,prev,head)
end=head.lChild
printList(head,end)
def convert2DoubleLst_recursive(node,prev,head):
if node:
convert2DoubleLst_recursive(node.lChild,prev,head)
node.lChild=prev
if prev:
prev.rChild=node
else:
head=node
nextNode=node.rChild
head.lChild=node
node.rChild=head
prev=node
convert2DoubleLst_recursive(nextNode,prev,head)
Naively put, because there is no pass-by-reference in Python. Recursion is not your problem.
It may become a little clearer, when you think "There are no variables in Python, only names and values".
When the statement convert2DoubleLst_recursive(node.lChild, prev, head) is executed, actually the function convert2DoubleLst_recursive is called with the values that node.lChild, prev and head point to at that time.
Then, within convert2DoubleLst_recursive, those values are given the (local) names node, etc. (which are different names from those before!).
Later you assign new values to those names, recurse (which is not actually relevant in your code) and essentially Python forgets the names and their values when exiting the function.
Basically, between line 8 and 10 prev does not change it's value (as you experienced), because the name used in the caller is never seen inside of the callee. The value assigned to that name is not changed in line 3 and it is not relevant what happens inside the callee.
If you want to pass values back to your caller, you have to return them.
Alternatively you could replace None by a guardian object that behaves like your node class and represents None.
Usually, though, there are better data structures you would use in Python than linked lists.
(Python lists for example are already linked lists internally and can be used instead.)
A much deeper analysis can be found here: https://stackoverflow.com/a/986145/110707 .