2

Right now I use a list to store altered string and return a string with .join()

def applyCoder(text, coder):
    l = []
    for i in text:
        if i in coder:
            l.append(coder[i])
        else:
            l.append(i)
    return ''.join(l)

# example output, shifts all letters by 3 in a string
# print applyCoder("Hello, world!", buildCoder(3))
# buildCoder(3) returns dictionary, e.g. {'A': 'D', ...}
# >>> Khoor, zruog!

Is there a faster method to change and return a string?

1
  • Fix your return statement Commented Apr 18, 2013 at 8:59

1 Answer 1

4

This should be as fast as possible:

''.join([coder[i] if i in coder else i for i in text])

List comprehensions are much more optimized in Python compared to for loops which bear great overhead. I've passed a list comprehension as opposed to a generator in ''.join because it must know the length of it's input in advance before joining. If you give it a generator it has to make that into a list anyway (which is a bit slower).

Actually you can simplify this even further, which should be even faster (this actually performs slower than the above method due to the method call)

''.join([coder.get(i,i) for i in text])

Timings:

def applyCoder(text, coder):
    L = []
    for i in text:
        if i in coder:
            L.append(coder[i])
        else:
            L.append(i)
    return ''.join(L)

def list_comp(text, coder):
    return ''.join([coder[i] if i in coder else i for i in text])

def list_comp2(text, coder):
    return ''.join([coder.get(i,i) for i in text])

from timeit import timeit
from string import ascii_letters
d = dict(zip(ascii_letters, ascii_letters[3:] + ascii_letters[-3:]))


print timeit(stmt='applyCoder("Hello, world!", d)',
             setup='from __main__ import applyCoder, d;')

print timeit(stmt='list_comp("Hello, world!", d)',
             setup='from __main__ import list_comp, d;')    

print timeit(stmt='list_comp2("Hello, world!", d)',
             setup='from __main__ import list_comp2, d;')

print timeit(stmt='applyCoder("Hello, world!"*10, d)',
             setup='from __main__ import applyCoder, d;')

print timeit(stmt='list_comp("Hello, world!"*10, d)',
             setup='from __main__ import list_comp, d;')


print timeit(stmt='list_comp2("Hello, world!"*10, d)',
             setup='from __main__ import list_comp2, d;')

Results:

''' Test 1 '''
5.0159105417    # applyCoder
3.41502481461   # listcomp1
4.76796932292   # listcomp2

''' Test 2 '''
34.9718502631   # applyCoder
22.0451702661   # listcomp1
34.1682597928   # listcomp2

It appears that the method call to coder.get completely negates the advantages of the list comprehension. I did predict it might be slower than listcomp1 because of this but I didn't think it would have this much of an impact. Anyway the list comprehension still wins.

Update: If you modify list_comp2 like so:

def list_comp2(text, coder):
    coder_get = coder.get
    return ''.join([coder_get(i,i) for i in text])

The times improve drastically:

from 4.76796932292 (1st test) -> 3.95217394948

and 34.1682597928 (2nd test) -> 27.1162974624

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

7 Comments

Average: 0.00015 vs 0.00011(yours) with 10,000 calls.
@user2144553 those must be tiny inputs, (I'm assuming those are seconds), can you try it on larger ones? Also if you could tell me the timings of both of mine EDIT: I'll try some timings as well
I used two and half average sentence long string. Here is my setup: pastie.org/7641446
@user2144553 Doing some timings now, it appears my first method is much faster than yours but the second one is slow, will update shortly
@user2144553 Updated: I did my tests using timeit which is much more accurate
|

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.