Note: I'm asking if there's One Pythonic Way to do this (using default args seems less Pythonic than using partial) and if there are significant limitations to either method (the "cost" - I wouldn't expect timing to differ significantly but perhaps there are other limitations I'm not seeing that tilt the balance towards one methodology versus the other).
I'm trying to understand the costs of using 'partial' in late-binding situations where a lambda is not feasible. I've created some example code based on this guide to exemplify this.
The following doesn't work as intended due to late binding:
def create_thingies():
thingies = []
for i in range(1,6):
def thingy(x):
print("Some output", i)
return i ** (x * i)
thingies.append(thingy)
return thingies
results=[]
for thingy in create_thingies():
results.append(thingy(2))
print(results)
Output:
Some output 5
Some output 5
Some output 5
Some output 5
Some output 5
[9765625, 9765625, 9765625, 9765625, 9765625]
Using 'partial' we avoid that problem, but at what cost?
from functools import partial
def create_thingies():
thingies = []
for i in range(1,6):
def thingy(i, x):
print("Some output", i)
return i ** (x * i)
thingies.append(partial(thingy, i))
return thingies
results=[]
for thingy in create_thingies():
results.append(thingy(2))
print(results)
Output:
Some output 1
Some output 2
Some output 3
Some output 4
Some output 5
[1, 16, 729, 65536, 9765625]
I've seen much discussion about lambda vs partial here, but in cases where a lambda wouldn't work well (a very complex function) if at all (function with more than expressions) is partial the way to go or is there a better way short of coercing this into a lambda expression?
Using partial, there is no need to define thingy once for each value of i, since thingy does not use any free/global variables, but just its parameters.
from functools import partial
def thingy(i, x):
print("Some output", i)
return i ** (x * i)
thingies = [partial(thingy, i) for i in range(1,6)]
results = [th(2) for th in thingies]
print(results)
As for cost, you should profile to see if the performance is acceptable.
Here's a quick test to compare 3 options:
import timeit
# The fastest: define a function using a default parameter value
print timeit.timeit('results = [ th(2) for th in create_thingies()]', '''
def create_thingies():
thingies = []
for i in range(1,6):
def thingy(x,i=i):
#print("Some output", i)
return i ** (x * i)
thingies.append(thingy)
return thingies
''')
# The slowest, but IMO the easiest to read.
print timeit.timeit('results = [ th(2) for th in create_thingies()]', '''
def create_thingies():
from functools import partial
def thingy(i,x):
#print("Some output", i)
return i ** (x * i)
return [partial(thingy, i) for i in range(1,6)]
''')
# Only a little slower than the first
print timeit.timeit('results = [ th(2) for th in create_thingies()]', '''
def create_thingies():
def make_thingy(i):
def thingy(x):
#print("Some output", i)
return i ** (x * i)
return thingy
thingies = [make_thingy(i) for i in range(1,6)]
return thingies
''')
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With