Most Efficient Way of Making an If-Elif-Elif-Else Statement When the Else Is Done the Most

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



Leave a reply



Submit