The killer case here is that if m > 2*k, we get foo taking as input something it produced as output on a prior call. That means that we can't simply evaluate s statically and iterate blithely over the known elements -- the elements after s[max(k, n)] are unknown at the initial evaluation, so feeding one of those to foo is a dynamic quantity.
Imagine that foo returns something dependent on the initial value and something random; it's easy enough to see that the propagation from one part of the list to another doesn't work statically. For instance, have foo append a random hex digit, and let's try both ideas in the original posting:
import random
def foo(chr_list):
inventory = "0123456789abcdef"
return chr_list[0] + random.choice(inventory)
k = 3
m = 10
n = 16
s = [''] * n
s[0:k] = ['X'] * k
print "1 s init", s
for i in range(k,m):
s[i] = foo(s[i-k:i])
print "1 s done", s
print
s = [''] * n
s[0:k] = ['X'] * k
print "2 s init", s
s = [foo(s[i-k:i]) for i in range(k,m)]
print "2 s done", s
Output:
1 s init ['X', 'X', 'X', '', '', '', '', '', '', '', '', '', '', '', '', '']
1 s done ['X', 'X', 'X', 'X7', 'Xd', 'X9', 'X74', 'Xd6', 'X9d', 'X741', '', '', '', '', '', '']
2 s init ['X', 'X', 'X', '', '', '', '', '', '', '', '', '', '', '', '', '']
2 s done ['X8', 'X2', 'X9', 'f', 'd', '0', '7']
In summary, I don't think it's worth putting this into a one-liner.