这种类型的缺陷通常会惹恼新手程序员。考虑这个函数
<span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="n">mydict</span><span class="o">=</span><span class="p">{}):</span> <span class="c1"># Danger: shared reference to one dict for all calls</span>
<span class="o">...</span> <span class="n">compute</span> <span class="n">something</span> <span class="o">...</span>
<span class="n">mydict</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span>
<span class="k">return</span> <span class="n">mydict</span>
www#gaodaima.com来源gaodai$ma#com搞$代*码网搞代码
第一次调用此函数时,mydict
包含一项。第二次,mydict
包含两项,因为当 foo()
开始执行时, mydict
中已经有一项了。
函数调用经常被期望为默认值创建新的对象。 但实际情况并非如此。 默认值会在函数定义时一次性地创建。 如果对象发生改变,就如本示例中的字典那样,则对函数的后续调用将会引用这个被改变的对象。
按照定义,不可变对象例如数字、字符串、元组和 None
因为不可变所以是安全的。 对可变对象例如字典、列表和类实例的改变则可能造成迷惑。
由于这一特性,在编程中应遵循的一项好习惯是不使用可变对象作为默认值。 而应使用 None
作为默认值和函数中的值,检查值为 None
的形参并创建相应的列表、字典或其他可变对象。 例如,不要这样写:
<span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="n">mydict</span><span class="o">=</span><span class="p">{}):</span>
<span class="o">...</span>
而要这样写:
<span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="n">mydict</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="k">if</span> <span class="n">mydict</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">mydict</span> <span class="o">=</span> <span class="p">{}</span> <span class="c1"># create a new dict for local namespace</span>
这一特性有时会很有用处。 当你有一个需要进行大量耗时计算的函数时,一个常见技巧是将每次调用函数的参数和结果值缓存起来,并在同样的值被再次请求时返回缓存的值。 这称为“记忆”,具体实现方式可以是这样的:
<span class="c1"># Callers can only provide two parameters and optionally pass _cache by keyword</span>
<span class="k">def</span> <span class="nf">expensive</span><span class="p">(</span><span class="n">arg1</span><span class="p">,</span> <span class="n">arg2</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">_cache</span><span class="o">=</span><span class="p">{}):</span>
<span class="k">if</span> <span class="p">(</span><span class="n">arg1</span><span class="p">,</span> <span class="n">arg2</span><span class="p">)</span> <span class="ow">in</span> <span class="n">_cache</span><span class="p">:</span>
<span class="k">return</span> <span class="n">_cache</span><span class="p">[(</span><span class="n">arg1</span><span class="p">,</span> <span class="n">arg2</span><span class="p">)]</span>
<span class="c1"># Calculate the value</span>
<span class="n">result</span> <span class="o">=</span> <span class="o">...</span> <span class="n">expensive</span> <span class="n">computation</span> <span class="o">...</span>
<span class="n">_cache</span><span class="p">[(</span><span class="n">arg1</span><span class="p">,</span> <span class="n">arg2</span><span class="p">)]</span> <span class="o">=</span> <span class="n">result</span> <span class="c1"># Store result in the cache</span>
<span class="k">return</span> <span class="n">result</span>
你也可以使用包含一个字典的全局变量而不使用参数默认值;这完全取决于个人偏好。
来源:搞代码网:原文地址:https://www.gaodaima.com