The following works but it won't be fast as it's operating as a loop over every row, the key thing here is to pass param axis=1 to operate row-wise and we can than access each column's value:
In [46]:
df['C'] = df.apply(lambda x: x['A'][:x['B']], axis=1)
df
Out[46]:
A B C
0 Jimmy 4 Jimm
1 Tommy 2 To
2 Karl 3 Kar
3 Jane 1 J
So just to look at your attempts and why they don't work: df['C'] = df['A'].str[:df['B']] this will fail as you are trying to subscript every element in column A by passing a series, it has to be some constant int value unfortunately, it's a nice idea but won't work.
l = (lambda x,y: str(x)[:y])
df[['A','B']].apply(l)
This won't work because the result of df[['A', 'B']] is a just your original df, you've not specified the axis to operate on so the default is 0 which is column wise, in effect your lambda now fails as only a single param is passed which on the first iteration will be df['A'], so the only way to make this work is to operate row-wise by passing param axis=1. I can't currently think of a better way here at the moment.