2013年12月29日 星期日

簡介 python 的物件生成 ( how to generate an instance in python)

    在開始介紹 python 怎麼產生 instnace 前,先來看一段簡單的程式碼


class MyStr1(str):
    def __init__(self, a='', b=''):
        super(MyStr1, self).__init__()
        self.a = a
        self.b = b


    這段程式碼看起來沒什麼,就繼承自 str 。然後 override __init__ 方法。所以我們可以用下面程式碼產生 instance。


 a = MyStr1("1")

   可是當我們執行下面程式碼時,

 a = MyStr1("1", "2")


    卻會出現

Traceback (most recent call last):
  File "", line 1, in 
TypeError: str() takes at most 1 argument (2 given)

    這到底發生什麼事? 會出現這個錯誤,是因為 str.__new__ 這個方法只接受一個參數,而你給他兩個。

    做個簡單的實驗。執行這兩行 str.__new__(str) 和 str.__new__(str, '1') 程式碼不會出錯。然而 str.__new__(str, '1', '2') 會出現上面的錯誤。所以可以合理懷疑問題真的出在這了。

     接下來來談一個 class 怎麼產生 instance。當 class 要產生 instance 時,他會呼叫 class 的 metaclss 的 __call__ 方法。而 metaclass 的 __call__ 方法大概長這樣:

def __call__(cls, *args, **kwargs):
    obj = cls.__new__(cls, *args, **kwargs)
    obj.__init__(*args, **kwargs)
    return obj

    所以執行 a = MyStr1("1", "2") 時,直譯器會先做 obj = str.__new__(str, *args, **kwargs) ,其中 *args 是 (1, 2, 3),可是因為 str.__new__ 只接受一個參數,因此發生錯誤。

    要怎麼知道直譯器在建立 instance 時,會把類別名稱後面的參數全都丟進 __new__ 和 __init__ 裡呢? 用一段簡單的程式即可說明。



class ClsA(object):
    def __init__(self, *args, **kwargs):
        print 'init: ', args, kwargs

    def __new__(cls, *args, **kwargs):
        print 'new: ', args, kwargs
        return super(ClsA, cls).__new__(cls, *args, **kwargs)

  
a = ClsA('a', 'b', 2)


   上面程式碼,當要建立 ClsA 的程式碼,他會先執行 __new__,而餵給 __new__ 的參數則是 ClsA 後面的參數。之後再把 ClsA 後面的參數餵給 __init__。而這也說明了為什麼一開始的程式碼會有問題。因為建立 instance 時,預設行為是把類別後面的參數全都丟給 __new__ 和 __init__。可是 str.__new__最多只接受一個參數,因此出錯。

    要如何解決第一個程式所遇到的問題呢?
    解法1.
    不要使用預設的 metaclass(type),自訂一個 meta class


class CustomMetaCls(type):
    def __call__(cls, *args, **kwargs):
        if len(args):
            content = args[0]
        else:
            content = ''
        obj = cls.__new__(cls, content)
        obj.__init__(*args, **kwargs)
        return obj

  
class MyStr(str):
    __metaclass__ = CustomMetaCls
 
    def __init__(self, a='', b=''):
        super(MyStr, self).__init__(a)
        self.a = a
        self.b = b


b = MyStr('a', 'b')


    解法2.
    override __new__

class MyStr1(str):
    def __init__(self, a='', b=''):
        super(MyStr1, self).__init__()
        self.a = a
        self.b = b

    def __new__(cls, *args, **kwargs):
        print args, kwargs
        if len(args):
            content = args[0]
        else:
            content = ''
        return str.__new__(cls, content)

2013年12月25日 星期三

docker 原理簡介

    Docker 官網上的簡介,寫了"Docker is an open-source project to easily create lightweight, portable, self-sufficient containers from any application."。不過 containers 是啥呢? 有點難解釋,基本上可以想像成一個 sandbox。不過 docker 還可以在上面跑完整的 linux distributions。

    先看看 Docker 怎麼實作的。他是建立在 lxc 和 aufs 的基礎上。那 lxc 是什麼呢? 官網上講"LXC is a userspace interface for the Linux kernel containment features."。官網上還有這一段話。

