Sharing

2012年7月29日 星期日

Python args and kwargs parsing


python 在 argument 的處理上有很大的彈性, 以前在寫 C/C++ 時, 總會為了參數要怎麼傳, 怎麼樣才能讓參數穿透層層關卡到達最下層而煩惱, 程式要寫的有架構常常必須要分層次, 彼此之間要用定好 interface 隔開, 但間接造成的問題是, 如果在最上層要新增新的參數, 你很可能要一層一層的改下去, 也是很頭痛, 但 python 在這部份就比較有方法可以做到.

先看這兩篇在介紹 args 和 kwargs, args 是 list (有順序性), kwargs 是 dict (無順序性)
http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/
http://docs.python.org/tutorial/controlflow.html#keyword-arguments

第一個例子, 說明 *args 會把 argument 按照順序收集起來, 所以 test_var_args 除了第一個 "fargs" 一定要傳之外, 其它的參數可以任意的接在後來
>>> def test_var_args(farg, *args):
...    print "formal arg:", farg
...    for arg in args:
...        print "another arg:", arg
>>> test_var_args(1, "two", 3)
formal arg: 1
another arg: two
another arg: 3

第二個例子, 說明 **kwargs 會把 keyword argument 收集起來放進 kwargs 這個字典中, test_var_args 除了第一個 "fargs" 一定要傳之外, 其它的參數可以用 keyword argument 的方式加進去
>>> def test_var_kwargs(farg, **kwargs):
...     print "formal arg:", farg
...     for key in kwargs:
...         print "another keyword arg: %s: %s" % (key, kwargs[key])
...
>>> test_var_kwargs(farg=1, myarg2="two", myarg3=3)
formal arg: 1
another keyword arg: myarg2: two
another keyword arg: myarg3: 3

最後是把兩個結合起來
>>> def test_vars(farg, *args, **kwargs):
...     print "formal arg:", farg
...     for arg in args:
...        print "another arg:", arg
...     for key in kwargs:
...         print "another keyword arg: %s: %s" % (key, kwargs[key])
...
>>> test_vars(1, 2, 3, myarg4="four", myarg5=5)
formal arg: 1
another arg: 2
another arg: 3
another keyword arg: myarg4: four
another keyword arg: myarg5: 5

所以有了 args 和 kwargs, python 的函式在傳參數時就可以做到不定個數、不定長度. 那就可以玩一些變化讓這些參數具有穿透力. 這有什麼用處呢? 在某些情況, 假設你有 f1, 內部會用到 f2, 你為了讓呼叫的人也可以控制到 f2, 所以你必須也要在 f1 參數上也加上 f2 的參數. 就像下面這個例子

>>> def f1(a, b=1):
...     f2(b)
...
>>> def f2(b=1): pass
...

這樣的寫法會有什麼困擾呢?

第一個是有關於預設值, 如果當我們呼叫 f1 時, 預期在不給 b 的狀況下, 能夠直接使用 f2 的設定值, 我們就只能在 f1 中也針對 b 設定一樣的預設值, 否則就會不同步, 產生錯誤的行為
第二個是如果當 f2 增加參數時或改變參數預設值時, f1 也必須要跟著修改

所以我們可以利用 args/kwargs 來讓參數有穿透力, 我們在 f1 只關心 a 這個參數, 於是我們把 a 拿走, 剩下的全部傳進 f2, 而 f2 只需要 b, 於是它把 b 拿走, 剩下的傳進 f3.

>>> def f3(c):
...     print c
...
>>> def f2(b, *args, **kwargs):
...     print b
...     f3(*args, **kwargs)
...
>>> def f1(a, *args, **kwargs):
...     print a
...     f2(*args, **kwargs)
...
>>> f1(1, 2, 3)
1
2
3
>>> f1(1, 2, c=3)
1
2
3
>>> f1(1, b=2, c=3)
1
2
3


