<h2 dir="auto">Environment info</h2>
<ul dir="auto">
<li>Python 3.7.7 (default, Mar 26 2020, 15:48:22)</li>
<li>[GCC 7.3.0] :: Anaconda, Inc. on linux</li>
<li>Operating System: Ubuntu 16.04.6 LTS / running on Docker</li>
<li>CPU/GPU model: CPU Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz</li>
<li>C++ compiler version:</li>
<li>CMake version:</li>
</ul>
<h4 dir="auto">Steps/Code to Reproduce</h4>
<p dir="auto">Suppose you create a class like the following to store variables passed in arguments as temporary local attributes for later use.</p>
<p dir="auto">At version <code class="notranslate">scikit-learn==0.22.2.post1</code>, this original estimator <code class="notranslate">MyClass</code> print works fine.</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="&gt;&gt;&gt; from sklearn.base import BaseEstimator
&gt;&gt;&gt; 
&gt;&gt;&gt; class MyClass(BaseEstimator):
...     def __init__(self, objective='logloss', **kwrgs):
...         self.objective = objective
...         self._other_params = kwrgs
...         
...     def get_params(self, deep=True):
...         params = super().get_params(deep)
...         params.update(self._other_params)
...         return params
... 
&gt;&gt;&gt; clf = MyClass(metric='auc')
&gt;&gt;&gt; 
&gt;&gt;&gt; print(clf)
MyClass(metric='auc', objective='logloss')"><pre class="notranslate"><span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> <span class="pl-k">from</span> <span class="pl-s1">sklearn</span>.<span class="pl-s1">base</span> <span class="pl-k">import</span> <span class="pl-v">BaseEstimator</span>
<span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> 
<span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> <span class="pl-k">class</span> <span class="pl-v">MyClass</span>(<span class="pl-v">BaseEstimator</span>):
...     <span class="pl-k">def</span> <span class="pl-en">__init__</span>(<span class="pl-s1">self</span>, <span class="pl-s1">objective</span><span class="pl-c1">=</span><span class="pl-s">'logloss'</span>, <span class="pl-c1">**</span><span class="pl-s1">kwrgs</span>):
...         <span class="pl-s1">self</span>.<span class="pl-s1">objective</span> <span class="pl-c1">=</span> <span class="pl-s1">objective</span>
...         <span class="pl-s1">self</span>.<span class="pl-s1">_other_params</span> <span class="pl-c1">=</span> <span class="pl-s1">kwrgs</span>
...         
...     <span class="pl-k">def</span> <span class="pl-en">get_params</span>(<span class="pl-s1">self</span>, <span class="pl-s1">deep</span><span class="pl-c1">=</span><span class="pl-c1">True</span>):
...         <span class="pl-s1">params</span> <span class="pl-c1">=</span> <span class="pl-en">super</span>().<span class="pl-en">get_params</span>(<span class="pl-s1">deep</span>)
...         <span class="pl-s1">params</span>.<span class="pl-en">update</span>(<span class="pl-s1">self</span>.<span class="pl-s1">_other_params</span>)
...         <span class="pl-k">return</span> <span class="pl-s1">params</span>
... 
<span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> <span class="pl-s1">clf</span> <span class="pl-c1">=</span> <span class="pl-v">MyClass</span>(<span class="pl-s1">metric</span><span class="pl-c1">=</span><span class="pl-s">'auc'</span>)
<span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> 
<span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> <span class="pl-en">print</span>(<span class="pl-s1">clf</span>)
<span class="pl-v">MyClass</span>(<span class="pl-s1">metric</span><span class="pl-c1">=</span><span class="pl-s">'auc'</span>, <span class="pl-s1">objective</span><span class="pl-c1">=</span><span class="pl-s">'logloss'</span>)</pre></div>
<p dir="auto">But In scikit-learn==0.23.0, I got the following <code class="notranslate">KeyError</code> that the instance has not metric key.</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="KeyErrorTraceback (most recent call last)
&lt;ipython-input-31-2e0afdc6afe0&gt; in &lt;module&gt;
     14 clf = MyClass(metric='auc')
     15 
---&gt; 16 print(clf)

/opt/conda/lib/python3.7/site-packages/sklearn/base.py in __repr__(self, N_CHAR_MAX)
    277             n_max_elements_to_show=N_MAX_ELEMENTS_TO_SHOW)
    278 
--&gt; 279         repr_ = pp.pformat(self)
    280 
    281         # Use bruteforce ellipsis when there are a lot of non-blank characters

/opt/conda/lib/python3.7/pprint.py in pformat(self, object)
    142     def pformat(self, object):
    143         sio = _StringIO()
--&gt; 144         self._format(object, sio, 0, 0, {}, 0)
    145         return sio.getvalue()
    146 

/opt/conda/lib/python3.7/pprint.py in _format(self, object, stream, indent, allowance, context, level)
    159             self._readable = False
    160             return
--&gt; 161         rep = self._repr(object, context, level)
    162         max_width = self._width - indent - allowance
    163         if len(rep) &gt; max_width:

/opt/conda/lib/python3.7/pprint.py in _repr(self, object, context, level)
    391     def _repr(self, object, context, level):
    392         repr, readable, recursive = self.format(object, context.copy(),
--&gt; 393                                                 self._depth, level)
    394         if not readable:
    395             self._readable = False