Features

Current LXC uses the following kernel features to contain processes:
  • Kernel namespaces (ipc, uts, mount, pid, network and user)
  • Apparmor and SELinux profiles
  • Seccomp policies
  • Chroots (using pivot_root)
  • Kernel capabilities
  • Control groups (cgroups)
    他用到 kernel 許多特性,不過和 container 有關的主要是 kernel namespace 和 control groups。不過能做到 container 主要是靠 kernel namespace。


    namespace 是什麼呢? 以 pid namespace 為例,在每個 namespace 裡,每個 pid 都是唯一的。而不同的 namespace 裡, pid 可以重複。因此在 A namespace 哩,會有一個自以為 pid = 1 的程序,在 B namespace 哩,也會有一個自以為 pid = 1 的程序。如果 A 和 B 繼承自最上層的 namespace (也就是 host 所在的 namespace,叫做 C namespace ),那麼從 C namespace 的觀點來看,前面那兩個自以為是 pid = 1 的程序,其實他的 pid 都不為 1 。這樣可以做到程序隔離的功能,也可以做到虛擬化。 lxc 用到的 namespace 就 ipc, uts, mount, pid, network and user。這篇文章有詳細說明 docker 用到的 namespace 作用。http://blog.dotcloud.com/under-the-hood-linux-kernels-on-dotcloud-part

    這樣要怎麼做到模擬作業系統呢? 一個 linux distributions 就是一個 linux kernel 加上很多其他程式。 namespace 沒辦法跑 kernel。不過那不重要,反正核心都差不多。剩下的程式,就可以把它丟進 namespace 跑,而那些程式在跑的時候,會以為整台電腦都是他的(因為被 namespace) 隔離了。所以他會建立 init(pid = 1) 程序,再做一些其他開機步驟。有 system call 就由 host 的 kernel 來處理。他活在虛擬的世界,還活得很開心。

虛線表示那兩個是同一個。箭頭表示程序間的父子關係。有箭頭的表示子程序。

                       
    如上圖所示, A namespace 的程序無法看到 root namespace 的程序。因此,我們可以在 A namespace 跑 centos 的開機程序,也可跑 ubuntu 的開機程序。在 A namespace 哩,無法察覺 root namespace 存在,他們會以為自己就是 root namespace。

    解決了程序的問題後,還有一個問題,檔案系統。基本上,用原本系統的檔案系統即可做到模擬作業系統,不過 docker 選擇使用 aufs,選用 aufs 還有個好處是可以做版本控制。

    Aufs,全名叫 advanced multi layered unification filesystem。恩,名字不太重要。基本上就分層。以下圖為例:

                                   


       上圖表示至少兩個檔案系統,你可以從 Delete file B 這個往下看,那你看到的檔案系統會是對 Base 加了C(B一加刪,最後看不到)。而從 Add file E 往下看,則看到的檔案系統是對 Base 加了 ADE 這三個檔案。每層只記錄和上一層的差異。要顯示完整的作業系統則把自己到 base 各層 union 起來。

       介紹完 lxc 和 aufs , 來看看 docker 如何整合。



    如上圖所示, docker 把客端作業系統安裝在 aufs 上,你可以 commit 你的修改,他會把你的修改當作一層新的 layer 蓋在上一層。當你要 run 客端作業系統時,他會在你要跑的 image 上面再蓋一層,叫做 container(image 是 read only)。之後 docker 會建立新的 namespace,把 namespace 的 root directory,然後執行 debian 的 init 程序(開機程序),這樣客端作業系統就成功執行了。


如果你想更了解 docker,強烈推薦閱讀  http://blog.dotcloud.com/category/under-the-hood
也可以參考我演講投影片:http://www.slideshare.net/ya790026/docker-33456641

參考資料:

2013年11月22日 星期五

mysql 常用管理指令

因為最近常常要用到 mysql ,只好做個小筆記。

給使用者所有權限
GRANT ALL PRIVILEGES ON dbTest.* To 'user'@'hostname' IDENTIFIED BY 'password';