如果我們在 f1 新增一個參數 d, 為了向前相容, 所以我們給他一個預設值, 所以原來的程式碼也還可以繼續使用, 新的程式碼如果需要修改參數 d, 也只需要在呼叫時, 多一個 keyword argument 即可, f2/f3 完全不需要修改, 真的是很方便.

>>> def f1(c, d=2):
...     print c,d
...
>>> f3(3, c=5, b=4)
3
4
5 2
>>> f3(3, c=5, b=4, d=6)
3
4
5 6


再來下一個問題:是否有辦法觀察一個函式的參數有那些? 而且是否有預設值, 我們可以利用 inspect 這個模組.
http://stackoverflow.com/questions/196960/can-you-list-the-keyword-arguments-a-python-function-receives
http://docs.python.org/library/inspect.html?highlight=inspect#inspect

>>> def func(a,b,c=42, *args, **kwargs): pass
...
>>> inspect.getargspec(func)
ArgSpec(args=['a', 'b', 'c'], varargs='args', keywords='kwargs', defaults=(42,))


除了原來連結寫的幾個函式很有用處之外, 我也寫了一個小工具, 主要是用來應付如果要乎叫的函式不能傳進 kwargs 時, 就必須要先把 kwargs 過濾過, 把可接受的部份留下, 然後去除掉不能使用的部份

def filter_args(func, kwargs):
    args, varargs, varkw, defaults = inspect.getargspec(func)
    args_with_default = args[-len(defaults):]
    valid_kw = dict()

    if not varkw:
        for arg in kwargs:
            # remove unaccepted argument
            if arg not in args:
                continue
            # argument = None but with default value
            elif not kwargs[arg] and arg in args_with_default:
                continue
            valid_kw.update({arg:kwargs[arg]})

    return valid_kw


這在 command line 的參數處理滿有用處的,

def shellcmd(func, *argv, **kwargs):
    kwargs = filter_args(func, kwargs)
    func(*argv, **kwargs)



monitor network traffic -- iftop/nethogs


之前有使用過 ntop 這套工具, 它的缺點就是 loading 比較重, 需要額外安裝 apache, 不過好處是網路的流量圖表做的很好, 所以要給老闆看美麗的報表時, 可以使用它.


但如果只是想要馬上看看網路狀況, 做一些環境的檢測時, 那我覺得下面這篇文章內介紹的兩個工具應該會派上用場.
http://techthrob.com/2010/07/26/how-to-monitor-network-traffic-in-linux/

這篇主要是介紹兩個可以觀察網路流量的工具 iftop/nethogs. 安裝上很簡單, 而且可以立即使用, 應該是不需要任何的教學 :)

pjack@ubuntu:~$ sudo apt-get install iftop
pjack@ubuntu:~$ sudo apt-get install nethogs
pjack@ubuntu:~$ sudo iftop -i eth0
pjack@ubuntu:~$ sudo nethogs eth0


2012年7月27日 星期五

Avahi 筆記

Avahi 是一個在內網內, 用來廣播自己是什麼服務以及用來尋找那些服務的一個工具, 有點像是在一個社區內有個廣播系統, 大家利用他就可以找到郵局在那裡, 醫院在那裡, 而不必事先知道郵局或醫院的名字. 據了解 avahi 是利用 multicast 來做到這件事,安裝上要細分的話可以分成 server 以及 client.


client 的部份主要是用來聽廣播, 得到網路上所有可使用的服務.
pjack@ubuntu:~$ sudo apt-get install avahi-utils
pjack@ubuntu:~$ avahi-browse --help
avahi-browse [options] <service type>
avahi-browse [options] -a
avahi-browse [options] -D
avahi-browse [options] -b

    -h --help            Show this help
    -V --version         Show version
    -D --browse-domains  Browse for browsing domains instead of services
    -a --all             Show all services, regardless of the type
    -d --domain=DOMAIN   The domain to browse in
    -v --verbose         Enable verbose mode
    -t --terminate       Terminate after dumping a more or less complete list
    -c --cache           Terminate after dumping all entries from the cache
    -l --ignore-local    Ignore local services
    -r --resolve         Resolve services found
    -f --no-fail         Don't fail if the daemon is not available
    -p --parsable        Output in parsable format
    -k --no-db-lookup    Don't lookup service types
    -b --dump-db         Dump service type database

