Python for-loop assignment updating/missing/ghost values - python

Edit:
I have solved the problem, but I'm still not sure of the specifics. It appears that the issue lies in an inconsistency between if conditionals and ternary operators. item is considered True when tested as such: if item: as long as it exists. However, it is considered False when tested as such: "xyz" if item else "abc".
I'm not sure if this is intended or unintended behavior. Or perhaps there are some underlying nuances that elude me. Hence, posting this as an edit rather than as an answer.
I have the following code:
for item in item_list[:]:
item.attribute = next((i for i in item_list if i.id == func(item.id)), item(-1))
print(item.attribute.id if item.attribute else "None")
print(item.attribute is None)
print(item.id)
print(func(item.id))
print(type(i.id) is int)
func is a function that specifically returns a number cast as an int. item(x) creates an instance of item whose .id is equal to x.
What I expect: the first print statement yields the .id value of the appropriate item.
What actually happens: the first item in list works as expected. For all other items, the first print statement yields "None"
Why this is confusing: in all flawed cases, the second print statement yields "False" (I have tried changing item.attribute is None to item.attribute == None). The third and fourth print statements show that every func(match.id) has a value that matches some match.id. The fifth (last) print statement verifies that every match.id is in fact an int. I have verified externally that the list is entirely formed prior to this function. There is no manipulation of the items happening simultaneously, because there is no manipulation of the items elsewhere whatsoever.
So basically, the comparison I've created must succeed somewhere during the iteration. The types are verified to be the same. Therefore, the assignment should occur as intended; however it does not. If the assignment does not occur as intended, it should instead produce a placeholder value; however it does not.
Sorry this is so much of a mess. I feel as though I have so little a clue what is going wrong that I have no idea what information to include or emphasize. The best hypothesis I could come up with was that the variables declared within the scope of the loop were being held over between iterations. But if that was the case, I would expect every item to have the .attribute of the final item. Any help would be greatly appreciated.

Related

Refactoring a loop in Python

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 ;-).

Dictionary Iteration

I'm given this dataset and tasked with coming up with a loop to find the highest value in this dictionary. I'm confused with why by placing the print function inside the loop(#1) produces a different answer from placing it outside the loop(#2).
data = {'Jeremy':73284, 'Hansel':8784.3, 'Uee':9480938.2,
'Seolhyun':984958.3, 'Ketsuno Ana':24131, 'Trump':45789}
highest_network = data["Jeremy"]
for key,value in data.items():
if value > highest_network:
highest_network = value
print(key,value) #1
print(key,value) #2
Sorry, I'm still a beginner in python hence I am still not very familiar with some concept.
At any given time during execution, python keeps what's essentially a dictionary of variable names that exist, and their values. The way this interacts with scope is kind of confusing/complicated, but suffice it to say that in this situation, key and value are declared in a scope that is outside their for loop.
Now, note the first print statement. Since it's inside the loop, and the key and value are going to be constantly updating, it will print the current key and value every time it's executed.
The second print statement is outside the loop, and is executed after the loop has run. Keep in mind that the variables key and value are still in scope, and still hold whatever the last things assigned to them are (in this case, key and value will be the last value you'd get from data.items()). This is why they behave differently - because the value of key and value are different every time you try printing them.
Keep in mind that the order in which data.items() puts the data is effectively arbitrary, as far as the standards are concerned. The order in which each key is put through that for loop will be consistent from run to run on your machine, but if you put the same code on someone else's machine it might act differently. As a result, you should not rely on this behavior being consistent.
The first print statement is inside an conditional branch and inside the for loop.
Therefore, every time the condition given by the if statement is True the print function gets executed. Now, given your data, this holds only for the case of "Uee", whose value has the largest value, and also is the first value to be larger than "Jeremy". Also note that the order which items() return does not have to be the same every time so you can get also different results and the first print can be executed many times.
Now, the second print is outside the for loop, which means that the key and value variables hold the last value they were assigned. Again, this could be anything from the dictionary, since the ordering returned by items() does not have to be the same. In case the dictionary is traversed in the order specified in the example, it would print the "trump" entry.

how recursion works in Python

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 .

Python Lists append mutable variable

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…)

Can't Delete Function Call

This question is just out of general curiosity. I've just noticed it when working on my current project (surprisingly I haven't came across before today).
Take this code:
List = ["hi","stack","over","flow","how","you","doing"]
del List(len(List)-1)
Error:
SyntaxError: can't delete function call
I don't understand why you aren't allowed to delete an index of a list by referencing a call to a function? Do I just shut up and accept you can't do it or am I doing something fundamentally wrong?
I apologise if there is an easy answer to this but either Google is getting less helpful or this is so blatantly obvious I need help.
You meant to delete the last element of the list, not somehow call List as a function:
del List[len(List)-1]
Python's del statement must take specific forms like deleting a variable, list[element], or object.property. These forms can be nested deeply, but must be followed. It parallels the assignment statement -- you'll get a similar syntax error if you try to assign to a function call.
Of course, what you really want in this case is
del List[-1]
which means the last element of the list, and is way more Pythonic.
You are calling a function List() when you should be indexing a list, List[].
In Python, Round parenthesis, (), are used to call functions, while square brackets, [] are used to index lists and other sequences.
Try:
del List[len(List) - 1]
or even better, use the fact that Python allows negative indexes, which count from the end:
del List[-1]
Also, you might want to make the list's name not so close to the built-in list type name, for clarity.
You are allowed. However, you are using the wrong syntax. Correct syntax is:
del List[-1]
Notice that the "len(List) part is useless.

Categories

Resources