建立一個資料庫與使用者,給他資料庫所有權限 。
CREATE USER 'test'@'localhost' IDENTIFIED BY 'password';
create database test;
grant all privileges on test.* to test@localhost ;
登入 mysql
$ mysql -u root -p 
修改使用者密碼
UPDATE mysql.user SET Password=PASSWORD('MyNewPass') -> WHERE User='root';
忘記 root 密碼,先停用 mysql server。

$ mysqld_safe --skip-grant-tables
$ mysql --user=root mysql
mysql> update user set Password=PASSWORD('new-password') where user='root';
mysql> flush privileges;
mysql> exit;

2013年11月1日 星期五

CPython 2.7.5 中使用 join 寫法來做字串加法比較快?還是使用 += ?

    如果你在 windows 下執行下面的程式碼,你會發現用 += 會比 join 寫法還快

a='''
def test_join():
    data = []
    for i in range(100):
        data.append('ad')
    return ''.join(data)
test_join()
'''
b = '''
def test_connect():
    data = ""
    for i in range(100):
        data += 'ad'
    return data
test_connect()
'''

import timeit
print timeit.timeit(a, number=10)
print timeit.timeit(b, number=10)

2013年10月20日 星期日

從 windows 8 連 samba

    這篇文章只是為了備忘。如果你在 windows 8 或其他版本,想連到 samba,明明伺服器設定
也正確。從 linux 連也可以,只有 windows 不能連。那麼,你可能需要啟用 smb 的協定。


    下面是啟用的步驟,請打開 cmd ,輸入下面的指令
  • To enable SMBv1 on the SMB client, run the following commands:
    sc.exe config lanmanworkstation depend= bowser/mrxsmb10/mrxsmb20/nsi 
    sc.exe config mrxsmb10 start= auto
  • To enable SMBv2 and SMBv3 on the SMB client, run the following commands:
    sc.exe config lanmanworkstation depend= bowser/mrxsmb10/mrxsmb20/nsi 
    sc.exe config mrxsmb20 start= auto

探討 scala 的函數呼叫與自動推論型別所產生的模糊情形

     scala 是靜態型別語言,不過他擁有自動推論型別的功能,因此可以減少重複宣告型別的情形。如以下程式碼:


    val a = 1

    由於 1 是整數,因此可以推論出 a 的型別為 int。再看看定義 function 的情形。


    def func(a:int, b:int) = a + b

    func 接受兩個參數,他的資料型別是 int,編譯器會推論出 func 的回傳值為 int。

2013年10月13日 星期日

浮點數除於零的行為

    程式語言中,整數除於零,基本上都會讓程式無法執行或是拋出類似例外的錯誤。可是如果是浮點數除於零的話,則行為就不一定。在 IEEE 754 的標準,浮點數除於零的話,會產生代表無窮大的值。c, c++, tcl, ruby, scala, java 遵循 IEEE 754。不過 perl, python , go 語言,發生浮點數除於零的情形,則是產生類似例外的訊息,而非給定無窮大的值。其他的語言沒有測試,這篇純粹紀錄各種語言的細微差異。

2013年8月20日 星期二

一個 golang 的非同步 stdin 的範例

    這篇文章只是一個簡單的例子,示範 golang 的 go 與 select 用法。 golang 原生就支援多序了,因此可以寫起來很簡潔。


2013年8月8日 星期四

C語言中,在呼叫函數前和呼叫函數後,執行某段程式碼

    gcc 有很多強大的選項,可以讓我們作到一些特別的事情。假設我們希望每次呼叫函數前,都先執行特定的動作。我們可以這樣寫。


在 C 語言中,發生錯誤時,顯示 backtrace

    在很多語言中, 如 python, java 等,當程式遇到錯誤時,他會顯示 backtrace( 就走到目前函數位置的呼叫流程)。在 C 語言中,預設只會出現錯誤訊息,就沒了。

    不過在 linux 下,你可以使用 catchsegv 來顯示程式錯誤時的 backtrace。用法大概向這樣
catchsegv ./a.out