pythonlistinline

I did some inline code for an assignment and I was wondering if using it would be more efficient with or without


The goal of the assignment was to initialize an empty list called rand_array then fill it with 10 unique random numbers

I figured out the assignment normally as per the teacher's directions:

import random

rand_array = []
while len(rand_array) != 10:
    lmnt = random.randint(1, 15)
    if lmnt not in rand_array:
        rand_array.append(lmnt)

print(rand_array)

I modified it later to have inline to make it more efficient since my teacher didn't like me using inline:

import random

rand_array = []
while len(rand_array) < 10:
    lmnt = random.randint(1, 15)
    rand_array.append(lmnt) if lmnt not in rand_array else None

I started questioning if this was actually more efficient and if I could still make it better if I take off the requirement of initializing an empty list. So, the main question is: "Is the second version more efficient than the first?" and also "Can I make the second version even more efficient?"

Also, before anyone says, "This is such a small assignment, it doesn't matter", if I use lots of inline code in the future, I need to know if the way I'm using it is more efficient than non-inline code.

Also I apologize in advance if anything I have typed is inaccurate.


Solution

  • Technically, your inline is actually a little bit less efficient. It forces Python to:

    1. (Briefly) store a None value in the True case
    2. Skip past the false case in the true case.

    This isn't just theoretical; the CPython bytecode for the second approach (from dis) has extra instructions for precisely that reason.

    This is a really small difference, though. More importantly, the second approach is hard to read. Ternary expressions are for essentially one use case:

    1. You have an if statement AND
    2. It needs and else statement AND
    3. The only thing this if-else does is determine the value of a variable.

    Your use case only passes the first test; it neither needs an else statement nor creates a variable based on the result of the if. For that reason, a ternary is an unintuitive choice. (I actually had to read the code a couple times to figure out why it didn't fill your list with None values.) The if statement is more readable and pythonic.

    The actual best solution is to do neither of these. Instead, use random.sample:

    import random
    rand_array = random.sample(range(1, 16), 10)
    

    This has three advantages: it's more readable, it avoids reinventing the wheel, and it's faster. You can compare execution speed using this script:

    import random
    from timeit import timeit
    
    def if_based():
        rand_array = []
        while len(rand_array) < 10:
            lmnt = random.randint(1, 15)
            if lmnt not in rand_array:
                rand_array.append(lmnt)
        return rand_array
    
    def sample_based():
        rand_array = random.sample(range(1, 16), 10)
        return rand_array
    
    print(f"If: {timeit(if_based)}")
    print(f"Sample: {timeit(sample_based)}")
    

    The difference is pretty drastic on my machine.

    If: 6.120091282999965
    Sample: 3.0449935650001407