Sorry if the title is not so specific. I am looking to restructure variables and if-statements in a program to make the code more efficient. The current code is pretty large and I am sure it can be shortened by at least 70%.
I just started learning Python two weeks ago, I may be missing basic functions that are pretty obvious.
Here is the code:
def function():
variable = int(input('Input: ')
if variable == 10:
variable = 1
elif variable == 9:
variable = 0.9
If I want to repeat this with different values and variables it gets pretty long, and this is what I want to fix.
A friend told me to use dictionaries to store the values but I am not sure how to use if-statements with dictionary values.
How should I structure my code to make it shorter?
At least for the 10 and 9, you are dividing variable by 10. So its better to write it that way instead of writing it explicitly. Also, you can combine the 2 cases in one if case then.
This will shorten the code a bit.
def function():
variable = int(input('Input: '))
if variable == 10 || variable == 9:
variable /= 10
Thank you all for your answers, the solution was easier than I expected and now I feel dumb for not knowing before.
def probeNew():
variable = int(input('Input: '))
output = variable/6.666667
print(output)
probeNew()
Instead of writing all of the fixed values for each input, I just make a formula for whatever result I want from the input. (I know, extremely obvious and easy)
If you want to implement a lookup logic clearly, use dict structure to save key/value pairs.
The key usually be a string, and value can be any others type, like string, integer, etc.
In your case, you can setup the lookup_table={"10":1,"9":0.9},
and get the value by a key, for example, variable = lookup_table[str(10)]
lookup_table={"variable":"value1","variable2":2}
variable = lookup_table[variable]
Related
Am I being dumb here?
I would like to know if I have a list called cons=['gps-ops', 'beidou'] and in my code there are different names for the same thing. i.e: 'gps-ops' = 'GPS' and 'beidou'='BDS'.
Some parts of the code (class) takes 'gps-ops' and some parts take 'GPS'. At the moment I have been using if and elif statements at different sections of the code.
Is there a way to say 'gps-ops' is also 'GPS' and vice versa, depending on how the user inputs the string throughout the code?
You could put every word for each valid name in a set like {'gps-ops', 'GPS'} and then go through your list in a for loop and check if that word is in the set:
gps_set = {'gps-ops', 'GPS'}
for name in cons:
if name in gps_set:
# do somehthing if true
Read the python docs for sets, they're quite an amazing data structure
I'm currently doing a dice game for my school programming project, and it includes the rules: 'Stores the winner’s score, and their name, in an external file' and 'Displays the score and player name of the top 5 winning scores from the external file.' I've done everything in my game apart from the leaderboard. I am able to write the name and score of the user into the txt file, but I am unsure of how to then sort it, and would also like when people first start the program can use the menu to go to the leaderboard where it would then read the txt file and print the top 5 scores in order including the names.
I've checked loads of over questions similar to mine, but none of them exactly worked for my code as I kept on getting errors implementing other people's code into mine which just weren't compatible with my layout.
(deleted)
Thanks in advance, I've never used stack overflow to ask a question so I apologize if there's anything I've done wrong in my post.
You did good on the question. You stated the problem clearly and, most importantly, you added enough code to run the code so we can have a look at how the program behaves and what's going wrong. In this case nothing is going wrong which is good :)
Considering you mention that this is a school project I will not give you a fully copy/paste solution but will explain hopefully enough details on how to solve this on your own.
Now according to the question, you don't know how to sort your leaderboard. I ran the program a few times myself (after removing the sleeps because I am impatient 😋) and see that your leaderboard file looks like this:
90 - somename
38 - anothername
48 - yetanothername
To display this you must do two things:
Open the file and read the data
Convert the data from the file into something usable by the program
The first step seems to be something you already know as you already use open() to write into the file. Reading is very similar.
The next step is not so obvious if you are new to programming. The file is read as text-data, and you need to sort it by numbers. For a computer the text "10" is not the same a the number 10 (note the quotes). You can try this by opening a Python shell:
>>> 10 == 10
True
>>> 10 == "10"
False
>>> "10" == 10
False
And text sorts differently to numbers. So you one piece of the solution is to convert the text into numbers.
You will also get the data as lines (either using readlines() or splitlines() depending on how you use it. These line need to be split into score and name. The pattern in the file is this:
<score> - <name>
It is important to notice that you have the text " - " as separator between the two (including spaces). Have a look at the Python functions str.split() and str.partition(). These functions can be applied to any text value:
>>> "hello.world".split(".")
['hello', 'world']
>>> "hello.world".partition(".")
('hello', '.', 'world')
You can use this to "cut" the line into multiple pieces.
After doing that you have to remember the previous point about converting text to numbers.
As a last step you will need to sort the values.
When reading from the file, you can load the converted data into a Python list, which can then be sorted.
A convenient solution is to create a list where each element of that list is a tuple with the fields (score, name). Like that you can directly sort the list without any arcane tricks.
And finally, after sorting it, you can print it to the screen.
In summary
Open the file
Read the data from the file as "lines"
Create a new, empty list.
Loop over each line and...
... split the line into multiple parts to get at the score and name separately
... convert the score into a number
... append the two values to the new list from point 3
Sort the list from point 3
Print out the list.
Some general thoughts
You can improve and simplify the code by using more functions
You already show that you know how to use functions. But look at the comments #THIS IS ROUND1 to #THIS IS ROUND5. The lines of code for each round are the same. By moving those lines into a function you will save a lot of code. And that has two benefits: You will only need to make a code-change (improvement or fix) in one place. And secondly, you guarantee that all blocks behave the same.
To do this, you need to think about what variables that block needs (those will be the new function arguments) and what the result will be (that will be the function return value).
A simple example with duplication:
print("round 1")
outcomes = []
value1 = random(1, 100)
value2 = random(1, 100)
if value1 > value2:
outcomes.append("A")
else:
outcomes.append("B")
print("round 2")
outcome = ""
value1 = random(1, 100)
value2 = random(1, 100)
if value1 > value2:
outcomes.append("A")
else:
outcomes.append("B")
Rewritten with functions
def run_round(round_name):
print(round_name)
value1 = random(1, 100)
value2 = random(1, 100)
if value1 > value2:
return "A"
else:
return "B"
outcomes = []
result_1 = run_round("round 1")
outcomes.append(result_1)
result_2 = run_round("round 2")
outcomes.append(result_2)
As you can see, the second code is much shorter and has no more duplication. Your code will have more function arguments. It is generally a challenge in programming to organise your code in such a way that functions have few arguments and no complex return values. Although, as long as it works nobody will look too closely ;)
Safe way to ask for a password
You can use getpass() from the module getpass() to prompt for a password in a secure manner:
from getpass import getpass
password = getpass()
Note however, if you are using PyCharm, this causes some issues which are out of scope of this post. In that case, stick with input().
Sleeps
The "sleep()" calls are nice and give you the chance to follow the program, but make it slow to test the program. Consider to use smaller values (comma-values are possible), or, even better, write your own function that you can "short-circuit" for testing. Something like this:
import time
ENABLE_SLEEP = True
def sleep(s):
if ENABLE_SLEEP:
time.sleep(s)
print("some code")
sleep(1)
print("more code")
sleep(4)
You will then use your own sleep() function anytime you want to wait. That way, you can simply set the variable ENABLE_SLEEP to False and your code will run fast (for testing).
I was working on a dictionary, but I came up with an issue. And the issue is, I want to be able to use more than 1 number in order to reference a key in a dictionary.
For example, I want the range of numbers between 1 and 5 to be all assigned to, let's say, "apple". So I came up with this:
my_dict['apple'] = range(1,5)
At the program's current state, its far from being able to run, so testing is an issue, but I do not receive any issues from my editor. I was just wondering, is this the correct way to go about this? Or is there a better way?
Thanks.
EDIT:
A little more info: I want to make a random integer with the randint function. Then, after Python has generated that number, I want to use it to call for the key assigned to the value of the random integer. Thing is, I want to make some things more common than others, so I want to make the range of numbers I can call it with larger so the chance of the key coming up becomes likelier. Sorry if it doesn't make much sense, but at the current state, I really don't even have code to show what I'm trying to accomplish.
You have the dictionary backwards. If you want to be able to recall, e.g., 'apple' with any of the numbers 1-5, you'd need the numbers to be the keys, not the values.
for i in range(1,6): # range(a,b) gives [a,b)
my_dict[i] = 'apple'
etc. Then, my_dict[4] == 'apple' and the same is true for the other values in the range.
This can create very large dictionaries with many copies of the same value.
Alternately, you can use range objects as dictionary keys, but the testing will be a bit more cumbersome unless you create your own class.
my_dict[range(1,6)] = 'apple'
n = random.randint(1, 5)
for key in my_dict:
if n in key:
print(my_dict[key])
...prints apple.
The value in a dictionary can be any arbitrary object. Whether it makes sense to use a given type or structure as a value only makes sense in the context of the complete script, so it is impossible to tell you whether it is the correct solution with the given information.
My apologizes for the basic question, I'm working through a programming book discussing arrays, Using python 2.7.5 I want to ask the user for input and store the numbers in an array, I can assign a value in the array to a string but don't want to do that, instead of doing:
emp_name[0] = raw_input("please enter employee name")
I want to add values to the array emp_name, and recall something like: emp_name[1] and have the 2nd name entered displayed for the user. I apologize for the novice question but I'm still wrapping my head around arrays.
I think I have to use a for numbers in range loop, or a while loop? Please assist
give this a shot
emp_names = []
num_to_enter = 10
while num_to_enter > 0:
emp_names.append(raw_input("enter name"))
num_to_enter -= 1
print emp_names[0]
#do what you need to do
The data structure you are looking for is dictionary, not array. Although, you can make array in that way which can be highly inefficient for searching, particularly when it involves large amount of data. If you want to stick with array, consider tuple rather than array.
Storing in dictionary can be done as key/value pair like you want.
my_store=dict()
or
my_store={}
and after that, you can store the data like you wanted as:
my_store[0]="emp_name_1"
My answer is based on the fact that you want to identify your data using some key, not just index like 1. If index is all you want to refer to your data, array is not a problem. But I think I hastened to answer you without looking and asking you properly if you just are looking to use index or other strings to fetch the stored data.
If array is all your are looking you can do as simple as:
emp_names = []
while True:
inp=raw_input("enter_name or nothing but enter to exit")
if inp='':
break
else:
emp_names.append(inp)
And you can use index 0 referring to the first element and so on to fetch the data.
Was coding something in Python. Have a piece of code, wanted to know if it can be done more elegantly...
# Statistics format is - done|remaining|200's|404's|size
statf = open(STATS_FILE, 'r').read()
starf = statf.strip().split('|')
done = int(starf[0])
rema = int(starf[1])
succ = int(starf[2])
fails = int(starf[3])
size = int(starf[4])
...
This goes on. I wanted to know if after splitting the line into a list, is there any better way to assign each list into a var. I have close to 30 lines assigning index values to vars. Just trying to learn more about Python that's it...
done, rema, succ, fails, size, ... = [int(x) for x in starf]
Better:
labels = ("done", "rema", "succ", "fails", "size")
data = dict(zip(labels, [int(x) for x in starf]))
print data['done']
What I don't like about the answers so far is that they stick everything in one expression. You want to reduce the redundancy in your code, without doing too much at once.
If all of the items on the line are ints, then convert them all together, so you don't have to write int(...) each time:
starf = [int(i) for i in starf]
If only certain items are ints--maybe some are strings or floats--then you can convert just those:
for i in 0,1,2,3,4:
starf[i] = int(starf[i]))
Assigning in blocks is useful; if you have many items--you said you had 30--you can split it up:
done, rema, succ = starf[0:2]
fails, size = starf[3:4]
I might use the csv module with a separator of | (though that might be overkill if you're "sure" the format will always be super-simple, single-line, no-strings, etc, etc). Like your low-level string processing, the csv reader will give you strings, and you'll need to call int on each (with a list comprehension or a map call) to get integers. Other tips include using the with statement to open your file, to ensure it won't cause a "file descriptor leak" (not indispensable in current CPython version, but an excellent idea for portability and future-proofing).
But I question the need for 30 separate barenames to represent 30 related values. Why not, for example, make a collections.NamedTuple type with appropriately-named fields, and initialize an instance thereof, then use qualified names for the fields, i.e., a nice namespace? Remember the last koan in the Zen of Python (import this at the interpreter prompt): "Namespaces are one honking great idea -- let's do more of those!"... barenames have their (limited;-) place, but representing dozens of related values is not one -- rather, this situation "cries out" for the "let's do more of those" approach (i.e., add one appropriate namespace grouping the related fields -- a much better way to organize your data).
Using a Python dict is probably the most elegant choice.
If you put your keys in a list as such:
keys = ("done", "rema", "succ" ... )
somedict = dict(zip(keys, [int(v) for v in values]))
That would work. :-) Looks better than 30 lines too :-)
EDIT: I think there are dict comphrensions now, so that may look even better too! :-)
EDIT Part 2: Also, for the keys collection, you'd want to break that into multpile lines.
EDIT Again: fixed buggy part :)
Thanks for all the answers. So here's the summary -
Glenn's answer was to handle this issue in blocks. i.e. done, rema, succ = starf[0:2] etc.
Leoluk's approach was more short & sweet taking advantage of python's immensely powerful dict comprehensions.
Alex's answer was more design oriented. Loved this approach. I know it should be done the way Alex suggested but lot of code re-factoring needs to take place for that. Not a good time to do it now.
townsean - same as 2
I have taken up Leoluk's approach. I am not sure what the speed implication for this is? I have no idea if List/Dict comprehensions take a hit on speed of execution. But it reduces the size of my code considerable for now. I'll optimize when the need comes :) Going by - "Pre-mature optimization is the root of all evil"...