2

Consider this code:

def function(condition):

    if condition == 'condition1':
        print('No problem here')

    if condition == 'condition2':
        test = ['1','2','3','4']
        print(test)

    if condition == 'condition3':
       #doing something else using variable 'test'

Is it possible to share the static list between the two if statements? For the moment, I have two working ideas but both has its limitations

Case 1: Declare the static list at the beginning of the function

def function(condition):
    test = ['1','2','3','4']
    if condition == 'condition1':
        print('No problem here')

    if condition == 'condition2':
        print(test)

    if condition == 'condition3':
       #doing something else using variable 'test'

Limitations: This means I will create the list every time I call function, even though I don't need it when condition == 'condition1'

Case 2: Declare the static list in both if statement

def function(condition):

    if condition == 'condition1':
        print('No problem here')

    if condition == 'condition2':
        test = ['1','2','3','4']
        print(test)

    if condition == 'condition3':
       test = ['1','2','3','4']
       #doing something else using variable 'test'

Limitations: In this particular case it seems not so bad, but if my list had a lot more data with nested lists, repeating it would make code maintenance a lot harder since changes would need to be done more than once.

Maybe there is an easy way out of this and I'm over thinking the whole thing, but as I said this is the first time I've seen this situation.

scharette
  • 9,437
  • 8
  • 33
  • 67
  • 1
    You could make the creation of the list a separate function, for example. – wvxvw Oct 18 '17 at 18:39
  • Wow, yes I will wait to see if other answers arise but it is a really good point. Can't beleive I didn't see it that way. Thank you ! – scharette Oct 18 '17 at 18:41
  • If `'condition1'` is the only time you don't need the list, put a `return` in that branch, and create the list just afterwards. – jasonharper Oct 18 '17 at 18:42
  • @jasonharper This is a simplified version of the situation. I'm was thinking it for a more general case. But its a really good trick, thank you for your answer ! – scharette Oct 18 '17 at 18:45
  • Also you could make it as optional argument at your function. Since it is a list with data - it would be evaluated when interprenter meets `def`. But that is not good option enough, it's just for educational purpouses – Yaroslav Surzhikov Oct 18 '17 at 18:56
  • BTW, if you never modify `test`, you should make it a tuple. Then the literal tuple `('1', '2', '3', '4')` gets created once, when `function` is compiled, and the assignment `test = ('1', '2', '3', '4')` when you call `function` merely binds the name to that tuple (which already exists). – PM 2Ring Oct 18 '17 at 18:58
  • @YaroslavSurzhikov You _could_ do that, but if `test` gets modified, that can lead to [unexpected weirdness](https://stackoverflow.com/questions/1132941/least-astonishment-and-the-mutable-default-argument). OTOH, I agree that mutable default args can be very handy, but when you use them you need to make it clear in the comments that you know what you're doing, or other readers of your code will assume it's a bug. ;) – PM 2Ring Oct 18 '17 at 19:02
  • By the way, you could split your condition bodies to functions ant call them using dictionary: `{'cond1: some_func, 'cond2': some_func2 etc}`. Also in your case it would be nice to group `if` conditions to `if-elif` chains, so if one of them is true - others are skipped – Yaroslav Surzhikov Oct 18 '17 at 21:54

5 Answers5

1

Creation of this list is trivial. You likely spent more resources in posting than question than you can save with an optimal solution. :-) If the list is the same on every function call, you have a couple of good options:

(1) Your Case 1 is good: it's easy to read and easy to maintain. If you're worried about the assignment, turn on an optimization switch and let the interpreter realize that this is a constant assignment.

(2) Force the constant status yourself: put the assignment before the function.

test = ['1','2','3','4']

def function(condition):
    if condition ...

EDIT

For more information please see timings in this answer

scharette
  • 9,437
  • 8
  • 33
  • 67
Prune
  • 76,765
  • 14
  • 60
  • 81
  • Even if the list gets a lot bigger ? We can still consider multiples assignments as being trivial ? – scharette Oct 18 '17 at 19:01
  • 1
    Yes, in general -- compared to a long list of conditions that's executed repeatedly. – Prune Oct 18 '17 at 19:05
  • @scharette: remember rule #1 of optimization: "Don't do it". Keep the code easy to read and maintain, and you'll almost always save resources in the long run. – Prune Oct 18 '17 at 19:12
  • I agree. But sometimes one comes with the other. In some cases, optimization leads to more readability. And I think my question is a good example of that. – scharette Oct 18 '17 at 19:14
  • When they come together, by all means, do it! – Prune Oct 18 '17 at 19:19
  • After confirmation, your answer seems the best to me. Please accept the edit and I'll accept your answer. I think the link will help people understand why your answer is the one to go with. Thank you! – scharette Oct 19 '17 at 00:04
1

The right hand side of a lambda expression is not evaluated until the lambda is called. If you care to avoid recomputing the list, you could

def function(condition):

    getTestArray = lambda: ['1','2','3','4']

    if condition == 'condition1':
        print('No problem here')

    if condition == 'condition2':
        test = getTestArray()
        print(test)

    if condition == 'condition3':
        test = getTestArray()
        #doing something else using variable 'test'

