-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy paththings-about-python-named-args.html
335 lines (276 loc) · 26.5 KB
/
things-about-python-named-args.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="HandheldFriendly" content="True" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="robots" content="index, follow" />
<link href="https://fonts.googleapis.com/css2?family=Source+Code+Pro:ital,wght@0,400;0,700;1,400&family=Source+Sans+Pro:ital,wght@0,300;0,400;0,700;1,400&display=swap" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="https://blog.tonychow.me/theme/stylesheet/style.min.css">
<link id="pygments-light-theme" rel="stylesheet" type="text/css"
href="https://blog.tonychow.me/theme/pygments/colorful.min.css">
<link rel="stylesheet" type="text/css" href="https://blog.tonychow.me/theme/font-awesome/css/fontawesome.css">
<link rel="stylesheet" type="text/css" href="https://blog.tonychow.me/theme/font-awesome/css/brands.css">
<link rel="stylesheet" type="text/css" href="https://blog.tonychow.me/theme/font-awesome/css/solid.css">
<link href="https://blog.tonychow.me/feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="Tonychow's Blog Atom">
<!-- Chrome, Firefox OS and Opera -->
<meta name="theme-color" content="#333333">
<!-- Windows Phone -->
<meta name="msapplication-navbutton-color" content="#333333">
<!-- iOS Safari -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<!-- Microsoft EDGE -->
<meta name="msapplication-TileColor" content="#333333">
<meta name="author" content="tonychow" />
<meta name="description" content="" />
<meta name="keywords" content="python">
<meta property="og:site_name" content="Tonychow's Blog"/>
<meta property="og:title" content="Python 函数的命名参数相关"/>
<meta property="og:description" content=""/>
<meta property="og:locale" content="en_US"/>
<meta property="og:url" content="https://blog.tonychow.me/things-about-python-named-args.html"/>
<meta property="og:type" content="article"/>
<meta property="article:published_time" content="2013-05-15 00:00:00+08:00"/>
<meta property="article:modified_time" content=""/>
<meta property="article:author" content="https://blog.tonychow.me/author/tonychow.html">
<meta property="article:section" content="python"/>
<meta property="article:tag" content="python"/>
<meta property="og:image" content="/images/avatar.jpg">
<title>Tonychow's Blog – Python 函数的命名参数相关</title>
</head>
<body class="light-theme">
<aside>
<div>
<a href="https://blog.tonychow.me/">
<img src="/images/avatar.jpg" alt="" title="">
</a>
<h1>
<a href="https://blog.tonychow.me/"></a>
</h1>
<p>Go/Python backend developer</p>
<ul class="social">
<li>
<a class="sc-github" href="https://github.com/chow1937" target="_blank">
<i class="fab fa-github"></i>
</a>
</li>
</ul>
</div>
</aside>
<main>
<nav>
<a href="https://blog.tonychow.me/">Home</a>
<a href="/archives.html">Archives</a>
<a href="/categories.html">Categories</a>
<a href="/tags.html">Tags</a>
<a href="https://blog.tonychow.me/feeds/all.atom.xml">Atom</a>
</nav>
<article class="single">
<header>
<h1 id="things-about-python-named-args">Python 函数的命名参数相关</h1>
<p>
Posted on 三 15 五月 2013 in <a href="https://blog.tonychow.me/category/python.html">python</a>
• 2 min read
</p>
</header>
<div>
<h3>起因</h3>
<p>今天师弟问了一个关于 Python 函数参数的一个问题:</p>
<div class="highlight"><pre><span></span><code><span class="c1">#1</span>
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">l</span><span class="o">=</span><span class="p">[]):</span>
<span class="k">pass</span>
<span class="c1">#2</span>
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">l</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="k">if</span> <span class="n">l</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">l</span> <span class="o">=</span> <span class="p">[]</span>
</code></pre></div>
<blockquote>
<p>为啥第一个函数会把 l 每次调用完的值保留下来?</p>
</blockquote>
<p>起初我认为问的是这两个函数使用的时候,为何会保持对传入的参数 l 的修改。从这个方面来讲,是因为 Python 对于数据赋值的处理的原因。</p>
<!--more-->
<p>在 Python 中,赋值是传引用的。一个列表,比如 [1, 2, 3],或者一个字符串,'tonychow',这些对象在创建的时候会在内存中分配一段空间。如果将这些对象赋值给一个变量名,那就会导致在 Python 的命名空间中该变量名指向内存中这个对象。对该变量名的操作就是对内存中这个对象的操作。所以如果尝试直接将一个变量 a 赋值给另外一个变量 b ,导致的后果是,命名空间中,这两个变量名 a 和 b 指向内存中同样一个对象,也就是所谓传引用赋值。对其中任意一个变量的操作,实质是对该对象进行操作,所以同样的操作后结果也会可以在另外一个变量中看到。如下:</p>
<div class="highlight"><pre><span></span><code>>>> <span class="nv">a</span> <span class="o">=</span> <span class="o">[</span><span class="m">1</span>, <span class="m">2</span>, <span class="m">3</span><span class="o">]</span>
>>> <span class="nv">b</span> <span class="o">=</span> a
>>> a.pop<span class="o">()</span>
<span class="m">3</span>
>>> b
<span class="o">[</span><span class="m">1</span>, <span class="m">2</span><span class="o">]</span>
>>> a
<span class="o">[</span><span class="m">1</span>, <span class="m">2</span><span class="o">]</span>
>>>
</code></pre></div>
<p>从上面的代码可以看到,在将 a 赋值给 b 之后,对 a 列表调用 pop 方法,导致的是 b 列表也发生了变化。我们还可以通过 Python 内置的 globals 函数和 id 函数来加深这个理解。globals 函数将会返回一个字典,这个字典是当前的全局符号表。而 id 函数则会返回一个对象的标识,可以将其返回值看作是这个对象在内存中的地址。</p>
<div class="highlight"><pre><span></span><code><span class="o">>>></span> <span class="nb">globals</span><span class="p">()</span>
<span class="p">{</span><span class="s1">'a'</span><span class="p">:</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">],</span> <span class="s1">'b'</span><span class="p">:</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">],</span>
<span class="s1">'__builtins__'</span><span class="p">:</span> <span class="o"><</span><span class="n">module</span> <span class="s1">'__builtin__'</span> <span class="p">(</span><span class="n">built</span><span class="o">-</span><span class="ow">in</span><span class="p">)</span><span class="o">></span><span class="p">,</span>
<span class="s1">'value'</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span> <span class="s1">'__package__'</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span>
<span class="s1">'key'</span><span class="p">:</span> <span class="s1">'__doc__'</span><span class="p">,</span> <span class="s1">'__name__'</span><span class="p">:</span> <span class="s1">'__main__'</span><span class="p">,</span> <span class="s1">'__doc__'</span><span class="p">:</span> <span class="kc">None</span><span class="p">}</span>
<span class="o">>>></span> <span class="nb">id</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
<span class="mi">3077280588</span><span class="n">L</span>
<span class="o">>>></span> <span class="nb">id</span><span class="p">(</span><span class="n">b</span><span class="p">)</span>
<span class="mi">3077280588</span><span class="n">L</span>
<span class="o">>>></span>
</code></pre></div>
<p>可以看到,a 和 b 都在当前的全局字符表中,他们的值也都是一致的。此外,id 函数的结果明确地说明了 a 和 b 这两个变量名都是指向了内存中的同一个对象。而在 Python 中,调用函数的时候,传入参数,也是进行传引用的赋值。所以我师弟说的这两个函数都会保留对于传入参数的修改,也就是:</p>
<div class="highlight"><pre><span></span><code><span class="o">>>></span> <span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="n">l</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="o">...</span> <span class="k">if</span> <span class="n">l</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="o">...</span> <span class="n">l</span> <span class="o">=</span> <span class="p">[]</span>
<span class="o">...</span> <span class="n">l</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="o">...</span>
<span class="o">>>></span> <span class="n">bar</span> <span class="o">=</span> <span class="p">[</span><span class="mi">2</span><span class="p">]</span>
<span class="o">>>></span> <span class="n">bar</span>
<span class="p">[</span><span class="mi">2</span><span class="p">]</span>
<span class="o">>>></span> <span class="n">func</span><span class="p">(</span><span class="n">bar</span><span class="p">)</span>
<span class="o">>>></span> <span class="n">func</span><span class="p">(</span><span class="n">bar</span><span class="p">)</span>
<span class="o">>>></span> <span class="n">bar</span>
<span class="p">[</span><span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span>
<span class="o">>>></span>
</code></pre></div>
<p>题外话,在 Python 内置的数据类型中,有两种不同的数据类型。一种是可变类型(mutable),比如 list , dict 等;另外一种就是不可变类型(immutable),比如字符串或者 tuple。</p>
<p>可是后来师弟贴出了另外一段代码:</p>
<div class="highlight"><pre><span></span><code><span class="o">>>></span> <span class="k">def</span> <span class="nf">func2</span><span class="p">(</span><span class="n">items</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="o">...</span> <span class="k">if</span> <span class="n">items</span> <span class="o">==</span> <span class="kc">None</span><span class="p">:</span>
<span class="o">...</span> <span class="n">items</span> <span class="o">=</span> <span class="p">[]</span>
<span class="o">...</span> <span class="n">items</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="o">...</span> <span class="k">return</span> <span class="n">items</span>
<span class="o">...</span>
<span class="o">>>></span> <span class="n">func2</span><span class="p">()</span>
<span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="o">>>></span> <span class="n">func2</span><span class="p">()</span>
<span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="o">>>></span>
</code></pre></div>
<p>这下我明白了,师弟说的不是我想到的那个问题,而是命名参数的问题。</p>
<h3>解决</h3>
<p>说实话这个问题一开始我也没有想到答案。大家在学习 Python 的时候,无论看的是哪本入门书,应该在开始的时候都会看到一句话“ Python 中一切都是对象”。看代码:</p>
<div class="highlight"><pre><span></span><code><span class="o">>>></span> <span class="nb">isinstance</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nb">int</span><span class="p">)</span>
<span class="kc">True</span>
<span class="o">>>></span> <span class="nb">isinstance</span><span class="p">(</span><span class="s1">'test'</span><span class="p">,</span> <span class="nb">str</span><span class="p">)</span>
<span class="kc">True</span>
<span class="o">>>></span> <span class="k">def</span> <span class="nf">func</span><span class="p">():</span>
<span class="o">...</span> <span class="k">pass</span>
<span class="o">...</span>
<span class="o">>>></span> <span class="nb">type</span><span class="p">(</span><span class="n">func</span><span class="p">)</span>
<span class="o"><</span><span class="nb">type</span> <span class="s1">'function'</span><span class="o">></span>
<span class="o">>>></span> <span class="nb">dir</span><span class="p">(</span><span class="n">func</span><span class="p">)</span>
<span class="p">[</span><span class="s1">'__call__'</span><span class="p">,</span> <span class="s1">'__class__'</span><span class="p">,</span> <span class="s1">'__closure__'</span><span class="p">,</span> <span class="s1">'__code__'</span><span class="p">,</span> <span class="s1">'__defaults__'</span><span class="p">,</span>
<span class="s1">'__delattr__'</span><span class="p">,</span> <span class="s1">'__dict__'</span><span class="p">,</span> <span class="s1">'__doc__'</span><span class="p">,</span> <span class="s1">'__format__'</span><span class="p">,</span> <span class="s1">'__get__'</span><span class="p">,</span>
<span class="s1">'__getattribute__'</span><span class="p">,</span> <span class="s1">'__globals__'</span><span class="p">,</span> <span class="s1">'__hash__'</span><span class="p">,</span> <span class="s1">'__init__'</span><span class="p">,</span>
<span class="s1">'__module__'</span><span class="p">,</span> <span class="s1">'__name__'</span><span class="p">,</span> <span class="s1">'__new__'</span><span class="p">,</span> <span class="s1">'__reduce__'</span><span class="p">,</span> <span class="s1">'__reduce_ex__'</span><span class="p">,</span>
<span class="s1">'__repr__'</span><span class="p">,</span> <span class="s1">'__setattr__'</span><span class="p">,</span> <span class="s1">'__sizeof__'</span><span class="p">,</span> <span class="s1">'__str__'</span><span class="p">,</span> <span class="s1">'__subclasshook__'</span><span class="p">,</span>
<span class="s1">'func_closure'</span><span class="p">,</span> <span class="s1">'func_code'</span><span class="p">,</span> <span class="s1">'func_defaults'</span><span class="p">,</span> <span class="s1">'func_dict'</span><span class="p">,</span> <span class="s1">'func_doc'</span><span class="p">,</span>
<span class="s1">'func_globals'</span><span class="p">,</span> <span class="s1">'func_name'</span><span class="p">]</span>
<span class="o">>>></span> <span class="n">func</span><span class="o">.</span><span class="vm">__code__</span>
<span class="o"><</span><span class="n">code</span> <span class="nb">object</span> <span class="n">func</span> <span class="n">at</span> <span class="mh">0xb76c8410</span><span class="p">,</span> <span class="n">file</span> <span class="s2">"<stdin>"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="o">></span>
<span class="o">>>></span> <span class="n">func</span><span class="o">.</span><span class="vm">__name__</span>
<span class="s1">'func'</span>
<span class="o">>>></span>
</code></pre></div>
<p>对的,数字1是一个对象,字符串 'test' 也是一个对象,甚至一个函数也是一个类型为 function 的对象,也有一堆的属性和方法。对于 function 对象而言,有一个特殊属性 <strong>defaults</strong> ,这个属性用一个元组保存了是这个 function 对象的命名参数的缺省值,如下:</p>
<div class="highlight"><pre><span></span><code><span class="o">>>></span> <span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="n">a</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">b</span><span class="o">=</span><span class="mi">2</span><span class="p">):</span>
<span class="o">...</span> <span class="k">pass</span>
<span class="o">...</span>
<span class="o">>>></span> <span class="n">func</span><span class="o">.</span><span class="vm">__defaults__</span>
<span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</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">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span>
<span class="o">...</span> <span class="k">pass</span>
<span class="o">...</span>
<span class="o">>>></span> <span class="n">foo</span><span class="o">.</span><span class="vm">__defaults__</span> <span class="ow">is</span> <span class="kc">None</span>
<span class="kc">True</span>
<span class="o">>>></span> <span class="k">def</span> <span class="nf">func_no</span><span class="p">():</span>
<span class="o">...</span> <span class="k">pass</span>
<span class="o">...</span>
<span class="o">>>></span> <span class="n">func_no</span><span class="o">.</span><span class="vm">__defaults__</span> <span class="ow">is</span> <span class="kc">None</span>
<span class="kc">True</span>
<span class="o">>>></span>
</code></pre></div>
<p>如果一个函数有命名参数,则按顺序保存了命名参数的缺省值。如果这个函数命名参数没有缺省值或者没有命名参数,则为 None 。回到问题,为什么第一个函数中指定缺省值为 [] 会导致随着执行过程中,缺省参数的值会被保留下来呢?代码如下:</p>
<div class="highlight"><pre><span></span><code><span class="o">>>></span> <span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="n">l</span><span class="o">=</span><span class="p">[]):</span>
<span class="o">...</span> <span class="n">l</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="o">...</span> <span class="k">return</span> <span class="n">l</span>
<span class="o">...</span>
<span class="o">>>></span> <span class="n">foo</span><span class="p">()</span>
<span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="o">>>></span> <span class="n">foo</span><span class="p">()</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span>
<span class="o">>>></span> <span class="n">foo</span><span class="p">()</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span>
<span class="o">>>></span>
</code></pre></div>
<p>其实通过上面的罗嗦一大堆,答案很容易就可以得到了:foo 是一个 function 类型的对象,这个对象中有个 <strong>defaults</strong> 属性,保存了命名参数 l 的值,而在一次次的调用过程中,因为没有传入参数,所以实际上 foo 函数改变的是命名参数的缺省值。也就是师弟所说的这个函数在一次次调用中保留了对命名参数l的结果的修改。而师弟贴出的第二个函数的命名参数缺省值是 None ,实质上就是没有缺省值,所以l的值修改没有在调用中保留下来。是不是真的这样?我们来看下:</p>
<div class="highlight"><pre><span></span><code><span class="o">>>></span> <span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="n">l</span><span class="o">=</span><span class="p">[]):</span>
<span class="o">...</span> <span class="nb">print</span> <span class="s1">'default_arg_addr:'</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="nb">id</span><span class="p">(</span><span class="n">l</span><span class="p">))</span>
<span class="o">...</span> <span class="n">l</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="o">...</span> <span class="nb">print</span> <span class="s1">'changed_var_addr:'</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="nb">id</span><span class="p">(</span><span class="n">l</span><span class="p">))</span>
<span class="o">...</span> <span class="nb">print</span> <span class="n">l</span>
<span class="o">...</span>
<span class="o">>>></span> <span class="nb">id</span><span class="p">(</span><span class="n">foo</span><span class="o">.</span><span class="vm">__defaults__</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
<span class="mi">3077402860</span><span class="n">L</span>
<span class="o">>>></span> <span class="n">foo</span><span class="p">()</span>
<span class="n">default_arg_addr</span><span class="p">:</span><span class="mi">3077402860</span>
<span class="n">changed_var_addr</span><span class="p">:</span><span class="mi">3077402860</span>
<span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="o">>>></span> <span class="n">foo</span><span class="p">()</span>
<span class="n">default_arg_addr</span><span class="p">:</span><span class="mi">3077402860</span>
<span class="n">changed_var_addr</span><span class="p">:</span><span class="mi">3077402860</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span>
<span class="o">>>></span> <span class="n">foo</span><span class="o">.</span><span class="vm">__defaults__</span>
<span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">],)</span>
<span class="o">>>></span>
</code></pre></div>
<p>上面这个函数 foo 有一个命名参数 l ,它的命名参数缺省值是一个空的列表,虽然是空列表,可是它确确实实是一个对象,已经在内存给它分配了空间。我们可以通过 id 函数的结果看出来。然后是两次的调用 foo 函数可以看到,因为没有传入参数,所以这两次修改的都是这个缺省的命名参数的值,所以可以得到所谓的对 l 的值的修改保留下来了的感觉。</p>
<h3>深入</h3>
<p>首先我们应该明白,在 Python 中,一个对象的实例化和初始化是不同的。一个对象实例化调用的是对象的 <strong>new</strong> 函数,而初始化调用的是 <strong>init</strong> 函数。所以,要深入地去看在 Python 中,函数在实例化的时候到底发生了什么,我们应该要去看 Python 源码。如下,源码版本为 Python2.7.4。</p>
<p>Python2.7.4/Objects/funcobject.c, func_new, L436-L439</p>
<div class="highlight"><pre><span></span><code><span class="k">if</span> <span class="p">(</span><span class="n">defaults</span> <span class="o">!=</span> <span class="n">Py_None</span><span class="p">)</span> <span class="p">{</span>
<span class="n">Py_INCREF</span><span class="p">(</span><span class="n">defaults</span><span class="p">);</span>
<span class="n">newfunc</span><span class="o">-></span><span class="n">func_defaults</span> <span class="o">=</span> <span class="n">defaults</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>Python2.7.4/Include/object.h, L765-L767</p>
<div class="highlight"><pre><span></span><code><span class="cp">#define Py_INCREF(op) ( \</span>
<span class="cp">_Py_INC_REFTOTAL _Py_REF_DEBUG_COMMA \</span>
<span class="cp">((PyObject*)(op))->ob_refcnt++)</span>
</code></pre></div>
<p>上面第一段代码是 funcobject 的 func_new 中的代码,也就应该是 functions 对象的 <strong>new</strong> 函数代码。可以看到,如果 defaults 不是 None,也就是说有值,而我们上面也提到 Python 中一切都是对象,所以就会对这个对象进行 Py_INCREF 操作,并且将这个 defaults 值设定为 func_defaults。Py_INCREF 操作是什么?从第二段代码可以看到,这是一个宏定义,将参数 op 的 ob_refcnt 值加一。ob_refcnt 是什么?refcnt----reference count,这样明白了,就是将该对象的引用计数值加一。在执行了函数函数之后,该命名函数的缺省值对象并没有被销毁,而是随着该函数对象的存在而存在。对这个缺省之对象的修改当然也会被保留下来。</p>
</div>
<div class="tag-cloud">
<p>
<a href="https://blog.tonychow.me/tag/python.html">python</a>
</p>
</div>
</article>
<footer>
<p>
© 2017 - This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/deed.en_US" target="_blank">Creative Commons Attribution-ShareAlike</a>
</p>
<p>
Built with <a href="http://getpelican.com" target="_blank">Pelican</a> using <a href="http://bit.ly/flex-pelican" target="_blank">Flex</a> theme
</p><p>
<a rel="license"
href="http://creativecommons.org/licenses/by-sa/4.0/"
target="_blank">
<img alt="Creative Commons License"
title="Creative Commons License"
style="border-width:0"
src="https://i.creativecommons.org/l/by-sa/4.0/80x15.png"
width="80"
height="15"/>
</a>
</p> </footer>
</main>
<script type="application/ld+json">
{
"@context" : "http://schema.org",
"@type" : "Blog",
"name": " Tonychow's Blog ",
"url" : "https://blog.tonychow.me",
"image": "/images/avatar.jpg",
"description": "tonychow's Thoughts and Writings"
}
</script>
</body>
</html>