Most efficient way of making an if-elif-elif-else statement when the else is done the most?
The code...
options.get(something, doThisMostOfTheTime)()
...looks like it ought to be faster, but it's actually slower than the if
... elif
... else
construct, because it has to call a function, which can be a significant performance overhead in a tight loop.
Consider these examples...
1.py
something = 'something'
for i in xrange(1000000):
if something == 'this':
the_thing = 1
elif something == 'that':
the_thing = 2
elif something == 'there':
the_thing = 3
else:
the_thing = 4
2.py
something = 'something'
options = {'this': 1, 'that': 2, 'there': 3}
for i in xrange(1000000):
the_thing = options.get(something, 4)
3.py
something = 'something'
options = {'this': 1, 'that': 2, 'there': 3}
for i in xrange(1000000):
if something in options:
the_thing = options[something]
else:
the_thing = 4
4.py
from collections import defaultdict
something = 'something'
options = defaultdict(lambda: 4, {'this': 1, 'that': 2, 'there': 3})
for i in xrange(1000000):
the_thing = options[something]
...and note the amount of CPU time they use...
1.py: 160ms
2.py: 170ms
3.py: 110ms
4.py: 100ms
...using the user time from time(1)
.
Option #4 does have the additional memory overhead of adding a new item for every distinct key miss, so if you're expecting an unbounded number of distinct key misses, I'd go with option #3, which is still a significant improvement on the original construct.
Shorten if-elif-elif...-else
That's a lot of duplicate code for what is, essentially, the same thing (I've cleaned up your syntax a bit):
itemSellCount = int(input("How many would you like to sell? "))
if itemSellCount <= inventory[itemSell]:
itemSoldCash = itemSellCount*10
print(f"You sold {itemSellCount} {itemSell}/s for ${itemSoldCash}")
cash += itemSoldCash
inventory[itemSell] -= itemSellCount
else:
print("You tried to sell more than what you have!")
There are, however, three things to consider:
1. How much does each item sell for?
This can be addressed in a number of ways, depending on the programming style, and what you need to track about each item. An OOP approach would be to make an item class, with each item having some attribute defining its value. A straightforward, procedural approach, would be to have a dictionary that defines this:
itemValue = {
"cobblestone": 10,
"coal": 5,
...
}
Now, use a dictionary lookup to determine itemSoldCash
:
itemSoldCash = itemSellCount*itemValue[itemSell]
2. Alternative Item Names
You accept alternative item names, e.g. "cobble stone" is treated as "cobblestone." This can also be approached with a dictionary, e.g. something like:
itemAltNames = {
"cobble stone": "cobblestone",
"iron ingot": "iron ingot",
...
}
Then, you can do something like:
if itemSell in itemAltNames:
itemSell = itemAltNames[itemSell];
Alternatively, if your alternatives only involve stripping spaces, then just do so:
itemSell = itemSell.replace(" ","")
3. Checking that the Item Exists
As it stands, your control flow won't execute if the user enters an invalid item. This is good, but overcomplicated! Also, do you give an error message (or allow repeated input) if the user enters an invalid item? Check against your inventory dictionary to ensure that the user has the item:
if itemSell in inventory:
Putting it All Together
Here's what everything might look like now:
def sell_command():
global cash
cash = 0
itemSell = input("What would you like to sell? ")
while (itemSell := itemSell.lower().strip().replace(" ","")) not in inventory:
itemSell = input(f"You do not have {itemSell} in your inventory. What would you like to sell? ")
itemSellCount = int(input("How many would you like to sell? "))
if itemSellCount <= inventory[itemSell]:
itemSoldCash = itemSellCount * itemValue[itemSell]
print(f"You sold {itemSellCount} {itemSell}/s for ${itemSoldCash}")
cash += itemSoldCash
inventory[itemSell] -= itemSellCount
else:
print("You tried to sell more than what you have!")
How to avoid very long if-elif-elif-else statements in Python function
You can't hash lists as dictionary values. So go other way round. Create a mapping of type -> list. And initialize your output with the default type. This allows you to keep on adding new types to your mapping without changing any code.
def very_long_func():
something = 'Audi'
car = ['VW', 'Audi', 'BMW']
drinks = ['Cola', 'Fanta', 'Pepsi']
countries = ['France', 'Germany', 'Italy']
out = {'type': 'nothing found'} # If nothing matches
mapping = {
'car brand': car,
'lemonade brand': drinks,
'country': countries
}
for k,v in mapping.items() :
if something in v:
out['type'] = k # update if match found
break
return out # returns matched or default value
Is there any way to set an elif statement inside the init() function in the class?
Yes, you can use conditionals fine within a constructor, but you don't need them. You can cap values using min
/max
functions, for example
self.math_score = min(max(0, math_score), 90)
self.english_score = min(max(0, english_score), 90)
self.science_score = min(max(0, science_score), 90)
If the input is negative, max(0, value)
returns 0
. If the input is above 90, then min(value, 90)
returns 90
.
Need to shrink my large if elif statement in python
The attributes don't appear to be dependent on each other; handle each one separately:
def cars(self, model=None, color=None, miles=None)
url = "cars"
if model is not None:
url += "/model=%s" % (model,)
if color is not None:
url += "/color=%s" % (color,)
if miles is not None:
url += "/miles=%s" % (miles,)
return url
This leads to the realization that you probably want to accept arbitrary keyword arguments, and check for the existence of a specific set:
def cars(self, **kwargs):
url = "cars"
for kw in ["model", "color", "miles"]:
if kwargs.get(kw) is not None:
url += "/%s=%s" % (kw, kwargs[kw])
return url
This ignores the issue of whether or not the string you are building is, in fact, a valid URL.
Related Topics
Handling Variable Number of Columns with Pandas - Python
How to Overload _Init_ Method Based on Argument Type
Differencebetween Class and Instance Variables
How to Build a Numpy Array from a Generator
How to Overload Python Assignment
Sorting Columns in Pandas Dataframe Based on Column Name
Typeerror: 'Nonetype' Object Is Not Iterable in Python
Converting Dict to Ordereddict
Python Class Instance Variables and Class Variables
Python - Extracting and Saving Video Frames
List VS Generator Comprehension Speed with Join Function
How to Solve a Pair of Nonlinear Equations Using Python
Attributeerror: Module 'Time' Has No Attribute 'Clock' in Python 3.8
How to Get the Different Parts of a Flask Request's Url
Pandas Dataframe Stored List as String: How to Convert Back to List