# 不間斷的聽取廣播內容, 一但有更新就會顯示出來
pjack@ubuntu:~$ avahi-browse -a
+   eth0 IPv6 Virtualization Host centos                    Virtual Machine Manager local
+   eth0 IPv4 Virtualization Host centos                    Virtual Machine Manager local

# 你也可以只聽特定的服務, 並且加上 -t 參數, 表示不需要持續的聽廣播
pjack@ubuntu:~$ avahi-browse -t _nodeinfo._tcp
+   eth0 IPv6 bmc-00:26:2d:0a:35:a4                         _nodeinfo._tcp       local
+   eth0 IPv4 bmc-00:26:2d:0a:35:a4                         _nodeinfo._tcp       local


# 可以再加上 -r, 除了可以獲得該服務的 ip address/hostname 之外, 
# 其實有時候該服務也會加上更多有關自己的訊息在 "txt" 欄位
pjack@ubuntu:~$ avahi-browse -r -t _mongodb._tcp
+   eth0 IPv4 db on mongodb-001                              _mongodb._tcp      local
=   eth0 IPv4 db on mongodb-001                              _mongodb._tcp      local
   hostname = [mongodb-001.local]
   address = [172.17.255.33]
   port = [0]
   txt = []

# -p 這個參數是把 output 格式變成方便處理一點, 但在舊一點的版本還沒有這個功能
pjack@ubuntu:~$ avahi-browse -r -p -t _mongodb-db._tcp
+;eth0;IPv4;db\032on\032mongodb-001;_mongodb._tcp;local
=;eth0;IPv4;db\032on\032mongodb-001;_mongodb._tcp;local;mongodb-001.local;172.17.255.33;0;


server 的部份主要是要告訴大家你自己是誰, 提供什麼服務
pjack@ubuntu:~$ sudo apt-get install avahi-daemon

裝好 package 之後, 就可以開始編寫你自己的服務, 預設是放在 /etc/avahi/services 下, 而在 /usr/share/doc/avahi-daemon/examples 裡有一些現成的範例
主要需要設定的
1. service protocol, ipv4/ipv6/all
2. service 名稱,
3. port number
其它細節可以參考這裡
http://manpages.ubuntu.com/manpages/precise/man5/avahi.service.5.html
<service-group>

  <name replace-wildcards="yes">%h</name>

  <service protocol="ipv4">
    <type>_ssh._tcp</type>
    <port>22</port>
  </service>

</service-group>

在顯示上不想要顯示 _xxxx._tcp 可以透過設定 /usr/share/avahi/service-types
那就可以讓別人看到 Human readable 的文字
pjack@ubuntu:~$ avahi-browse -t _libvirt._tcp
+   eth0 IPv6 Virtualization Host localhost                 Virtual Machine Manager local

實際應用上, 我曾因為 ipv6 打開的關係造成了一些困擾, 所以如果想要把 ipv6 關掉, 可以在 /etc/avahi/avahi-daemon.conf 內關掉 ipv6. 也可以設定只對那些 interface 發送廣播.
[server]
#host-name=foo
#domain-name=local
#browse-domains=0pointer.de, zeroconf.org
use-ipv4=yes
use-ipv6=no
allow-interfaces=eth0

