1

I have a basic "napkin algorithm" that takes a list of tuples (with 2 items) and changes them. Here it is in english:

  1. if item 2 is a list, change item 1 to be string x
  2. otherwise change item 1 to be string y

Here is a quick function I put together that does this:

def f(objects):
    list_type = "string_x"
    other = "string_y"
    hold = []
    for x in objects:
        if isinstance(x[1],list):
            hold.append((list_type,x[1]))
        else:
            hold.append((other,x[1]))
    return hold

I do not believe that this is the most efficient way. This algorithm is very simple and is only a basic statement. What would be a more efficient way of doing this?

3
  • Can you give an example of what you're passing as objects? Commented Jan 17, 2014 at 19:31
  • 3
    By "efficient" do you mean "fast" or "readable" or something different? What makes you think it isn't efficient? Commented Jan 17, 2014 at 19:31
  • 2
    No matter how you're going to do it, map, list comprehension or otherwise, it will have to be some sort of loop Commented Jan 17, 2014 at 19:31

4 Answers 4

2

You can make this a little shorter by using an if expression:

def f(objects):
    list_type = "string_x"
    other = "string_y"
    hold = []
    for x in objects:
        hold.append((list_type if isinstance(x[1], list) else other, x[1]))
    return hold

And then it's pretty easy to turn into a comprehension, which is even shorter, and probably more readable, and a little faster:*

def f(objects):
    list_type = "string_x"
    other = "string_y"
    return [(list_type if isinstance(x[1], list) else other, x[1]) 
            for x in objects]

And really, I'm not sure those local variables are making things any clearer:**

def f(objects):
    return [("string_x" if isinstance(x[1], list) else "string_y", x[1]) 
            for x in objects]

Meanwhile, if the only thing you're going to do with the returned list is to iterate over it (e.g., because this is just one in a chain of transformations), you shouldn't be returning a list at all. Either yield each value, return a genexpr instead of a listcomp, or (if you have Python 3.3+) get the best of both worlds:

def f(objects):
    yield from (("string_x" if isinstance(x[1], list) else "string_y", x[1]) 
                for x in objects)

* You are still doing the exact same loop, so you have the exact same algorithmic complexity. However, both the looping and the list appending, are happening with custom bytecodes that take some shortcuts, making each iteration a bit more efficient. (The "custom bytecodes" detail is of course specific to CPython and other bytecode-compatible implementations like PyPy, but in general, any implementation at least could take shortcuts on list comprehensions.)

** This last very might be slightly faster, because it's loading a constant onto the stack instead of a local variable. Then again, it might also have slightly worse cache locality. If it really matters, test it and see.

Sign up to request clarification or add additional context in comments.

3 Comments

wow I can't believe I forgot you can do if and else in a list comprehension
@RyanSaxe: The "in a list comprehension" part isn't really relevant here; that's a general-purpose expression you can do anywhere any expression is allowed. (There's also a comprehension-specific use of if, for the filter clause at the end, but I'm not using that here.)
thanks for the clarification. I will mark this answer as correct in 10 minutes when I am permitted
0

Using list comprehension would be more efficient and pythonic (if I'm not wrong):

[("string_x" if isinstance(x[1], list) else "string_y", x[1]) for x in objects]

2 Comments

You can't map an expression, only a function. Maybe you wanted a list comprehension? Or map with a lambda?
Fixed. My obsession with map.
0

If you need to check and change all items in a list you need a loop, and you cant do it otherwise.

you can make it a little faster using list comprehension which reduces the append function all for each list item.

def f(objects):
    return [('string_x',x[1]) if isinstance(x[1],list) else ('string_y',x[1]) for x in objects]

But the performance difference will only be visible if you are using a large list

Comments

0

I kind of like doing it this way, but I might be a silly person:

strings = 'string_x', 'string_y'

[(strings[isinstance(x[1],list)], x[1]) for x in objects]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.