Dictionary Iteration - python

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.

Related

Variable value is kept into next iteration of "for" loop?

Take the pseudocode below for example:
Python keeps the previous value for x, so if get_sum() fails, the conditional is still checked using the previous value of x.
Is this because python for loop doesn't introduce a new scope and is it ok to simply del the object at the end of each iteration?
for number in number_list:
try:
x = get_sum()
except:
....
if x > 100:
do something
Every variable in python is created in the scope of their respective functions and classes rather than at different levels of indentation, or in for loops or while loops. There's no block scope in python like there is in java.
If you need x to not retain its old value, you can always set x = None in your except clause and have a conditional catch it later on. If i'm misinterpreting your question please leave a comment

Python for-loop assignment updating/missing/ghost values

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.

Access dictionary several times or store in temporary variable

Say, I need to use some value from Python dictionary several times in one piece of code. What are the best practices?
Access dictionary once, store value in some temporary variable and use this variable:
value = d['my_key']
do_some_work(value)
do_some_other_work(value)
and_again(value)
or access dictionary everytime a need this value:
do_some_work(d['my_key'])
do_some_other_work(d['my_key'])
and_again(d['my_key'])
The first approach leads to more readable functions when called, in particular when the key of the dictionary is long or not self explanatory. However, the reader will always have to check the origin of the variable if he's not willing to blindly trust the name of the variable. So why not calling the dictionary directly then?
Personally, I use both approaches according to the use case. If the key or dictionary names are long or not sufficiently self-explanatory, I create a temporary variable. Otherwise, I access the dictionary directly when calling the functions.
For a dict, the average time complexity of accessing an item is constant O(1), see
Python Time Complexity.
So, I wouldn't expect much difference in performance.

What happens to a Python input value if it's not assigned to a variable?

Was just wondering this. So sometimes programmers will insert an input() into a block of code without assigning its value to anything for the purpose of making the program wait for an input before continuing. Usually when it runs, you're expected to just hit enter without typing anything to move forward, but what if you do type something? What happens to that string if its not assigned to any variable? Is there any way to read its value after the fact?
TL;DR: If you don't immediately assign the return value of input(), it's lost.
I can't imagine how or why you would want to retrieve it afterwards.
If you have any callable (as all callables have return values, default is None), call it and do not save its return value, there's no way to get that again. You have one chance to capture the return value, and if you miss it, it's gone.
The return value gets created inside the callable of course, the code that makes it gets run and some memory will be allocated to hold the value. Inside the callable, there's a variable name referencing the value (except if you're directly returning something, like return "unicorns".upper(). In that case there's of course no name).
But after the callable returns, what happens? The return value is still there and can be assigned to a variable name in the calling context. All names that referenced the value inside the callable are gone though. Now if you don't assign the value to a name in your call statement, there are no more names referencing it.
What does that mean? It's gets on the garbage collector's hit list and will be nuked from your memory on its next garbage collection cycle. Of course the GC implementation may be different for different Python interpreters, but the standard CPython implementation uses reference counting.
So to sum it up: if you don't assign the return value a name in your call statement, it's gone for your program and it will be destroyed and the memory it claims will be freed up any time afterwards, as soon as the GC handles it in background.
Now of course a callable might do other stuff with the value before it finally returns it and exits. There are a few possible ways how it could preserve a value:
Write it to an existing, global variable
Write it through any output method, e.g. store it in a file
If it's an instance method of an object, it can also write it to the object's instance variables.
But what for? Unless there would be any benefit from storing the last return value(s), why should it be implemented to hog memory unnecessarily?
There are a few cases where caching the return values makes sense, i.e. for functions with determinable return values (means same input always results in same output) that are often called with the same arguments and take long to calculate.
But for the input function? It's probably the least determinable function existing, even if you call random.random() you can be more sure of the result than when you ask for user input. Caching makes absolutely no sense here.
The value is discarded. You can't get it back. It's the same as if you just had a line like 2 + 2 or random.rand() by itself; the result is gone.

Accessing specific values of a loop-created dictionary in a Python function

I am very new to Python and as an exercise I tried solving a basic finance exercise using code. My objective is to get a dictionary of spot rates and then a dictionary of discount rates calculated from those. I had thought to something like this:
discountrates={}
def discountrates(n):
spotrates={}
for x in range(1,n+1):
spotrates['s'+str(x)]=float(input('What is s'+str(x)+'? (not in percentage)'))
for y in range(1,n+1):
discountrates['d(0,'+str(y)+')']= 1/((1+float(spotrates['s'+str(y)]))**y)
for key, value in discountrates.items():
print (key, value)
Now the problem is that dictionary items cannot be accessed in a function. When I looked in your forum, I found solutions for unpacking the dictionary but that does not work in my case because I need to access a specific element of the dictionary, whose name cannot be fully specified (as I have seen in the Python manual) because it's part of a loop, in order for the formula to work without having to manually insert anything else. I used a dictionary in the first place to create names that were automatically generated but now I can't seem to get the information out of it.
What is the best solution?
Thanks in advance for the help. It's been driving me crazy.
It's because you called your global variable discountratesdict not discountrates (which is the name of your function).
I suggest you don"t name your dictionary like your function since the later will overwrite the former. In line 1 you say discountrates is an empty dict, in line 2 you say discountrates is a function object. You need to give them different names in python if they are on the same scope.
Furthermore why do you need discountrates to be global? would you like to keep old rates if n is smaller than a previous n? For performance I suggest you combine the two loops. Besides that there is no reason why the second loop can't read for x ... as well since zou don't use x anymore anyway. As a further hint, if you come to the conclusion, that a global is the only way it might help to add global discountratesdict, so it is easier to spot that a global is intended here, even though this is not necessary in your particular case since the []-operator needs an object and thus it already refers to your global.
Putting all this together yields:
discountratedict={}
def discountrates(n):
global discountratedict
spotrates={}
for x in range(1,n+1):
spotrates['s'+str(x)]=float(input('What is s'+str(x)+'? (not in percentage)'))
discountratedict['d(0,'+str(x)+')']= 1/((1+float(spotrates['s'+str(x)]))**x)
for key, value in discountratedict.items():
print (key, value)

Categories

Resources