因為 avahi 是一個 daemon, 如果想要和這個 daemon 溝通, 可以通過 dbus. 下面是 Python 的範例. 有一次我無法將 avahi-daemon 叫起來, 後來才發現是 dbus 這個 service 死掉了, 一但把 dbus 叫起來之後, avahi 就可以啟動了
http://stackoverflow.com/questions/3430245/how-to-develop-an-avahi-client-server
pjack@ubuntu:/etc/avahi$ ps aux | grep daemon
102        506  0.0  0.0  23908  1000 ?        Ss   08:48   0:00 dbus-daemon --system --fork --activation=upstart
avahi      523  0.0  0.0  32560  1952 ?        S    08:48   0:00 avahi-daemon: running [wistor.local]
avahi      524  0.0  0.0  32172   472 ?        S    08:48   0:00 avahi-daemon: chroot helper

如果覺得透過 dbus 溝通有點麻煩的話, 也可以直接透過 bash command, 下面是我寫的一個小程式
def _avahi_browse(service):
    output = subprocess.check_output('avahi-browse -r -t %s' % service, shell=True)

    node = {}
    nodes = []
    for line in output.splitlines():
        if line.startswith(' '):
            key,value = line.split('=')
            key = key.strip(' ')
            value = value.strip(' []')
            node.update({key:value})
        else:
            if node:
                node['hostname'] = node['hostname'].strip('.local')
                nodes.append(node)
            node = {}
    if node:
        node['hostname'] = node['hostname'].strip('.local')
        nodes.append(node)
    return nodes


最後分享的一個經驗是, 在使用 avahi 要注意的一點就是他為了要降低 loading, 所以更新的速度並不是很快, 一般他都是直接從 cache 裡面抓資料給你, 所以有時你會發現有的服務不存在了, 但從 avahi-browse 還是會看到它, 所以拿到資料後, 你還是要處理有可能服務已經不存在的情況. 不過如果有新的服務加進來, 因為是主動 broadcast, 所以你一定會接到更新. 如果你一定要拿到最新的情況, 目前我已知的方法就只有重啟 avahi-daemon, 比較耗時間.



Reference link:

http://manpages.ubuntu.com/manpages/precise/man5/avahi.service.5.html
http://manpages.ubuntu.com/manpages/precise/man8/avahi-daemon.8.html
http://manpages.ubuntu.com/manpages/precise/man1/avahi-browse.1.html
http://en.gentoo-wiki.com/wiki/Avahi


2012年7月24日 星期二

git 好文分享



Git-rebase 筆記
這篇是我目前為止看到把 rebase 解釋的最清楚的一篇, 之前看了好幾篇, 不論是中文或是英文都無法理解全部的用法, 但這篇配合小例子, 把 rebase 的所有用法都講的很清楚, 所以很推薦
http://blog.yorkxin.org/2011/07/29/git-rebase

Git branching model
這篇主要是在講解一般軟體開發上應該要怎麼維護你的 branch, 其實這樣的觀念不管是 svn/git 都通用, 尤其是長期開發的 project 都應該要這樣管理
http://nvie.com/posts/a-successful-git-branching-model/

Git immersion
帶著你一步步的操作 git, 算是入門篇
中文: http://gitimmersion-apputu.rhcloud.com/
英文: http://gitimmersion.com/

寫給大家的 Git 教學
http://www.slideshare.net/littlebtc/git-5528339

2012年7月21日 星期六

Android x86 系統初體驗


最近因為在上一些課程, 意外能接觸到 Android X86 系統, 覺得還滿新鮮的, 再加上虛擬機的幫助, 你可以很輕鬆的在家的電腦上使用到 Android 系統, 速度也不慢, 再搭配 Android SDK, 應該就可以讓很多人可以更輕易的開發軟體以及試用. 我自己則是想要在電腦上玩一些 Android 的小遊戲, 所以也覺得很興奮.

英文官網
http://www.android-x86.org/

