我的網誌已搬家至blog.miaout17.net,本網誌將不再更新

2009年4月26日 星期日

Ruby V.S. Python: Operator +=

剛剛在Godfat的Blog看到這篇文章,比較了Ruby和Python在對於list進行+=運算子操作的差異:

[~/test]# irb
irb(main):001:0> a = b = []
=> []
irb(main):002:0> a += [1]
=> [1]
irb(main):003:0> a
=> [1]
irb(main):004:0> b
=> []
irb(main):005:0>
Python 2.5.1
>>> a = b = []
>>> a += [1]
>>> a
[1]
>>> b
[1]

這個結果同樣的出乎我的意料之外,所以花了些時間研究一下。以下分別討論a=b=[]; a+=[1]在Ruby和Python的運作狀況。

先看看a=b=[]。毫無疑問,不管在Ruby或Python,這個敘述都是「造一個空list,並讓a和b指向同一個list」。如果你被上面的例子迷惑了,試試:

irb(main):001:0> a = b = []
=> []
irb(main):002:0> a << 1
==> [1]
irb(main):003:0> a
=> [1]
irb(main):004:0> b
=> [1]
irb(main):005:0>

再來,先討論Python
Python的a+=[1]等價於a.__iadd__([1])
對呼叫a+=[1]的block,變數a所綁定的物件不會改變
既然a和b所綁定的物件相同,那a和b的內容理所當然也會一樣

再來看看Ruby的a+=[1]
雖然Ruby可以覆載運算子,但+=運算子是不能被覆載的
簡單的試試

Ruby的method可以名為+,可以以=結尾,就是不能名為+=。同理,-=, *=, /=, &&=, ||=之類的也不行。參考Programming Ruby
In common with many other languages, Ruby has a syntactic shortcut: a=a+2 may be written as a+=2.
可以得到結論:a+=[1]只是一個Syntax sugar,可以想像他parse時就變成了a=a+[1],而不是由a去invoke一個名為+=的member function。

差異就在這邊,對呼叫a+=[1]的block來說,a+[1]造出了一個新物件,再綁定到變數a上,所以a和b指向不一樣的東西

這裡或許值得注意的是,要是Ruby中+=是一個member method,那a+=[1]就很難不影響到b。因為對呼叫a+=[1]的block來說,他只是去呼叫a的一個member method。完成之後a與b還是指向同一個object。像a<<b就是這樣的例子。

另外,這些敘述應該是等價的:
  • Python的a+=[b]
  • Python的a.append(b)
  • Ruby的a<<b
  • Ruby的a.push b
這也造就了Ruby和Python對於a+=b語意本質上的不同。

我個人是Python和Ruby都有在用,在這個小地方上我比較喜歡Python的作法:將+=視為member function,operator的left operand所綁定的目標不會改變。知道這點的話,Python應該是易了解又有一致性。

5 則留言:

Lin Jen-Shin (godfat) 提到...

c++ 也是可以定義 +=,
看了一下這邊:
http://docs.python.org/reference/datamodel.html#object.__radd__
其實意思就是不要把 x += y 想成 x = x + y 就是了 :s
而在 ruby 裡只是單純 parsing 上的差異罷了

匿名 提到...

sorry about that, the guy godfat like to show off and use other people's material. Godfat, God Father?

Here is the post, I did not say any dirty words.


I said :

"""

In [11]: id(1)
Out[11]: 7747304

In [12]: a=b=[]

In [13]: id(a)
Out[13]: 139847821343704

In [14]: id(b)
Out[14]: 139847821343704

In [15]: b=[]

In [16]: id(b)
Out[16]: 139847821070416

In [17]: id(a)==id(b)
Out[17]: False

That is the why.
"""

#<----------python is no good for you ----------
import time, datetime

t = time.time()
gt = time.gmtime(t)
hour = int(t) - int(time.strftime("%S", gt)) - int(time.strftime("%M", gt)) * 60 # what is that ??

print hour, int(t-int(t)%3600)
#-----------python is no good for you ---------->

"""
python is no good for you, because you may not have basic sense of programming.

1. it is not the hour, it is second
2. if so, you are still wrong, because , you may not know the basic math, int(t-int(t)%3600) could do better job

please stick with ruby.

"""

匿名 提到...

sorry about that

Mr. BigCat 提到...

以我長期看Godfat文章的理解,Godfat對程式語言有非常深的認識。我相信這些他都懂的。他只是在說Ruby的+=語意對他來說比較直覺,處理time的function比較好用。

但時間的那個問題有點冤枉Python,Python還有gt.tm_sec可以用,不用轉字串再prase回來。

Lin Jen-Shin (godfat) 提到...

雖然那段不是我寫的,但我會貼到自己的 blog 上,大抵上就只是抱怨而已,沒有其他意思。不過 gt.tm_sec 就真的是那時文件上沒查到了 @@ 感謝這個資訊。就覺得奇怪怎麼會搞成這個樣子...