fix(core): return deepcopy from get_params to prevent state mutation#524
fix(core): return deepcopy from get_params to prevent state mutation#524pankajbaid567 wants to merge 4 commits into
Conversation
Fixes dswah#522 where get_params exposed direct references to nested objects like Term and TermList causing params['terms'][0].lam = ... to silently mutate the underlying estimator configuration. Returns copy.deepcopy of parameter dictionary and objects.
|
Nice fix and good regression test! Also thanks for picking up the issue reported in #522. One potential concern is the use of |
Address PR review feedback: replace blanket copy.deepcopy() on all parameter values with a _maybe_deepcopy() helper that only copies mutable types (list, dict, ndarray, Core subclasses), returning immutable scalars (int, float, bool, str, None) as-is. This avoids unnecessary overhead when get_params() is called frequently during model selection (e.g. GridSearchCV). Also add tests verifying scalar identity and list copy behavior.
|
Thanks for the thoughtful review, @dkstlzk ! Great point deepcopy on every parameter is indeed wasteful, especially in model selection loops like GridSearchCV where get_params() is called frequently. Also added two new tests to verify: |
PR Title: Fix:
get_paramsexposes mutable nested objects (#522)Description
This PR fixes issue #522, where
GAM.get_params()exposes direct references to theTermandTermListobjects through the parameter dictionary. Because of this, modifications to the returned dictionary such asparams["terms"][0].lam = ...would silently mutate the underlying model's state, violating the scikit-learn estimator contract.This is fixed by updating
Core.get_params()to returncopy.deepcopy()of the dictionary and nested lists/objects rather than returning direct referential dictionaries.Changes Made
pygam/core.py: Addedimport copyand wrapped returned variables fromCore.get_params()incopy.deepcopy().pygam/tests/test_get_params.py: Introduced a new test file validating that external mutations to the dictionary returned byget_paramsdo not change the underlying structure.Testing
test_get_params.py(verified it appropriately captures parameter changes without model changes).pytest pygam/tests); all passes.clonelogic.Closes #522