中文安裝介紹, 連進去後, 選取左邊
Android 網站資料庫設計 -> 02-Android 系統 -> 02-Android 平板電腦系統
http://tobala.net/x/Cloud2010/CloudTech-BCC-201003.html
裡面有三則, 介紹了如何在 KVM 上安裝 Android X86 系統, 以及安裝好了之後要如何設定, 其中很重要的是網卡的設定, 必須改成 pcnet, 否則 Android X86 會認不得網卡. 那你就不能上網囉~

英文安裝介紹
http://www.android-x86.org/documents/installhowto/

如果是用vmware 安裝, 網卡的部份必須要手動修改 .vmx 檔案, 有點就像是 kvm 的.xml 檔. 將網卡的設定改成 vlance.  (ethernet0.virtualDev = "vlance" )
http://www.vladan.fr/how-to-install-android-in-vmware-workstation/


我安裝的是 android-x86-4.0-RC2-eeepc.iso
http://www.android-x86.org/download
安裝好之後就可以看到以下的畫面, 就跟真的在使用平版一樣, 只不過平常是用手在滑動, 現在只能用滑鼠滑動, 有點吃力就是了.


網路設定好之後也可以上網


不過現在還是有點困擾, 無法將這台虛擬機器登入 google play, 所以下載遊戲來玩, 真是殘念呀~
http://www.android-x86.org/documents/apphowto
根據官網的說法這件事是不可行的, 但我想這只是官方說法, 一定有辦法處理的, 只是不方便直接寫在官網上, 改天如果找到再和大家法享.


其它參考連結:
http://www.androidemulatorforpc.com/2012/06/android-x86-apps-in-virtualbox.html
http://www.androidemulatorforpc.com/2012/06/google-play-apk-download.html
http://www.addictivetips.com/mobile/what-is-adb-and-how-to-install-it-android/

Android SDK 下載
http://developer.android.com/sdk/index.html

2012年7月5日 星期四

How to distribute your python package?


想要研究一下要怎麼把自己寫的 Python Module 打包起來, 結果意外發現 Python 這方面的工具也是多條支線發展當中, 這應該是有很多歷史因素吧~

http://stackoverflow.com/questions/6344076/differences-between-distribute-distutils-and-setuptools
http://wokslog.wordpress.com/2011/06/04/distutils-diff/
http://ziade.org/2010/03/03/the-fate-of-distutils-pycon-summit-packaging-sprint-detailed-report/

  • distuils
          最剛開始的工具, 但太陽春
  • Setuptools
          補足了 distutils 許不足的地方, 並且大部份的行為都和 distuils 相容
  • Distribute
          是 setuptools 的一個支線, 但後來因為發展太慢, 所以沒有辦法繼續開發下去
  • Distuils2
          因為前兩者的一些狀況讓人覺得混亂, 所以甘脆開了個完全獨立的案子, 重新開發  distribution 工具,不過同時也儘量和前三者相容。

State of packaging 

結論就是, 目前的狀況還是以  Setuptools or Distribute 為主, 兩者的功能沒有相差很多.下面這個網站就教導了大家應該要如何安排 Folder Structure 以及如何打包你的 module, 照著作就可以了。

Guide
http://guide.python-distribute.org/introduction.html

Distribution 的問題解決之後, 接下來就是 Installer 的問題, 你打包好的東西, 裡面都會有個 setup.py, 一般情況下可以直接執行這個 setup.py 就可以安裝你的 module. 不過還是有些麻煩, 必須要解壓縮, 而且重點是別人要怎麼找到你打包好的 package 呢? Installer 在這裡發揮的角色就是負責幫你找出來要去那裡下載 package, 以及處理相關 dependency, 最後再幫你安裝好, 其實和 Debian/Ubuntu 裡的 apt-get 是一樣的角色
  • easy_install
          http://packages.python.org/distribute/easy_install.html
          早期的 installer, 但目前也還是很多人使用
  • Pip
           http://www.openfoundry.org/tw/tech-column/8536-introduction-of-python-extension-management-tools
           取代 easy_install, 目前最 Popular 的 python 安裝工具, 事實上 Pip 在安裝時還是會用到 easy_install, 只是額外再多提供了一些功能, 像是完整的列出你目前已安裝好的 python package 之類的, 讓你在管理你的 python package 更方便

