作者KSJ (阿真)
看板Python
标题[心得] 关於 function (def) 的 default...
时间Tue Nov 15 17:20:55 2011
--前言
这文章标题不知道下的好不好
我也没爬文 如果已有类似观念 麻烦推文说一下
本来还想把python原文书卖掉
发生这件人为bug後 还是留起来吧XD
--内容
这是个关於定义函式时 给定预设值 与 mutable object 的概念
为了简化实际的案件 下面是一个例子
def show_append(x=[],y=2):
x.append(y)
print x
写成这样...
本来的意思是希望 show_append函式里的二个变数
x 是个有 append method 的 object
y是被 append 的值
如果都没给值的话 就来个范例 空的list 与 被加入的数2
但很多事情总是不如想像...
下面来三个范例
>>> show_append()
[2]
>>> show_append()
[2, 2]
>>> show_append()
[2, 2, 2]
这...这饭粒...有毒
跟预想的不一样
原因是:
在定义函式中 预设的变数(x跟y) 是在定义函式执行时所产生(不是在呼叫的时候)
所以 x (也就是[])是在定义show_append函式时
就一直存在的东西
而因为[] 是mutable obj
在使用 append 这个in-place change的方法时
本身就会改变
所以 要范例的话 可以如下
>>> show_append([])
[2]
给个新的"[]" 就行了
顺带一提
>>> show_append()
[2, 2, 2, 2, 2]
原来的预设值还在哟~
顺带再提
也许你可能会这样想
如果给x一个新的值[]行不行 像下面
>>> show_append(x=[])
[2]
x的预设直就变成空的[]了~??
当然是不行...
否则假使你用 show_append (y=3)
以後不就都append 3了?(又违反意思了)
在定义函式里 使用的预设值 比方 def func(name = value_d): ...
呼叫func(name = value)
并不是创建name,并给一个新的值value
所以你不能在呼叫show_append时创建新的变数
像 show_append(another_value = 99)
呼叫时用的name 只是拿来对应func里的name用而已 (找名字)
预设的value_d值仍然存在
所以
>>> show_append()
[2, 2, 2, 2, 2, 2]
一个也没少哟~
--结论
要注意给函式的预设值是否为mutable,是否符合创造函式的原意
记住python这样的特性 运用在该用的地方吧!!
记得 力量越大 bug也越大... 共de之
--题外话
总觉得不会碰上的事 总是会碰上...orz
当这样的问题发生在class里头的__init__时候 就更难发现了(我这是这种)
希望不要跟我犯一样的错...(还是只有我...
以上 欢迎指正与分享
--
※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 140.112.63.202
2F:→ KSJ:看来是只有我了 囧... 11/15 17:45
3F:→ mathfeel:每次show_append()之后跑help(show_append)。有甚麽发现 11/15 18:10
4F:→ KSJ:在idle下 show_append( 之後就破梗了 x=会越来越长XD 11/15 18:18
5F:→ sbrhsieh:Python 中除了 global ...,其实是没有 definition 的 11/15 20:41
6F:→ sbrhsieh:这个例子改写成 C++,不也是一样的行为吗? 11/15 23:21
是指说 在runtime才"执行"产生函式吗@@?
说来残愧... 我在c与c++写函式的时候还没用过default值...
在我初学oop观念时碰到的是python而非c++
c++的 private public的观念 或是宣告class的方式
相较於python都较令我难理解
例如c++ . -> 在python下都是 . 理解能力的需求就不一样
不过当然在c++的程式码上 可读性也许不够 但理解後观念会更清楚吧
我觉得还是该弄懂c++的类别观念与写法 (也许先略懂指标吧...
很多python相关的套件 说明文件还是在c++下较完整 例如PyQt
要了解而使用 以免像我对python def的误用
7F:推 mikapauli:不要创造会改变外部物件的函式,就没这个问题了w 11/16 01:19
8F:推 mikapauli:一般都会先做适当的copy说 11/16 01:22
其实我还蛮常用把cls instance当做变数在函式(别的class里的)内传来传去
大部份是读取需要的资料
修改资料的话就直接用method了 (classinstance.method("资料"))
我觉得在函式中修改instance时用instance的method 感觉还ok说
所以我觉得创造修改外部物件的函式应该是需要的
不知道这观念对不对...
而我是以为class def __init__(self, arg = another_instance_create) 时
会重新创造 default arg里的东西
所以中招了XD (如1F的文件的 important部份)
9F:→ sbrhsieh:哎呀,我搞错了。写成 C++ 在写法上没有等效... 11/16 12:23
10F:推 kdjf:到python3就很好理解了, 因为function也是class 11/17 09:43
我参考的原文书里有提了许多 3与2不同的地方
print, string type, type & class, range 等等
不知道function的修改呢~
有空再来看看:)
(这让我想到 刚学python就抓3下来因为print变成func而无法helloworld的一段往事)
不过我目前使用的相关套件在2下仍有比较好的支援度
所以3还可以再缓一下吧
(我目前是用python2.5较多)
11F:推 darkgerm:推~写得好清楚~ 11/19 02:14
※ 编辑: KSJ 来自: 180.176.140.46 (11/19 16:28)
12F:推 mikapauli:真的需要的话当然OK阿,只是一般函式是没有side effect的 11/20 21:09