3

I am new to python. I have query regarding un-hardcoding object names(if condition) in python script. I have fruit = [ Apple, Mango, Pineapple, Banana, Oranges] and size = [ small, medium , big] Currently I write code as below:

if (fruit == apple, size == small): 
   statement 1
   statement 2
elif (fruit == apple, size == medium):
   statement 1
   statement 2
elif (fruit == apple, size == big):
   statement 1
   statement 2
elif (fruit == Mango, size == small):
   statement 1
   statement 2
elif (fruit == Mango, size = medium):
   statement 1
   statement 2

How can I avoid writing multiple if...else conditions?

Statement 1: Pulling up a dot file related to fruit and size from directory The path structure is main-directory/fruit/collateral/fruit_size.dot statement 2: Pulling up a txt file related to fruit and size from directory The path structure is main-directory/source/readparamters/readparam/fruit_size.txt

I want to execute statements for each condition one at a time. Currently I take inputs for fruit and size from user. Is there a way in python that the script can automatically take combinations one by one and execute statements? I know it's somewhat complex and python expert can help me.

geek_xed
  • 165
  • 1
  • 8
  • 15
  • Is `statetment 1` equivalent to `statement 2`? – Ajoy Jul 02 '15 at 01:53
  • no statement 1 and 2 both are different. but for each case they are same. – geek_xed Jul 02 '15 at 01:55
  • Are these the only cases (5 in all ) when the statements are executed? Or are the statements executed for each fruit and size? – Ajoy Jul 02 '15 at 02:00
  • 1
    for each fruit and size combination. like each fruit can be paired up with three sizes (small, medium and large) – geek_xed Jul 02 '15 at 02:07
  • Is [this](http://stackoverflow.com/a/31174472/1761793) (my answer) what you wanted? – Ajoy Jul 02 '15 at 02:29
  • 2
    This expression `(fruit == apple, size == small)` is a tuple and will always be `True`. I suspect you mean `fruit == apple and size == small` – John La Rooy Jul 02 '15 at 03:03

6 Answers6

7

You can create a map of values and functions. For example

MAP = {'apples':{'small':function1,
                'large':function3},
      'oranges':{'small':function2}}
#Then Run it like so:
fruit = 'apples'
size = 'large'
result = MAP[fruit][size]()

That will look up the function for you in the dictionary using fruit and size then and run it and store output in result. This way, if you need to add additional fruit or sizes you can simply modify the data in the dictionary without altering any code.

EDIT: I just read your update. If processing steps are the same and the only thing that changes is the location of the file, I would suggest to write a function that takes fruit and size as arguments and opens the file based on input. Then you can run it with your desired fruit and sizes and not have a crazy if statement.

nick_v1
  • 1,654
  • 1
  • 18
  • 29
  • Concise! Especially good if there are different conditions for each combination. – Ajoy Jul 02 '15 at 02:39
  • Can u tell me some example? I am really new to python – geek_xed Jul 02 '15 at 03:31
  • This is indeed a nice solution. However, I would wonder the performance difference with the naive if-else statements when conditions become much more complex. – TaoPR Jul 02 '15 at 04:29
  • @TaoP.R. What is faster a couple of dictionary lookups or a bunch of conditional if statements? – nick_v1 Jul 02 '15 at 13:29
2

(My first answer)

You can combine all the tests into one condition:

if (fruit == apple and (size == small or size == medium or size == big)) or \
        (fruit == Mango and (size == small or size == medium)):
    statement 1
    statement 2

(My second answer after the original poster made clarifications)

Write a loop to check if the condition is met, then execute the statements.

fruit = (...)  # From input
size  = (...)  # From input

found = False
known_fruits = ["apple", "mango"]  # Can be customized
known_sizes = ["small", "medium", "large"]  # Can be customized
for fr in known_fruits:
    for sz in known_sizes:
        if fruit == fr and size == sz:
            found = True
if found:
    statement 1
    statement 2
Nayuki
  • 17,911
  • 6
  • 53
  • 80
  • Note: The backslash `\\` lets you split a long line of code over multiple lines. – Nayuki Jul 02 '15 at 01:53
  • Based on the comment which states that statement 1 and statement 2 are always the same, this is the correct answer. – Jerska Jul 02 '15 at 02:03
  • Thanks for this. What if I want to write a script where I don't know about fruit to be added in future in list. can we just make us of variable 'fruit' and 'sizes' in the script, avoiding names such as apple, mango or small, medium ? – geek_xed Jul 02 '15 at 02:12
  • I wrote an alternate answer now. – Nayuki Jul 02 '15 at 02:41
1

How about using itertools.product and handling errors with try..catch. https://docs.python.org/3/library/itertools.html#itertools.product

import itertools

fruits = ['Apple', 'Mango', 'Pineapple', 'Banana', 'Oranges']
sizes = ['small', 'medium', 'big']

for fruit, size in itertools.product(fruits, sizes):
    dot_file = 'main-directory/{fruit}/collateral/{fruit}_{size}.dot'.format(fruit=fruit, size=size)
    text_file = 'main-directory/source/readparamters/readparam/{fruit}_{size}.txt'.format(fruit=fruit, size=size)
    try:
        open(dot_file)  # statement 1
        open(text_file)  # statement 2
    except Exception as e:
        print(e)  # handle erorrs!

Or, check file existence os.path.isfile without using try..catch.

import os
..
    dot_file = ..
    text_file = ..
    if os.path.isfile(dot_file):
        open(dot_file)  # statement 1
    if os.path.isfile(text_file):
        open(text_file)  # statement 2

itertools.product generates cartesian product of input iterables.

>>> fruits = ['Apple', 'Mango', 'Pineapple', 'Banana', 'Oranges']
>>> sizes = ['small', 'medium', 'big']
>>> list(itertools.product(fruits, sizes))
[('Apple', 'small'), ('Apple', 'medium'), ('Apple', 'big'), ('Mango', 'small'), ('Mango', 'medium'), ('Mango', 'big'), ('Pineapple', 'small'), ('Pineapple', 'medium'), ('Pineapple', 'big'), ('Banana', 'small'), ('Banana', 'medium'), ('Banana', 'big'), ('Oranges', 'small'), ('Oranges', 'medium'), ('Oranges', 'big')]
satomacoto
  • 11,349
  • 2
  • 16
  • 13
  • Thanks for this. I am getting one error that {fruit}_{size}.txt is not defined. like apple_medium is not defined. any idea how can I tackle this? – geek_xed Jul 02 '15 at 15:54
0

It really depends on what you have as statement 1 and statement 2.
One thing that works if the code is similar is to extract all the changing constants into a container, and use a dict to access them.

fruits = {
  'mango': {'color': 'orange'},
  'apple': {'color': 'red'},
   # ...
}
weights = {
  'apple': {
    'small': 100,
    'medium': 200,
    # ...
  }
  # ...
}

fruit = 'apple'
size = 'medium'

print "A %s %s is %s and weighs %ig" % (size, fruit, fruits[fruit]['color'], weights[fruit][size])
# => A medium apple is red and weighs 200g
Jerska
  • 11,722
  • 4
  • 35
  • 54
  • basically I have fruit = [ Apple, Mango, Pineapple etc.] and size = [ small, medium, large]. Now for each fruit three conditions are possible. The statements are basically pulling up info from directories. The path structure is main-directory/fruit-name/collateral/fruitname_size.png. and similarly other file: main-directory/source/readparameters/readparam/fruit_size.txt. I have written multiple if else conditions for all combinations. Any way I can do it in small piece of code? – geek_xed Jul 02 '15 at 02:00
  • I'm saying that the most important here is actually what you have in statement1 and statement2 in order to design your code. – Jerska Jul 02 '15 at 02:01
0

If all the file names can be generated using just the fruit names and sizes:

def process_fruits(fruit, size, param):
    dot_file = "main-directory/{fruit_name}/collateral/{size}.dot"
    text_file = "main-directory/source/readparamters/{param}/{size}.txt"

    fruit = dict(fruit_name=fruit, size=size, param=param)

    paths = dict(dot=dot_file.format(**fruit), text=text_file.format(**fruit))

    # load paths['dot']
    # load paths['text']

You can add this to a function, to get the paths whenever a fruit is created.


fruits = ['apple', 'mango']

for fruit in fruits:
    for size in ['small', 'medium', 'big']:
        process_fruits(fruit, size, 10)
Ajoy
  • 1,838
  • 3
  • 30
  • 57
  • @NayukiMinase see op's [comment](http://stackoverflow.com/questions/31174387/how-to-avoid-hard-coding-in-if-condition-of-python-script/31174472?noredirect=1#comment50356366_31174466) above. – Ajoy Jul 02 '15 at 02:03
  • Thanks. Actually dot_file = 'main-directory/{fruit_name}/collateral/fruitname_size.dot' and text_file = 'main-directory/source/readparamters/readparam/fruitname_size.txt'. so in this case we will have to define fruit = dict (fruit_name = 'apple', size ='small') for each case, like apple, medium; apple, large; mango, small? The sizes will remain the same (small, medium, large) but fruits list can increase. – geek_xed Jul 02 '15 at 02:37
  • @geek_xed All that is up to you. This is the way I prefer when I can directly map the fruits to the files. When to call them will depend on where you are processing the fruits. – Ajoy Jul 02 '15 at 02:51
  • @geek_xed Updated my answer. Something like this will suffice for you – Ajoy Jul 02 '15 at 02:59
0

Python doesn't have switch cases unfortunately, but you can use a dictionary like the accepted answer to this question:

Replacements for switch statement in Python?

Community
  • 1
  • 1
Nishant Roy
  • 1,043
  • 4
  • 16
  • 35