而不管是 easy_install 或是 Pip, 預設都會到  PyPI http://pypi.python.org/pypi 找尋已發行的 Python 套件

另外我發現 pip install 在執行時, 會加上  --single-version-externally-managed, 這和直接執行 easy_install 產生的結果會有些不同, 主要是資料夾的排放方式
http://stackoverflow.com/questions/368636/questions-about-setuptools-and-alternatives
http://peak.telecommunity.com/DevCenter/setuptools#install-command

Easy_install 會將 egg 資料夾和你的 package 統一擺放在一個資料夾下, 如下圖
/usr/local/lib/python2.7/dist-packages
├── easy-install.pth
└── python_novaclient-2012.2-py2.7.egg      <-------------  統一放在這邊下面
    ├── EGG-INFO                     <--------------  EGG 資料夾
    │   ├── PKG-INFO
    │   ├── ...
    │ 
    └── novaclient                        <---------------  你的 package
        ├── base.py
        ├── client.py
        ├── ...

而用 pip install 產生的結果會如下

/usr/local/lib/python2.7/dist-packages
├── easy-install.pth
├── novaclient                                                      <------------ 你的 package
│   ├── base.py
│   ├── client.py
│   ├── ...
│ 
└── python_novaclient-2012.1.egg-info                  <------------ EGG 資料夾
    ├── PKG-INFO
    ├── ...

雖然資料夾擺放的方式不一樣, 不過在使用上是一樣的, 不會造成問題。
最後再提供一兩個教你怎麼擺放 Folder Structure 的 Template.

Folder Template

2014/03/28 Update

目前最新的教戰手則
https://python-packaging-user-guide.readthedocs.org/en/latest/current.html


2012年7月3日 星期二

Python implement local static variable



http://www.saltycrane.com/blog/2008/01/python-variable-scope-notes/

http://stackoverflow.com/questions/68645/static-class-variables-in-python

http://www.daniweb.com/software-development/python/threads/33025/static-variables-in-python

python 其中一個特點是他沒有 local static variable. 在這種情況下有很多方式可以實作出來

其中我覺得最簡易也最漂亮的是利用 python 本身的特性, 所有的 function 其實都是一個 object, 既然是 object, 那我存點東西在裡面是很自然的一件事

>>> def counter():
...     if "static" not in dir(counter):
...         counter.static = 0
...     else:
...         counter.static+= 1
...     return counter.static
...
>>> counter()
0
>>> counter()
1
>>> counter.static = 3
>>> counter()
4
>>>


再來是利用 yield


>>> def counter_generator(start):
...     def inner():
...         k = start
...         while True:
...             k += 1
...             yield k
...     return inner().next
...
>>> counter = counter_generator(100)
>>> counter()
101
>>> counter()
102
>>> counter()
103

利用 inner function, 你可以讀取到 outer variable, 不過因為無法直接修改 outer variable, 如果你試著直接去修改, Python 會認為你要修改的 variable 是 local variable, 我們只好把我們要儲存的東西包裝成 dictionary. 然後在 inner function 才能修改成功. 有點麻煩就是了, 聽說在 python 3.0 已解決這個問題, 有一個新的 keyword 叫 'nonlocal' 可以讓你也直接修改 outer variable.

>>> def counter_generator(start):
...     x = {"count":start}
...     def counter():
...         x["count"] += 1
...         return x["count"]
...     return counter
...
>>> counter = counter_generator(100)
>>> counter()
101
>>> counter()
102
>>> counter()
103


最後當然你也可以利用 Class 來做到 static variable, 不過我覺得有點殺雞用牛刀就是了