I would use a completly different approach using string interpolation and the
f-string
def buildprint(n,i,char):
start = "".join(['.']*(i))
mid = char
end = "".join(['.']*(n - i-1))
print(start+mid+end)
def build_f_string(total_char, dots, char):
total_char -= dots + 1
print(f'{"." * dots}{char}{"." * total_char}')
test_1 = (10, 3, "j")
buildprint(*test_1)
build_f_string(*test_1)
One Liner
build_f_string = lambda total_char, dots, char : print(f'{"." * dots}{char}{"." * (total_char - (dots + 1 ))}')
build_f_string(*test_1)
Performance
Make a timer function
from time import perf_counter
from functools import wraps
def timer(runs):
def _timer(f,):
@wraps(f)
def wrapper(*args, **kwargs):
start = perf_counter()
for test in range(runs):
res = f(*args, **kwargs)
end = perf_counter()
print(f'{f.__name__}: Total Time: {end - start}')
return res
return wrapper
return _timer
Decorate our functions
TEST_RUNS = 100_000
@timer(runs=TEST_RUNS)
def buildprint(n, i, char):
start = "".join(['.'] * (i))
mid = char
end = "".join(['.'] * (n - i - 1))
return start + mid + end
@timer(runs=TEST_RUNS)
def build_f_string(total_char, dots, char):
return f'{"." * dots}{char}{"." * (total_char - dots + 1)}'
Run Tests
test_1 = (10, 3, "j")
buildprint(*test_1)
build_f_string(*test_1)
# Output
# buildprint: Total Time: 0.06150109999999999
# build_f_string: Total Time: 0.027191400000000004
Going to next level
We can use Python Deque
Deques are a generalization of stacks and queues (the name is pronounced “deck” and is short for “double-ended queue”). Deques support thread-safe, memory efficient appends and pops from either side of the deque with approximately the same O(1) performance in either direction.
def build_withdeque(n, i, char):
hyper_list = deque([char]) # initialize the deque with the char, is in the middle already
hyper_list.extendleft(["." for _ in range(i)]) # Add left side
hyper_list.extend(["." for _ in range(n - i - 1)])
return "".join(hyper_list)
Running test
from collections import deque
TEST_RUNS = 10_00_000
@timer(runs=TEST_RUNS)
def build_withdeque(n, i, char):
hyper_list = deque([char]) # initialize the deque with the char, is in the middle already
hyper_list.extendleft(["." for _ in range(i)]) # Add left side
hyper_list.extend(["." for _ in range(n - i - 1)])
return "".join(hyper_list)
test_1 = (10, 3, "j")
buildprint(*test_1)
build_f_string(*test_1)
build_withdeque(*test_1)
# buildprint: Total Time: 0.546178
# build_f_string: Total Time: 0.29445559999999993
# build_withdeque: Total Time: 1.4074019
Still not so good..
Pre build a list
@timer(runs=TEST_RUNS)
def build_list_complete(n, i, char):
return ''.join(["." * i, char, "." * (n - i - 1)])
test_1 = (10, 3, "j")
buildprint(*test_1)
build_f_string(*test_1)
build_withdeque(*test_1)
build_list_complete(*test_1)
# buildprint: Total Time: 0.5440142
# build_f_string: Total Time: 0.3002815999999999
# build_withdeque: Total Time: 1.4215970999999998
# build_list_complete: Total Time: 0.30323829999999985
Final Result
# TEST_RUNS = 10_00_000
test_1 = (10, 3, "j")
buildprint(*test_1)
build_f_string(*test_1)
build_withdeque(*test_1)
build_list_complete(*test_1)
# buildprint: Total Time: 0.6512364
# build_f_string: Total Time: 0.2695955000000001
# build_withdeque: Total Time: 14.0086889
# build_list_complete: Total Time: 3.049139399999998
Wait no so fast
As @MisterMiyagi point out, using return "." * i + char + ("." * (n - i + 1))
could be equivalent than using return f'{"." * dots}{char}{"." * (total_char - dots + 1)}'
.
However it is actually Faster.
Look at this:
def SuperHyperMegaUltraFastButBasicallyLikeFStringThatMisterMiyagiSayd(n, i, char):
return "." * i + char + ("." * (n - i + 1))
Run some test with 100_00_000 repetitions.
@timer(runs=TEST_RUNS)
def SuperHyperMegaUltraFastButBasicallyLikeFStringThatMisterMiyagiSaid(n, i, char):
return "." * i + char + ("." * (n - i + 1))
test_1 = (10, 3, "j")
buildprint(*test_1)
build_f_string(*test_1)
build_withdeque(*test_1)
build_list_complete(*test_1)
buildprint: Total Time: 5.3067210000000005
build_f_string: Total Time: 2.721678
build_withdeque: Total Time: 14.302031600000001
build_list_complete: Total Time: 3.0364287999999995 SuperHyperMegaUltraFastButBasicallyLikeFStringThatMisterMiyagiSayd: Total Time: 2.440598699999999
buildprint: Total Time: 5.3067210000000005
build_f_string: Total Time: 2.721678
build_withdeque: Total Time: 14.302031600000001
build_list_complete: Total Time: 3.0364287999999995
SuperHyperMegaUltraFastButBasicallyLikeFStringThatMisterMiyagiSayd
Total Time: 2.440598699999999
The f-string string concatenation is the clear winner here, untill proven otherwise