/opt/conda/lib/python3.7/site-packages/sklearn/utils/_pprint.py in format(self, object, context, maxlevels, level)
    168     def format(self, object, context, maxlevels, level):
    169         return _safe_repr(object, context, maxlevels, level,
--&gt; 170                           changed_only=self._changed_only)
    171 
    172     def _pprint_estimator(self, object, stream, indent, allowance, context,

/opt/conda/lib/python3.7/site-packages/sklearn/utils/_pprint.py in _safe_repr(object, context, maxlevels, level, changed_only)
    412         recursive = False
    413         if changed_only:
--&gt; 414             params = _changed_params(object)
    415         else:
    416             params = object.get_params(deep=False)

/opt/conda/lib/python3.7/site-packages/sklearn/utils/_pprint.py in _changed_params(estimator)
     96     init_params = {name: param.default for name, param in init_params.items()}
     97     for k, v in params.items():
---&gt; 98         if (repr(v) != repr(init_params[k]) and
     99                 not (is_scalar_nan(init_params[k]) and is_scalar_nan(v))):
    100             filtered_params[k] = v

KeyError: 'metric'"><pre class="notranslate"><code class="notranslate">KeyErrorTraceback (most recent call last)
&lt;ipython-input-31-2e0afdc6afe0&gt; in &lt;module&gt;
     14 clf = MyClass(metric='auc')
     15 
---&gt; 16 print(clf)

/opt/conda/lib/python3.7/site-packages/sklearn/base.py in __repr__(self, N_CHAR_MAX)
    277             n_max_elements_to_show=N_MAX_ELEMENTS_TO_SHOW)
    278 
--&gt; 279         repr_ = pp.pformat(self)
    280 
    281         # Use bruteforce ellipsis when there are a lot of non-blank characters

/opt/conda/lib/python3.7/pprint.py in pformat(self, object)
    142     def pformat(self, object):
    143         sio = _StringIO()
--&gt; 144         self._format(object, sio, 0, 0, {}, 0)
    145         return sio.getvalue()
    146 

/opt/conda/lib/python3.7/pprint.py in _format(self, object, stream, indent, allowance, context, level)
    159             self._readable = False
    160             return
--&gt; 161         rep = self._repr(object, context, level)
    162         max_width = self._width - indent - allowance
    163         if len(rep) &gt; max_width:

/opt/conda/lib/python3.7/pprint.py in _repr(self, object, context, level)
    391     def _repr(self, object, context, level):
    392         repr, readable, recursive = self.format(object, context.copy(),
--&gt; 393                                                 self._depth, level)
    394         if not readable:
    395             self._readable = False

/opt/conda/lib/python3.7/site-packages/sklearn/utils/_pprint.py in format(self, object, context, maxlevels, level)
    168     def format(self, object, context, maxlevels, level):
    169         return _safe_repr(object, context, maxlevels, level,
--&gt; 170                           changed_only=self._changed_only)
    171 
    172     def _pprint_estimator(self, object, stream, indent, allowance, context,

/opt/conda/lib/python3.7/site-packages/sklearn/utils/_pprint.py in _safe_repr(object, context, maxlevels, level, changed_only)
    412         recursive = False
    413         if changed_only:
--&gt; 414             params = _changed_params(object)
    415         else:
    416             params = object.get_params(deep=False)

/opt/conda/lib/python3.7/site-packages/sklearn/utils/_pprint.py in _changed_params(estimator)
     96     init_params = {name: param.default for name, param in init_params.items()}
     97     for k, v in params.items():
---&gt; 98         if (repr(v) != repr(init_params[k]) and
     99                 not (is_scalar_nan(init_params[k]) and is_scalar_nan(v))):
    100             filtered_params[k] = v

KeyError: 'metric'
</code></pre></div>
<p dir="auto">Such a mechanism is also used in other famous libraries such as lightGBM. Concretely, it is used to apply aliases to variable names at fit method. (ex. convert metric -&gt; metrics)</p>
<p dir="auto">You can refer the detail description in lightgbm case I reported at the url.<br>
<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="620648513" data-permission-text="Title is private" data-url="https://github.com/microsoft/LightGBM/issues/3100" data-hovercard-type="issue" data-hovercard-url="/microsoft/LightGBM/issues/3100/hovercard" href="https://github.com/microsoft/LightGBM/issues/3100">microsoft/LightGBM#3100</a></p>
<p dir="auto">Since the error cause only  when you print or eval as string and there are no bad effect at fit (predict) method, I think it might be a good idea that the print behavior revert to the older version's.</p>
<p dir="auto">I'm opening the issue just to have a reference</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="from sklearn import set_config
from lightgbm import LGBMClassifier
set_config(print_changed_only=True)
print(LGBMClassifier(metric='auc'))"><pre class="notranslate"><span class="pl-k">from</span> <span class="pl-s1">sklearn</span> <span class="pl-k">import</span> <span class="pl-s1">set_config</span>
<span class="pl-k">from</span> <span class="pl-s1">lightgbm</span> <span class="pl-k">import</span> <span class="pl-v">LGBMClassifier</span>
<span class="pl-en">set_config</span>(<span class="pl-s1">print_changed_only</span><span class="pl-c1">=</span><span class="pl-c1">True</span>)
<span class="pl-en">print</span>(<span class="pl-v">LGBMClassifier</span>(<span class="pl-s1">metric</span><span class="pl-c1">=</span><span class="pl-s">'auc'</span>))</pre></div>
<p dir="auto">will fail because <code class="notranslate">metric</code> is not part of the signature of init, it's part of a kwargs parameter.</p>
<p dir="auto">Arguably, that's LightGBM not being super complient, but there's nothing in our docs explicitly preventing this.</p>
<p dir="auto">Fixed by <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="617388983" data-permission-text="Title is private" data-url="https://github.com/scikit-learn/scikit-learn/issues/17205" data-hovercard-type="pull_request" data-hovercard-url="/scikit-learn/scikit-learn/pull/17205/hovercard" href="https://github.com/scikit-learn/scikit-learn/pull/17205">#17205</a></p>