You actually can use the partial function to modify the default keyword arguments so that they don't need to be explicitly set later. The trick is to set the original function to be equal to the resulting partial function.
Generic example of partial
Define our generic function
def originalFunction(x,defaultValue='DEFAULT'):
print('x:', x, 'defaultValue:', defaultValue,)
## shows the original function outputing x and the defaultValue
originalFunction(1)
x: 1 defaultValue: DEFAULT
Use partial to overwrite function signature (THESE TOP TWO LINES ARE THE ONLY IMPORTANT PART)
from functools import partial
originalFunction = partial(originalFunction,defaultValue=999) ## Overwites the function with partial
## Use function with updated defaults
originalFunction(1)
x: 1 defaultValue: 999
To reset the default values you can either repeat the call to partial
originalFunction = partial(originalFunction,defaultValue="whateverYouWant") ##sets default value
## Use function with updated defaults
originalFunction(3)
x: 3 defaultValue: whateverYouWant
Or you can modify the partial function’s keywords dictionary like this
## Show dict containing keywords
originalFunction.keywords
{'defaultValue': 'whateverYouWant'}
originalFunction.keywords['defaultValue'] = "ThePossabilitesAreEndless" ##sets default value
## Use function with updated defaults
originalFunction("x")
x: x defaultValue: ThePossabilitesAreEndless
To revert to the original kwargs, you can overwrite the variable to the original function address found in .func
originalFunction = originalFunction.func ##resets the default
## shows the original function outputing x and the defaultValue
originalFunction(3)
x: 3 defaultValue: DEFAULT
Implamenting Partial in Original Question
Showing normal use of our function "spell"
import numpy as np
import itertools
import pandas as pd
def spell(X, kind='wet', how='mean', threshold=0.5):
print(kind,how,threshold) ##adding print to show kwargs
if kind=='wet':
condition = X>threshold
else:
condition = X<=threshold
length = [sum(1 if x==True else nan for x in group) for key,group in itertools.groupby(condition) if key]
if not length:
res = 0
elif how=='mean':
res = np.mean(length)
else:
res = np.max(length)
return res
np.random.seed(1324)
idx = pd.date_range(start='1960-01-01', periods=100, freq='d')
values = np.random.random(100)
df = pd.DataFrame(values, index=idx)
#Apply function
print(df.resample('M').agg(lambda x: spell(x)).to_markdown())
wet mean 0.5
wet mean 0.5
wet mean 0.5
wet mean 0.5
| | 0 |
|:--------------------|--------:|
| 1960-01-31 00:00:00 | 1.55556 |
| 1960-02-29 00:00:00 | 1.5 |
| 1960-03-31 00:00:00 | 1.77778 |
| 1960-04-30 00:00:00 | 6 |
updating spell kwargs with functools partial function
from functools import partial
spell = partial(spell,kind='dry', how='max', threshold=0.6)
#Apply function
print(df.resample('M').agg(lambda x: spell(x)).to_markdown())
dry max 0.6
dry max 0.6
dry max 0.6
dry max 0.6
| | 0 |
|:--------------------|----:|
| 1960-01-31 00:00:00 | 4 |
| 1960-02-29 00:00:00 | 5 |
| 1960-03-31 00:00:00 | 5 |
| 1960-04-30 00:00:00 | 2 |
Using functools vs editing __defaults__
The functools partial method is also useful when you are using a function that you have imported because functions within modules do not have __defaults__ like functions that are described in the current python script
Because we defined originalFunction, we can access the default kwargs using .defaults
##defaults from function we wrote
originalFunction.__defaults__
('DEFAULT',)
##editing defaults
originalFunction.__defaults__ = ('Edited',)
originalFunction(1)
x: 1 defaultValue: Edited
This method won't work on an imported module like np.array
np.array.__defaults__ ## module functions do not have __defaults__
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_15784/3587101803.py in <module>
----> 1 np.array.__defaults__ ## module functions do not have __defaults__
AttributeError: 'builtin_function_or_method' object has no attribute '__defaults__'
so instead we can use the partial function to update the np.array kwargs
## how numpy.array normally works
np.array([1,1,1]).T
array([1, 1, 1])
## modify numpy.array ndmin default
from functools import partial
np.array = partial(np.array,ndmin = 2)
## how it works now
np.array([1,1,1]).T
array([[1],
[1],
[1]])
Using functools vs passing on kwargs
It is possible to pass on args and kwargs to another function instead of using partial
## definition of originalFuction
def originalFunction(x,defaultValue='DEFAULT'):
print('x:', x, 'defaultValue:', defaultValue,)
## shows the original function outputing x and the defaultValue
originalFunction(1)
x: 1 defaultValue: DEFAULT
We can pass on the incoming args and kwargs to another function while overwriting the defaultValue kwarg with the following method
def newFunction(*args,defaultValue='NEW_DEFAULT',**kwargs):
return originalFunction(*args,defaultValue = defaultValue, **kwargs)
newFunction(5)
x: 5 defaultValue: NEW_DEFAULT
One downside to this approach, is that you MUST NOT use this new function signature to overwrite the original function!
## add display() to show infinite loop
def newFunction(*args,defaultValue='NEW_DEFAULT',**kwargs):
display('Passing vals to OriginalFunction')
return originalFunction(*args,defaultValue = defaultValue, **kwargs)
newFunction(5)
'Passing vals to OriginalFunction'
x: 5 defaultValue: NEW_DEFAULT
If you do, it will create an INFINITE LOOP
originalFunction = newFunction
originalFunction(1)
'Passing vals to OriginalFunction'
'Passing vals to OriginalFunction'
'Passing vals to OriginalFunction'
'Passing vals to OriginalFunction'
'Passing vals to OriginalFunction'
'Passing vals to OriginalFunction'
'Passing vals to OriginalFunction'
'Passing vals to OriginalFunction'
'Passing vals to OriginalFunction'
'Passing vals to OriginalFunction'