Изменение аргументов функции по умолчанию в python

у меня есть функция, которая преобразует массив numpy в массив, содержащий True или False на основе условия, затем группирует True или False записи, которые соседствуют друг с другом, и вычисляет длину каждой группы. Это для определения продолжительности сухих или влажных периодов в данном месяце данных осадков.

это функция:

import itertools
def spell(X, kind='wet', how='mean', threshold=0.5): 

    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

таким образом, в основном есть возможность определить среднюю длину или максимальную длину влажных или сухих заклинаний учитывая массив данных numpy осадков с параметрами по умолчанию, установленными на среднюю длину влажных заклинаний.

Я использую эту функцию с пандами, чтобы применить ее к каждому месяцу исторической записи:

#Create example dataframe
np.random.seed(1324)
idx = pd.DatetimeIndex(start='1960-01-01', periods=100, freq='d')
values = np.random.random(100)
df = pd.DataFrame(values, index=idx)

#Apply function
df.resample('M', how=spell)

и я получаю:

0
1960-01-31  1.555556
1960-02-29  1.500000
1960-03-31  1.777778
1960-04-30  6.000000

что идеально, однако я хочу иметь возможность изменять значения по умолчанию этой функции несколько на лету, чтобы я мог использовать другие варианты с df.resample (). Я заглянул в functools.partial() однако это только решение для случаев, когда входные аргументы явно заданы ie. spell(kind='dry', how='max', threshold=0.7). Есть ли способ изменить аргументы по умолчанию функции таким образом, чтобы их не нужно было явно устанавливать после слов, чтобы я мог использовать его с df.resample()?

2 ответов


значения по умолчанию для функции хранятся в функции func_defaults атрибут, который является кортежем значений, которые соединяются с трейлинг элементы функции func_code.co_varnames кортежа. Например:

>>> def foo(x, y=5):
...    return x, y
...
>>> foo(10)
(10, 5)
>>> foo.func_code.co_varnames
('x', 'y')
>>> foo.func_defaults
(5,)
>>> foo.func_defaults = (7,)
>>> foo(10)
(10, 7)

вы даже можете дать параметру значение по умолчанию после факта:

>>> foo.func_defaults = (2, 3)
>>> foo()
(2, 3)

предупреждение: Я думал (ab)использовать mock библиотека, позволяющая временно переопределять функции по умолчанию, аналогично мой последний ответ. Однако, похоже, оставить значения по умолчанию равными None после этого, что означает, что либо есть ошибка (или я неправильно понимаю поведение)mock, или что возиться с такими функциями немного опасно.

def foo(x=5):
    return x

assert foo() == 5
with mock.patch.object(foo, 'func_defaults', (10,)):
    assert foo() == 10

assert foo() == 5  # Oops; I'm observing foo.func_defaults to be None now

ручное сохранение и восстановление значений по умолчанию, похоже, работает нормально, хотя, как и следовало ожидать.

orig_defaults = foo.func_defaults
foo.func_defaults = (10,)
assert foo() == 10
foo.func_defaults = orig_defaults
assert foo() == 5

это звучит как работа для оболочки функции!

def spellwrapper(newkind, newhow, newthreshold):
    def wrapped_spell_func(X):
        spell(X, kind=newkind, how=newhow, threshold=newthreshold)
    return wrapped_spell_func

вы бы назвали эту функцию с

new_spell_func = spellwrapper(newkind, newhow, newthreshold)

и он вернет завернутую версию spell функция, которая использует ваши новые аргументы как" по умолчанию " вместо тех, которые созданы в определении функции. Тогда вы бы использовали

df.resample('M', how=new_spell_func)