Monkey Patching: Python V.S. Ruby

之前在寫Rails的時候還不是很熟悉Ruby,所以很多Ruby的特性沒有去深究。最近剛考完期末考比較有時間了,所以好好來看了一下Ruby。目前覺得學Ruby的好處就是會讓我想去比較Python,因為我個人還是Python的愛好著,所以看Ruby可以做到的功能就想要看Python可不可以做到。拿Ruby最有名的名能之一來說好了: Monkey Patching,從Wikipedia:
monkey patch is a way to extend or modify the run-time code of dynamic languages without altering the original source code.
Ruby 可以很容易的動在舊有的Class/Module中加入新的Class/Method,舉例來說我們可以新增一個  method 到 Array 中:

def Array.test
  puts "This is a singleton method `test' of the Array class"
end

這時候 Array 就會多了一個名為 'test' 的 class method,事實上 test 其實是 Array 這個 class 的 singleton method,因為 Ruby 裡 class 是 first class object,所有的 class 都是 Class 的 instance。 所以這個動作就是在 Array 這個 'instance' 上加入一個 singleton method。同樣的動作也可以寫成

class << Array
  def test
    puts "This is a singleton method `test' of the Array class"
  end
end



class Array
  def self.test
    puts "This is a singleton method `test' of the Array class"
  end
end

來測試一下Array是不是真的有一個test method:

irb(main):006:0> Array.respond_to? :test
=> true
irb(main):007:0> Array.test
This is a singleton method `test' of the Array class
=> nil

Python 一樣可以做到 Monkey patching

>>> import hashlib
>>> def fake_md5(data):
...     print 'Replacing original md5'
...
>>> hashlib.md5 = fake_md5
>>> hashlib.md5('bla')
Replacing original md5

可以看到上面我們把 hashlib 裡的 md5 function 替換掉了。不過Python有一個限制就是不能Monkey patch 'built-in' type:

>>> def test():
...     print 'aha!'
...
>>> list.test = test
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't set attributes of built-in/extension type 'list'

Ruby 也有類似的功能可以禁止Monkey patching

irb(main):001:0> require 'matrix'
=> true
irb(main):002:0> Matrix.frozen?
=> false
irb(main):003:0> Matrix.freeze
=> Matrix
irb(main):004:0> Matrix.frozen?
=> true
irb(main):005:0> def Matrix.test
irb(main):006:1> puts 'Monkey-patched method test'
irb(main):007:1> end
RuntimeError: can't modify frozen Class
        from (irb):5
        from /usr/bin/irb:12:in `<main>'

不過 Ruby 的 built-in 大多沒有被freeze。我想這是設計哲學的問題,個人來說我比較喜歡Python的方式。在 Python 裡如果你想要延伸 built-in class 的功能的話,直接繼承就好了。這樣的好處是你在使用這個 class 時,class 的名稱就會提醒你這是你改過 class;如果直接用像 Ruby 常用的 monkey-patching 的話,你或其他用你的 class 的 programmer 會不知道或忘記你已經修改了原本那個class的預設行為,需造成不必要的錯誤,也很難 debug。所以我還是比較喜歡 Python 哲學:
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Readability counts.
Reference:
http://madebydna.com/all/code/2011/06/24/eigenclasses-demystified.html

留言

這個網誌中的熱門文章

決定了!!!

P.S. I Love You

Tips: PPTP client on Openwrt