I should mention, while we're on the subject, that Python's lambda and its BDFL, Guido, have an interesting history: https://www.quora.com/Why-did-Guido-van-Rossum-want-to-remove-lambda-from-Python-3/answer/Max-Fischer-10

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
samstav
  • 1,945
  • 1
  • 20
  • 20
  • This is neat, I think I will go for a normal function call but I didn't think of lamdas expression. thanks ! – scharette Oct 18 '17 at 19:12
  • **Always use a def statement instead of an assignment statement that binds a lambda expression directly to an identifier.** - PEP8 style guide. If you preffer oneliner - use `def getTestArray(): return ['1','2','3','4']` – Yaroslav Surzhikov Oct 18 '17 at 19:47
  • Yes, this is exactly why I posted an answer where I define a function instead of using lambda. I dounf the recomandation [here](https://www.python.org/dev/peps/pep-0008/#programming-recommendations) – scharette Oct 18 '17 at 19:53
0

I hate conditionals. Use objects and Polymorphism. This might seem like more work up front, but this pattern is very powerful to learn:

class ProblemAbstract:
    @property
    def test_data(self):
        return [1, 2, 3, 4]

    def process(self):
        raise NotImplementedError("Need to implement 'process'")


class NullProblem(ProblemAbstract):
    def process(self):
        return None


class ProblemType1(ProblemAbstract):
    def process(self):
        return "Do something with: {}".format(self.test_data)


class ProblemType2(ProblemAbstract):
    def process(self):
        return "Do something else with: {}".format(self.test_data)


def function(condition):
    condition_matrix = {
                        'condition1': NullProblem,
                        'condition2': ProblemType1,
                        'condition3': ProblemType2,
                        }
    return condition_matrix[condition]().process()

if __name__ == "__main__":
    print(function('condition1'))
    print(function('condition2'))
    print(function('condition3'))

Output:

None
Do something with: [1, 2, 3, 4]
Do something else with: [1, 2, 3, 4]
RobertB
  • 1,879
  • 10
  • 17
  • I will need to look this more in depth, but it seems like a good way of doing things, just overkill for the case I was looking for. Thank you though! – scharette Oct 19 '17 at 00:06
0

I ended up using the solution from a comment of @wvxvw and props goes to him.

I decided to create a function like so

def get_test():
    test = ['1','2','3','4']
    return test

def function(condition):

    if condition == 'condition1':
        print('No problem here')

    if condition == 'condition2':
        test = get_test()
        print(test)

    if condition == 'condition3':
        test= get_test()

The other answers were great, but I don't always agree that choosing not to optimize your code is the best way of doing things. Maybe I'm wrong and this is personal, but the code shown above seems to me as optimize, readable and maintainable. Especially for cases where function would be call thousands of times.

EDIT

I did some timing to confirm answers. Here is an example of code used, this is for what I call Case1

import time

def function(condition):
    test = ['1','2','3','4']
    if condition == 'condition1':
       pass

    if condition == 'condition2':
       pass

    if condition == 'condition3':
       pass

start_time = time.time()
num_iterations = 10000
for i in range(num_iterations):
    function('condition1')
    function('condition2')
    function('condition3')

print("Case1 for %d iterations" %(num_iterations))    
print("--- %s seconds ---" % (time.time() - start_time))

I followed the same pattern for case2 which is related to case2 in my question and case3 represent the logic used in my answer. As you can see, only list creation is impacting the time for the tests.

Results

Case1 for 10000 iterations
--- 0.01559591293334961 seconds ---

Case2 for 10000 iterations
--- 0.015594244003295898 seconds ---

Case3 for 10000 iterations
--- 0.015636682510375977 seconds ---

Case1 for 1000000 iterations
--- 1.1179990768432617 seconds ---

Case2 for 1000000 iterations
--- 0.9396021366119385 seconds ---

Case3 for 1000000 iterations
--- 1.0606272220611572 seconds ---

Summary

We can conclude all approaches are pretty much the same to achieve efficiency. Even though for pure performance Case2 seems the best (which is logic when you think about it). Still, I think that we should refer to this answer.

scharette
  • 9,437
  • 8
  • 33
  • 67
  • You should do some timings and see if your new solution is faster or slower than case 1. I think you might be surprised. – RobertB Oct 18 '17 at 20:41
0

Similar to @RobertB answer, but using functional programming:

from functools import partial

def condition1():
  print('Hello first case')

def condition2(test):
  print('Hello second case. Test var is: {0}'.format(test))

def condition3(test):
  print('Hello thrid case. Test var is: {0}'.format(test))

def function(condition):

  test = []

  if condition in ('condition2', 'condition3'):
    test = ['1','2','3','4']

  conditions = {
    'condition1': condition1,
    'condition2': partial(condition2, test),
    'condition3': partial(condition3, test)
  }

  action = conditions.get(condition, lambda: print(NotImplemented))
  action()

function('condition')
function('condition1')
function('condition2')
function('condition3')
Yaroslav Surzhikov
  • 1,568
  • 1
  • 11
  • 16
  • @scharette i must admit that my example is far from ideal because over and over dictionary with mappings is recreated, but as an example of organizing conditions - it's good enough – Yaroslav Surzhikov Oct 19 '17 at 00:55