Sharing

顯示具有 Python 標籤的文章。 顯示所有文章
顯示具有 Python 標籤的文章。 顯示所有文章

2013年7月10日 星期三

SOAP client in Python

在 stackoverflow 上可以找到一篇比較,  看起來最容易上手的是 SUDS,

http://stackoverflow.com/questions/206154/whats-the-best-soap-client-library-for-python-and-where-is-the-documentation-f/206964#206964

在 PyPi 上找到的 Package 卻有幾個變種
https://pypi.python.org/pypi?%3Aaction=search&term=suds&submit=search

PackageWeight*Description
suds 0.48Lightweight SOAP client
suds-jurko 0.4.1.jurko.45Lightweight SOAP client (Jurko's fork)
suds-philpem 0.4.35Lightweight SOAP client (philpem's fork-and-merge)
suds-vingd 0.4.35Lightweight SOAP client (philpem's fork-and-merge)

suds 最後更新的時間是 2010/09/15, 也有點久, 難怪會有很多變種, 不過我要做的事很單純, 試用了之後也沒問題, 就先使用了. 推薦給只需要簡單功能的 DevOp 工作人員們!

2013年6月13日 星期四

Problem: How to manage python package from apt-get?

目前 pip&distibute 解決了大部份  python package 的 installation 時遇到的 dependency 問題.
http://pythonhosted.org/distribute/
http://guide.python-distribute.org/


但如果遇到和外界的 c library 有 dependency 時, ex: pyzmq --> libzmq3  或是 pymysqldb --> libmysqlclient, 就無法在 python 的世界裡解決了, 必須要跳脫出來. 我想這也是為什麼 Ubuntu 在 python package 之外經常要再包一層看似沒做什麼事情的 .deb 檔.  ex: python-zmq, python-crypto, 透過 .deb 檔可以讓 python package 和其它 c library 建立起關係. 並且有時候可以做更多的事情, 比方說在 /etc/ 下建立 .conf 檔案, 在 /etc/init.d/ 下建立這樣一來 python package 和 OS 的關係


但問題又來了,  precise 內建的 python-crypto 是 2.4 版, 如果我要裝 2.6 版要怎麼做?

$ pip install pycrypto==2.6 
$ dpkg -l | grep python-crypto
ii  python-crypto                                       2.4.1-1ubuntu0.1                                    cryptographic algorithms and protocols for Python
$ pip freeze | grep pycrypto
pycrypto==2.6

用 pip 直接安裝是最快速的方式, 但會讓 apt/dpkg 內的資訊不一致, 這在 management 以及 deployment 有時會造成麻煩
目前還沒有找到直接的解法, 先紀錄下來, 未來有找到解法時再更新

相關 material:

http://www.g-loaded.eu/2008/01/28/how-to-extract-rpm-or-deb-packages/

http://www.pyinstaller.org/

http://stackoverflow.com/questions/4700178/should-i-bundle-c-libraries-with-my-python-application

http://www.debian.org/doc/packaging-manuals/python-policy/ch-python.html

http://developer.ubuntu.com/packaging/html/
http://developer.ubuntu.com/packaging/html/python-packaging.html
http://askubuntu.com/questions/90764/how-do-i-create-a-deb-package-for-a-single-python-script
https://wiki.ubuntu.com/PackagingGuideDeprecated/Python









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)



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, 不過我覺得有點殺雞用牛刀就是了


2012年6月19日 星期二

Build Python 2.7.3 on Centos 5.7

http://toey.tc20.net/2010/08/04/install-python-2-7-gevent-on-centos-5-x86_64/

CentOS 預設安裝的是 Python 2.4, 實在是太老了...

[root@centos ~]$ yum install gcc gcc-c++.x86_64 compat-gcc-34-c++.x86_64 openssl-devel.x86_64 zlib*.x86_64
[root@centos ~]$ wget http://www.python.org/ftp/python/2.7.3/Python-2.7.3.tgz
[root@centos ~]$ tar xzvf Python-2.7.3.tgz
[root@centos ~]$ cd Python-2.7.3
[root@centos Python-2.7.3]$ ./configure --prefix=/opt/python27
[root@centos Python-2.7.3]$ make
[root@centos Python-2.7.3]$ make install

安裝要一陣子, 好了之後要設定環境路徑

[root@centos ~]$ cat ~/.bash_profile
# .bash_profile

# Get the aliases and functions
if [ -f ~/.bashrc ]; then
        . ~/.bashrc
fi

# User specific environment and startup programs

# 把 /opt/python27/bin 加上去
PATH=$PATH:$HOME/bin:/opt/python27/bin

export PATH
unset USERNAME

[root@centos ~]$ source ~/.bash_profile
[root@centos ~]$ echo "/opt/python27/lib" > /etc/ld.so.conf.d/python27.conf
[root@centos ~]$ ldconfig

接下來把 setuptools 也裝上去, 你就可以安裝其它所有的 project 了

[root@centos ~]$ wget http://pypi.python.org/packages/2.7/s/setuptools/setuptools-0.6c11-py2.7.egg
[root@centos ~]$ sh setuptools-0.6c11-py2.7.egg

裝好後, 你會發現在 /opt/python27/bin/ 下有一個 easy_install-2.7, 你可以用它來裝其它 package, 以 pip 為例

[root@centos ~]$ easy_install-2.7 pip
Searching for pip
Reading http://pypi.python.org/simple/pip/
Reading http://pip.openplans.org
Reading http://www.pip-installer.org
Best match: pip 1.1
Downloading http://pypi.python.org/packages/source/p/pip/pip-1.1.tar.gz#md5=62a9f08dd5dc69d76734568a6c040508
Processing pip-1.1.tar.gz
Running pip-1.1/setup.py -q bdist_egg --dist-dir /tmp/easy_install-fchPYD/pip-1.1/egg-dist-tmp-ztv_Wk
warning: no files found matching '*.html' under directory 'docs'
warning: no previously-included files matching '*.txt' found under directory 'docs/_build'
no previously-included directories found matching 'docs/_build/_sources'
Adding pip 1.1 to easy-install.pth file
Installing pip script to /opt/python27/bin
Installing pip-2.7 script to /opt/python27/bin

Installed /opt/python27/lib/python2.7/site-packages/pip-1.1-py2.7.egg
Processing dependencies for pip
Finished processing dependencies for pip

要記得執行程式要用 python2.7

[root@centos  ~]$ python2.7
Python 2.7.3 (default, Jun 19 2012, 11:11:42)
[GCC 4.1.2 20080704 (Red Hat 4.1.2-52)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>


2012年5月22日 星期二

Install Python on Window7


發現要裝同樣的東西在 Windows  上, 還真不是普通的困難,

雖然到最後我仍然沒有解掉全部的問題, 但還是紀錄一下好了...

1. Python 2.7
    http://www.python.org/getit/

2. Pip
    http://www.lfd.uci.edu/~gohlke/pythonlibs/#pip

3. PyCrypto
    http://yorickdowne.wordpress.com/2010/12/22/compiling-pycrypto-on-win7-64/

如果想要試著直接用 pip 裝 PyCrypto , 可能會遇到他找不到適合 compiler 的問題, 我試了網路上建議的方式
a. 先安裝 mingw32
b. 下載 PyCrypto 的原始碼
c.  python setup.py build -c mingw32
來嘗試安裝它, 但都沒有成功. 會遇到 raise RuntimeError("chmod error"), 情況和這篇描述的一樣
http://stackoverflow.com/questions/9131323/problems-installing-python-fabric-on-windows-7

最後我只好放棄裝最新版的 PyCrypto, 而直接下載別人做好的

4. Fabric
    http://www.lfd.uci.edu/~gohlke/pythonlibs/#base
    或是 pip install fabric

5. pywin32
    http://www.lfd.uci.edu/~gohlke/pythonlibs/#pywin32
    如果你遇到 ImportError: No module named win32com.shell.shell, 就請安裝這個

這篇找到一篇有人利用 mingw32 編譯 pycrypto 成功的範例
http://posterous.adambard.com/installing-fabric-under-windows-7-64-bit-with

2011年11月20日 星期日

Python 2.7 Standard Library 筆記 -- Regular Expression

教學連結
http://docs.python.org/howto/regex.html#regex-howto

re Module
http://docs.python.org/library/re.html


  • \d 就是 [0-9] \D 則是 [^0-9]
  • \s 就是 [ \t\n\r\f\v] \S 則是 [^ \t\n\r\f\v]
  • \w 就是 [a-zA-Z0-9_] \S 則是 [^a-zA-Z0-9_]
  • [] 是一堆字元的集合, 只要出現裡面任一字元, 就算符合
  • * 是重覆  0 ~ 無限多次
  • + 是重覆 1 ~ 無限多次
  • ? 是重覆 0 或 1 次

  • re.compile 會回傳一個 pattern object,利用這個 pattern object 可以持續的分解一段文字,還滿好用的
  • class SRE_Pattern(__builtin__.object)
     |  Compiled regular expression objects
     |
     |  Methods defined here:
     |
     |  findall(...)
     |      findall(string[, pos[, endpos]]) --> list.
     |      Return a list of all non-overlapping matches of pattern in string.
     |
     |  finditer(...)
     |      finditer(string[, pos[, endpos]]) --> iterator.
     |      Return an iterator over all non-overlapping matches for the
     |      RE pattern in string. For each match, the iterator returns a
     |      match object.
     |
     |  match(...)
     |      match(string[, pos[, endpos]]) --> match object or None.
     |      Matches zero or more characters at the beginning of the string
     |
     |  scanner(...)
     |
     |  search(...)
     |      search(string[, pos[, endpos]]) --> match object or None.
     |      Scan through string looking for a match, and return a corresponding
     |      MatchObject instance. Return None if no position in the string matches.
     |
     |  split(...)
     |      split(string[, maxsplit = 0])  --> list.
     |      Split string by the occurrences of pattern.
     |
     |  sub(...)
     |      sub(repl, string[, count = 0]) --> newstring
     |      Return the string obtained by replacing the leftmost non-overlapping
     |      occurrences of pattern in string by the replacement repl.
     |
     |  subn(...)
     |      subn(repl, string[, count = 0]) --> (newstring, number of subs)
     |      Return the tuple (new_string, number_of_subs_made) found by replacing
     |      the leftmost non-overlapping occurrences of pattern with the
     |      replacement repl.
     |
    
    
  • 要注意 raw string 及非 raw string 的差別,一般會直接使用 raw string,比較直覺
  • # 第一個方案用一般  string 來找尋 '\n' 必須要輸入 "\\n"
    >>> p  = re.compile('\\n', re.IGNORECASE)   
    >>> p.findall("\np")
    ['\n']
    >>> p.findall("\\np")
    []
    # 第二個方案用 raw string, 就直接輸入 "\n" 就可以了,效果是一樣的
    >>> p  = re.compile(r'\n', re.IGNORECASE)
    >>> p.findall("\np")
    ['\n']
    >>> p.findall("\\np")
    []
    >>> print p.findall("\np")[0]
    
  • Compilation Flags
    • IGNORECASE, 這個應該很方便, 可以省去很多必須注意的小地方
    • MULTILINE, 可以自動把每一行分開解析
  • A | B 可以找尋 A 或 B, A 和 B 分別是一個 RE
  • ^A 可以指定字串起始點必須要符合 A, A 是一個 RE
    • 不過在集合 [] 內, ^ 是當反相的意思
  • A$ 可以指定字串尾巴必須要符合 A, A 是一個 RE
  • \bS\b 用來指明要找尋的字串S前面或是後面必須要有分隔的字元
  • \BS\B 剛好是\b 的相反, 字串 S 的前面或是後面不可以是分隔的字元
  • () 用在區隔 group, 可以讓你一次在一個字串內找尋兩個 pattern, 甚至這兩個 pattern 是有交互作用的
  • >>> p = re.compile('(a(b)c)d')
    >>> m = p.match('abcd')
    >>> m.group(0)
    'abcd'
    >>> m.group(1)
    'abc'
    >>> m.group(2)
    'b'
    
    • (?P...) ,指定 group 的名字, (?P=name) 是舊的寫法
    • (?:...), 不指定 group 的名字, 只 mapping 但不抓回來, 這邊的寫法有點難以理解
    • >>> m = re.match("([abc])+", "abc")
      >>> m.groups()
      ('c',)
      >>> m = re.match("(?:[abc])+", "abc")
      >>> m.groups()
      ()
      
    • (...)\1 可以用來指明第幾個 group, \1 表示要搜尋第一個 group
  • Splitting Strings
    • 也可以利用 RE 來 split string, 符合的字串將會被消去, 然後分段整個字串
    • 如果不想把符合的字串消去, 就必須加上 group
    • >>> p = re.compile(r'\W+')
      >>> p2 = re.compile(r'(\W+)')
      >>> p.split('This... is a test.')
      ['This', 'is', 'a', 'test', '']
      >>> p2.split('This... is a test.')
      ['This', '... ', 'is', ' ', 'a', ' ', 'test', '.', '']
      
  • Search and Replace
    • 符合的 pattern 可以用在 Replace Rule 當中, 這三種表示法都是同樣的意思 \1 = \g<1> = \g
  • *?、+?、?? 都是 Non-Greedy


2011年11月1日 星期二

Python 學習手冊第三版筆記 (七)


CH.27 例外事件基礎

with/as 環境管理器的敘述滿難懂的,感覺大部份的 with/as 都可以用 try/finally 實作完成,但好處似乎是物件可以事先把對 exception 的處理定義好,ex: 檔案碰到 exception 時會自動關閉,等到別人使用你的物件時,只要透過 with/as 就可以正確的處理 exception ,而不需要讓每個人都煩惱 "如果遇到 exception ,我是否需要去關檔?"

CH.28 例外事件物件

類別式例外事件的概念在 Java 中已發展的很完全,這讓我覺得原作者果然很懶惰 :P 所以只想用最簡單的方式來處理 Exception,不過看的出來 Python 在這部份的設計很明顯的不敷使用,也間接造成了光是 raise 就有很多種奇怪怪的寫法,書的作者也只能嘆氣囉! 這就是 Design 或是架構在一開始沒有設計好的後遺症

CH.29 例外事件的設計


>>> help(sys.exc_info)
Help on built-in function exc_info in module sys:

exc_info(...)
    exc_info() -> (type, value, traceback)
    
    Return information about the most recent exception caught by an except
    clause in the current stack frame or in an older stack frame.



>>> help(os.popen)
Help on built-in function popen in module nt:

popen(...)
    popen(command [, mode='r' [, bufsize]]) -> pipe
    
    Open a pipe to/from a command returning a file object.

>>> help(os.system)
Help on built-in function system in module nt:

system(...)
    system(command) -> exit_status
    
    Execute the command (a string) in a subshell.


PyDoc 、 PyChecker、PyUnit 好用的三大工具!


看到這裡我終於把這本書都讀完囉!

2011年10月31日 星期一

Python 學習手冊第三版筆記 (六)

CH.22 OOP: 大藍圖

  • 可多重繼承,我不一向不太喜歡這個,比較喜歡 Java 的單一繼承,事情有時會簡單一點
  • 每個函式必須加上 self ,我也覺得這個很多餘

CH.23 類別撰碼基礎

  • 和 def 一樣是可執行敘述,執行完後就打包成一個 class 物件,而且是可以動態增加元素
  • >>> class rec: pass    # 一開始裡面什麼都沒有
    
    >>> rec.name = 'Bob'      # 動態增加了一個欄位
    >>> x = rec()
    >>> x.name                      #  x 是 rec 的物件,可以存取到 name
    'Bob'
    >>> x.age                         # 想要存取 age 這個欄位會失敗,因為不存在
    
    Traceback (most recent call last):
      File "< pyshell#20>", line 1, in < module>
        x.age
    AttributeError: rec instance has no attribute 'age'
    >>> rec.age = 3              # 那我們就幫他增加 age 這個欄位吧!
    >>> x.age                        #  可以成功增加
    3
    
    除此之外,我們甚至可以動態增加物件裡面的欄位,不過這就不會影響到原生 Class
    >>> class rec:
     name = 'BoB'
     age = 3
    
    >>> x = rec()
    >>> x.length = 180
    >>> x.length                # 成功增加了 length 這個欄位
    180
    >>> rec.length            # 原生的 rec 仍然沒有這個欄位
    
    Traceback (most recent call last):
      File "< pyshell#34>", line 1, in < module>
        rec.length
    AttributeError: class rec has no attribute 'length'
  • __init__ 是建構子,__del__是解構子,在學C++時有聽過三一律,不知道 Python 有沒有符合這樣的特性
  • 可以利用 __add__、__mul__ 覆蓋運算子

另外函式也是可以輕易的被動態換掉,只要第一個參數是 Self 即可,我想這樣稍微解釋了為什麼他規定要每個函式第一個參數都是 Self,這樣的寫法其實很像我們用 C 在模擬 OOP 時,利用第一個參數來傳送 Instance 的道理是一樣的,所以要說 Python 是很先進的 OOP 的語法,好像又不是那麼一回事,只能說他反璞歸真吧~~

>>> def upperName(self):
 return self.name.upper()

>>> rec.method = upperName
>>> x.method()
'BOB'

CH.24 類別撰碼細節

我突然領悟到為什麼書一開頭有講到 Python 的 Class 類別長的像其它 OOP 語言但其實內裡完全不是那麼一回事。一般 OOP 的 Class 是一種宣告,存在於 ROM 之中,而且在一般狀況下會事先 loading 到 Memory 當中,然後 Instance 是 Class 的實作,每份 Instance 都是獨立的個體、獨立的 Memory,換一種方式來說 Class 的宣告是死的,Instance 是活的。然而 Python 的設計我覺得是一個很偷懶的設計,Class 本身就是一個活著的物件,而每個 Instance 看似是獨立的個體,但其實是障眼法,它其實只做了個身體,然後等你要用到右手的時候,它才做個右手給你,等你要用到左手的時候,它才做個左手給你,不然平常它都是直接偷用 Class 內的東西,所以如果你改了 Class 內的值,也會間接影響到這些 Instance,我看到書裡面這個範例時,快笑翻了,怎麼會有這麼偷懶的語言。

>>> class SharedData:
 spam = 42

>>> x = SharedData()
>>> y = SharedData()
>>> x.spam, y.spam
(42, 42)
>>> SharedData.spam = 99
>>> x.spam, y.spam, SharedData.spam
(99, 99, 99)
>>> x.spam = 88
>>> x.spam, y.spam, SharedData.spam
(88, 99, 99)

而且 Class 的宣告和 Instance 的生成,兩者之間看似很有關係,但這關係很容易被破壞,你除了可以在 Instance 上面加上原來就該有的手、腳外,你高興的話也可以臨時在 Class 上增加個翅膀,也可以臨時在 Instance 上增加個尾巴,然後也可以任意的把眼睛的功能變成透視眼功能,在這樣惡搞之下,Instance 其實可能會長的和 Class 一點也不像,不過幸好的是只能做"加法",而不能做"減法",所以 Instance 至少會保有 Class 所有的欄位及功能。

繼承的部份和 Instance 也很像,關係看似很緊,但其實也是很薄弱,Super class 可以動態亂改,Inheritor 也可以動態亂改,看了半天,我覺得繼承其實也是一種 Instance,只是他是一個有名字、可以方便再利用的 Instance。

Java 中有 Interface,C++ 中有 virtual function,Python 裡面沒有這樣的概念,但在實作上卻可以做的出來,只是如果忘了實作的話要等到要用到時才會發現。(這就是所謂了要吃飯時才發現飯忘了煮嗎?!天呀!真的是偷懶到極點的語言)

>>> class Super:
 def delegate(self):
  self.action()

  
>>> class Provider(Super):
 def action(self):
  print 'in Provider'

  
>>> x = Super()
>>> x.delegate()

Traceback (most recent call last):
  File "< pyshell#75>", line 1, in < module>
    x.delegate()
  File "< pyshell#69>", line 3, in delegate
    self.action()
AttributeError: Super instance has no attribute 'action'
>>> x = Provider()
>>> x.delegate()
in Provider


看到這裡其實也突然讓我發現一個事實,Python 的 method binding 在 def / class 中是不會即時檢查的,也不會事先 linking,而是動態的 linking,所以即使用了一個沒有宣告的函式或是物件,也必須要等到真的有被用到時,才會發現這個事情,但也因為這樣的設計,當你要寫兩個會互相用到的函式(放在不同的模組內),而必須互相 import 時卻不會出事

import modb

def funcA(x):
    if x == 1:
        return 0
    return 1 + modb.funcB(x /2)

class ClassA:
    def getInterClass():
        x = modb.ClassB()
        return x


import moda

def funcB(x):
    if x == 1:
        return 1
    return 1 + moda.funcA(x + 1)

class ClassB:
    def getInnerClass():
        x = moda.ClassA()
        return x



接下來看到 __getitem__ 的應用,書中寫到"買一送一堆",真的是太傳神了,而且我覺得這真的很強大,和__iter__不同的是,反覆器只能繞行一次,而索引運算式則可以一直重覆使用,不過反覆器的用法在寫程式上比較直覺。要解決這樣的問題可以做一個反覆器生成器出來。

__getattr__ 和 __setattr__ 就先跳過不看,太複雜了,竟然是用在實作 private 特性。



CH.25 類別的設計

getattr(X,N) 和 X.__dict__[N] 差異在於,前者也會執行繼承搜尋,但後者不會!

有幾個內建的屬性我覺得還滿重要的,雖然少用但在重要時刻可能會派上用場,特別是 Debug 時

  • 每個類別都有內建 __name__來顯示類別的名字
  • 每個類別都有內建 __bases__ 來顯示其繼承的類別
  • >>> class A: pass
    >>> class B: pass
    >>> class C(A,B): pass
    >>> C.__name__
    'C'
    >>> C.__bases__
    (< class __main__.A at 0x000000000279EF48>, < class __main__.B at 0x000000000279EFA8>)
    >>> C.__bases__[0].__name__
    'A'
    
    
  • __class__ ,每一個實體都可以藉此來辨別他的類別
  • >>> class sample:
     pass
    
    >>> x = sample()
    >>> x.__class__
    < class __main__.sample at 0x000000000279EEE8>
    
  • 每個類別及實體都有 __dict__ 來表示其擁有的屬性,但不包含繼承來的
  • 可以使用 dir 函式來找出所有的屬性,包括自動繼承的部份
  • >>> class C(A,B): pass
    >>> C.__dict__
    {'__module__': '__main__', '__doc__': None}
    >>> dir(C)
    ['__doc__', '__module__']
    >>> x = C()
    >>> x.__dict__
    {}
    >>> dir(x)
    ['__doc__', '__module__']
    
無綁束類別方法物件:必須要明確提供實體物件作為第一個引數 綁束實體方法物件: self + 函式配對,不必再另外傳入 self 兩種除了在使用方法上不一樣外,其它內建的屬性幾乎都一樣,只有 im_self 不同

>>> class Spam():
 def doit(): pass
>>> Spam.doit
< unbound method Spam.doit>
>>> x = Spam()
>>> x.doit
< bound method Spam.doit of <__main__.Spam instance at 0x00000000027A3CC8>>
>>> x.doit.__dict__
{}
>>> Spam.doit.__dict__
{}
>>> dir(x.doit)
['__call__', '__class__', '__cmp__', '__delattr__', '__doc__', '__format__', '__func__', '__get__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'im_class', 'im_func', 'im_self']
>>> dir(Spam.doit)
['__call__', '__class__', '__cmp__', '__delattr__', '__doc__', '__format__', '__func__', '__get__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'im_class', 'im_func', 'im_self']

>>> x.doit.im_self
<__main__.Spam instance at 0x00000000027A3188>
>>> Spam.doit.im_self

__doc__ 可以取得註解,註解必須寫下類別或是函式宣告的下一行



CH.26 高等類別議題
類別內以 "__" 開頭的屬性會被重新命名成 _[Classname]__[原始名稱],不論是一般變數或是函式,但在類別內的宣告時仍然可以用原始名稱來存取,但我覺得 Python 的作者發明這樣的東西其實也不是真的私有化,如果真的要解決書中的問題,可以發明關鍵字 "private" ,不是更乾脆?! 再來是靜態方法及類別方法之間的轉換可以靠 staticmethod 及 classmethod 我覺得也滿醜的,為什麼不用 static 這樣的關鍵字來處理? 更何況打從一開始,我就覺得 Python 是給懶人用的,根本不需要私有化及靜態方法,硬加上這些功能有點四不像。
__slots__ 可能會造成實體沒有 __dict__,以下是一個例子,不但影響了實體,其實連類別的 __dict__ 也產生了變化
>>> class limiter(object):
 __slots__  = ['age', 'name']
>>> x = limiter()
>>> limiter.__dict__
< dictproxy object at 0x00000000027A9108>
>>> x.__dict__

Traceback (most recent call last):
  File "< pyshell#169>", line 1, in < module>
    x.__dict__
AttributeError: 'limiter' object has no attribute '__dict__'
>>> dir(limiter)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'age', 'name']
>>> dir(x)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'age', 'name']
靜態方法、類別方法、一般方法的定義都不太一樣,尤其是"類別方法" ,被宣告成靜態方法後,不管是透過類別或是透過實體去呼叫,都只是一般的函式而己,不需要管是否有綁定,再來是實體內的函式都是有被綁定,透過類別的函式,如果有宣告 classmethod ,那就變成綁定的函式,但不管呼叫的是類別或是實體,都是綁定 class。
class Multi:
    def imeth(self, x):
        print self, x
    def smeth(x):
        print x
    def cmeth(cls, x):
        print cls, x
    smeth = staticmethod(smeth)
    cmeth = classmethod(cmeth)

>>> Multi.imeth
< unbound method Multi.imeth>                         # 非綁定
>>> Multi.smeth
< function smeth at 0x0000000002773E48>     # 一般函式
>>> Multi.cmeth
< bound method classobj.cmeth of < class __main__.Multi at 0x000000000276E6A8>>   # 綁定 class

>>> y = Multi()
>>> y.imeth
< bound method Multi.imeth of <__main__.Multi instance at 0x0000000002774D88>>   # 綁定 instance
>>> y.smeth
< function smeth at 0x0000000002773E48>   
>>> y.cmeth
< bound method classobj.cmeth of < class __main__.Multi at 0x000000000276E6A8>>   # 綁定 class

>>> Multi.imeth.im_self
>>> Multi.smeth.im_self

Traceback (most recent call last):
  File "< pyshell#297>", line 1, in < module>
    Multi.smeth.im_self
AttributeError: 'function' object has no attribute 'im_self'
>>> Multi.cmeth.im_self
< class __main__.Multi at 0x000000000270E6A8>


>>> y.imeth.im_self
<__main__.Multi instance at 0x0000000002713D08>
>>> y.smeth.im_self

Traceback (most recent call last):
  File "< pyshell#293>", line 1, in < module>
    y.smeth.im_self
AttributeError: 'function' object has no attribute 'im_self'
>>> y.cmeth.im_self
< class __main__.Multi at 0x000000000270E6A8>


2011年10月26日 星期三

Python 學習手冊第三版筆記 (五)


CH.18 模組: 大藍圖

網路上 Python Standard library 真是多的誇張呀~ 看起來還是要花一點時間瀏覽一下基本的東西,然後剩下的只能邊用邊查了! 這讓我覺得 Python 真是好物,library 很齊全而且都有統一整理起來。


CH.19 模組撰碼基礎

Programming Python
Free online Python Programming Tutorial

使用模組幾個注意的點

  • 匯入只會發生一次, 這也順便解決部份 recursive import 的問題, 而 reload 可強制重新 import
  • 沒有必要就少用 from,會模糊掉模組空間
  • 模組程式碼絕無法看見其他模組內的名稱,除非刻意予以匯入,P.402 的例子是在說明這點,如果我把 modb 加一行,會發現 modb 內的 X 值改變了,原因是我們強迫在 modb 內複製一份 moda 的 X,但同時要強調的是這只是複製品,所以 moda.X 最終結果會是 99 ,而本地的 X 絕對不會受到影響

    X = 11
    import moda
    from moda import X   # 強迫把 X import 進來且複製一份
    moda.f()
    print X, moda.X
    

    >>> 
    loading moda...
    loading moda finish 
    88 99
    

CH.20 模組套件

套件的匯入是利用指定目錄路徑,這一點很像 Java 的設計,大家可以同時寫自己的 utility.py 而不會彼此衝突到

CH.21 高等模組議題

__name__ 預設是 '__main__',但如果檔案是被匯入的,則會被改成其模組名稱,用途之一是把自我測試呼叫包放在檔案內,被人匯入時又不會被執行,另一個可能會用到的是 __file__

if __name__ == '__main__':
    test()    # test code


import as 延伸功能,這個有點像是 alias 功能

比較這兩種寫法,前者在使用 function 時必須把全部的名字寫出來,後者則不需要
import sound.effects.echo
sound.effects.echo.echofilter(...)

from sound.effects import echo
echo.echofilter(...)



2011年10月25日 星期二

Python 學習手冊第三版筆記 (四)

CH.15 函式基礎

要注意的概念有幾點
  • def 是可執行的程式碼,它並不是宣告而已,也不是 Macro,它其實做的事情是把一段程式碼打包成一個函式物件,然後傳給函式變數,之後我們可以透過這個函式變數來執行這段程式碼,所以他甚至是可以動態決定這段程式碼要長成什麼樣子
if test:
    def func():
        ...
else:
    def func():
       ....
...
func()

CH.16 範圍和引數

區域變數和廣域變數的簵圍的概念在 C 當中並不陌生, 不過在操作上有一些要注意的部份,因為python不需要宣告 variable 就可以開始使用,所以一但你在函式中把一個值指定給一個變數後,他就會生出一個 local variable,以下面這個例子為例,對 C programmer 來說,應該會印出 100,但在 python 來說,它其實已經建立了 local variable,所以 function 中存取的其實是 local variable,而不是 global variable,乍看之下會覺得有問題,但理解 python 是怎麼運作之後就不會覺得奇怪了

>>> def local():
 var = 100
>>> var = 0
>>> local()
>>> print var
0

如果一定要改變 global variable,就必須加上 global 這個關鍵字
>>> def local():
 global var
 var = 100
>>> var = 0
>>> local()
>>> print var
100

如果只是單獨要讀取 global variable,不一定要加上 global 關鍵字,只要你沒有宣告過同樣名字的 local variable ,它就會自動去上一層的區域找尋這個變數,這個還算顯然,和 C 沒什麼不同

>>> def local():
 print var
>>> var = 100
>>> local()
100

不過我又改寫了 local 這個函式,一開始不太明白為什麼會有 error 訊息,到後來才明白一件事,我們不能把 python 的函式看成逐行執行的 script,而必須把它看成一個整體的東西,一但函式打包起來,它就會知道裡面有一個叫 var 的 local variable,所以其實第一行要印 var 時就會出問題,它會認為 var 還沒給過值是不能印的


>>> def local():
 print var                     #  我覺得應該要印出 global variable
 var = 100                    #  生成  local variable
 print var                     #  我覺得應該要印出  local variable
>>> var = 100
>>> local()

Traceback (most recent call last):
  File "", line 1, in 
    local()
  File "", line 2, in local
    print var
UnboundLocalError: local variable 'var' referenced before assignment
>>> 

工廠函式以前在 C 是個禁忌,因為一般都是病毒型的程式碼才會動態生成函式,聽說會自我變種的病毒也都是利用了類似的技巧,想不到在 Python 中可以輕易的實作出來。

書中的這個例子,說明了如果要大量製造函式時,引數必須要以預設值傳進新函式中,否則製造出來的函式都會是長的一樣的,對於這個例子我看了很久,最後我對於他的解讀是新函式中的 i 因為找不到對應的 local variable ,所以會被強迫指向 makeActions 中的 i,也就是說所有新生成的函式都會指向同一個 i ,才會造成執行結果都一樣。書中用 "實質上都是記住相同之值" 來描述這件事其實讓我一開始無法理解,如果用 "指標指向同一個變數" 來講,可能就會更清楚。

>>> def makeActions():
    acts = []
    for i in range(5):
        acts.append(lambda x: i ** x)
    return acts

>>> acts = makeActions()
>>> acts[0](2)
16
>>> acts[1](2)
16

記得在 C 當中在處理任意引數是很麻煩的,我曾經想要做一件事,就是把 funcA 的任意引數再傳進 funcB,結果我花了很多時間研究有沒有辦法做到,發現似乎沒有解,到現在我仍然不知道要怎麼做,但在 Python 當中這件事真的是簡單到不行,不得不佩服 Python 設計者的巧思

int sum(int count, ...)
{
   va_list ap;
   int j;
   int tot = 0;
   va_start(ap, count);
   tot = va_arg(ap, int);                         /* 把第一個取出來 */
   va_end(ap);

   return tot + sum(count - 1, ...?? )      /* 想要用遞迴的方式算出剩下的加總,但這裡不知道要填什麼  */
}

def sum(*args):
    len_of_args = len(args)
    if len_of_args == 1:
        return args[0]
    if len_of_args == 0:
        return 0
    return sum(*args[0:len_of_args/2]) + sum(*args[len_of_args/2:])   /* 輕易的把變動參數傳給下一個函式 */

CH.17 高等函式議題

在第十六章看到 lambda 時還無法理解為什麼要有這個東西,覺得它的功用和 function 沒什麼兩樣,但看到第十七章的說明後,這讓我想到 Java 裡面有 Anonymous inner classes,感覺有異曲同工之妙,同樣都是為了省下宣告新的 class ,把 class 及使用 function/class 的人緊緊的綁在一起。

button1.addActionListener(
         new java.awt.event.ActionListener()
         {
            public void actionPerformed(java.awt.event.ActionEvent e)
            {
               // do something
            }
         }
      );

apply, filter, reduce 跳過不看

list comprehension 的語法要記熟!

[expression for target1 in sequence1 [if condition1]
for target2 in sequence2 [if condition2]
for target3 in sequence3 [if condition3] ... ]

然後記下一個二維矩陣的用法,和一般用法不同

[[M[row][col] * N[row][col] for col in range(3)] for row in range(3)]

2012.03.24 補:
因為實在太重要了, 但又常忘記要怎麼用, 留幾個 sample link
http://docs.python.org/reference/expressions.html?highlight=list%20comprehension#list-displays
http://www.secnetix.de/olli/Python/list_comprehensions.hawk
http://docs.python.org/tutorial/datastructures.html#list-comprehensions
http://www.bogotobogo.com/python/python_list_comprehension.html



可變更預設引數在函式呼叫之間會保留其狀態,這一點我真的覺得太奇怪了,真的很容易造成陷阱,一般來說程式設計者預設應該不是想要這種功能才對呀~ 早就己經習慣固定的參數輸入值會得到固定的結果的我,實在不太能接受這點,這真的要好好記一下

>>> def saver (x=[]):
 x.append(1)
 print x
>>> saver()
[1]
>>> saver()
[1, 1]

2011年10月24日 星期一

Python 學習手冊第三版筆記 (三)


Ch.10 Python 敘述簡介

None

Ch.11 指定敘述、運算式、以及列印

Python 會建立臨時之 tuple,以儲存右側變數原始之值,所以在交換兩變數之值,就很方便,不過我做了個小實驗,刻意的讓它交換兩次,原以為會有交換兩次的狀況,但發現沒有,也找不到合理的解釋來滿足第三個及第四個例子,所以目前還是不太清楚它是怎麼運作的,總之,少寫這樣會讓人看不懂的式子吧!

>>> A = 1
>>> B = 2
>>> A, B = B, A
>>> A, B
(2, 1)
>>> A, B, C = 1, 2, 3
>>> A, B, C
(1, 2, 3)
>>> A, B, C = B, C, A = C, A, B
>>> A, B, C
(2, 3, 1)
>>> A1, B1, C1 = 1, 2, 3
>>> A2, B2, C2 = 4, 5, 6
>>> A1, B1, C1 = B1, C1, A1 = A2, B2, C2
>>> A1, B1, C1
(6, 4, 5)
>>> A1, B1, C1 = 1, 2, 3
>>> A2, B2, C2 = 4, 5, 6
>>> A3, B3, C3 = 7, 8, 9
>>> A1, B1, C1 = A2, B2, C2 = A3, B3, C3
>>> A1, B1, C1
(7, 8, 9)

  • 單一底線的名稱 (_X) 不會被 from module import * 匯入,可以用來分辨這是公用函式或是私用函式

  • 前後有雙底線的名稱 (__X__) 是系統定義名詞,也不要隨意使用

  • 以兩底線開頭、但結尾沒有雙底線 (__X) 是類別之區域變數

  • 可利用 sys.stdout = open(xxxx) 來重導向輸出串流

  • 可以用 print >> file, [...] 來印東西到檔案中

  • 如果物件有定義 write 函式,也可以用 print >> object, [...] 來輸入東西


  • Ch.12 if 測試

  • X or Y or ... : Python 會由左至右求算運算元物件,然後傳回第一個為真者,不然就就傳回最後一個

  • X and Y and ... : 傳回第一個為假者,不然就傳回最後一個

  • if / else 三元運算式,有幾種表示法


  • if X:
        A = Y
    else:
        A = Z
    
    A = Y if X else Z
    
    A = ((X and Y) or Z)
    
    A = [Z, Y][bool(X)]
    
    

    如果用布林值 (or 、and) 來做運算,要注意的是可能不會執行所有的運算式,這和 C 也是一樣的

    Ch.13 while 與 for 迴圈


    和 C 不太一樣的部份有兩個

    1. 多了  pass 語法,代表的是什麼事也不做, 不過就和書上寫的一樣,真的有用的程式碼似乎都不太會用到這個語法, 比較像是我們在開發過程中, 有時候會寫上註解 /* TODO:  */ ,留下空白的內容,計劃之後再補上,這個時候可以先用 pass 來代替這個部份,所以目前我想像中的可能性是我寫完程式碼後可以回頭檢查有沒有 "pass" 留在程式碼中,如果有的話就表示我有東西忘了補。
    2. .
    3. 可以和 else 做搭配。以前寫 C 時, 會遇到一些 case, 在 for 迴圈之後,判斷 counter 是否有執行到最後,如果有才會觸發某些事,如果是中途就中斷的,就會跳過,我覺得這個有些相似,可以強化彼此的關係,讓可讀性更強。不過 while/for 和 else 搭配, 看起來真的很不習慣

    C 的寫法
    while (x > 1)
    {
           if ((y % x) == 0) 
           {
              printf(" y has factor %d\n", x);
              break;
           }
           x--           
    }
    
    if (x == 1)
         printf("y is prime\n");
    


    Python 的寫法
    while x > 1:
        if y % x == 0:
            print 'y has facter %d' % x
            break;
        x = x - 1
    else:
        print 'y is prime'
    
    

    過去用 C 寫遞迴的邏輯,是有一個 counter 時,而且可以清楚有上限時,就用 for loop,否則就用 while loop,但在 Python 當中它把 for loop 更抽像化一層,如果是在一個集合中想要輪流對每一個子元素操作時,就用 for loop,否則用 while loop ,用 for loop 的好處是有些物件會定義反覆器 (iterator? ),幫助更快速的存取子元素,而且可以在Python虛擬機器中做最佳化處理


    D = {'a':1, 'b':2, 'c':3}
    # 不使用反覆器
    for key in D.keys() :
        print key, D[key]
    
    # 使用 Dictionary 內建的反覆器
    for key in D
        print key, D[key]
    
    這兩種方式可以達到一樣的結果, 但效能卻不一樣, 後者會更好
    
    

    zip: 把兩個序列平行串接起來, 當引數長度不同時,zip 會以最短序列為主來截斷其它的序列,拿來建構辭典很方便

    >>> S1 = 'abc'
    >>> S2 = 'xyz123'
    >>> zip(S1, S2)
    [('a', 'x'), ('b', 'y'), ('c', 'z')]
    
    

    map: 和 zip 類似, 但會以最長序列為主來做 mapping

    >>> map(None, S1, S2)
    [('a', 'x'), ('b', 'y'), ('c', 'z'), (None, '1'), (None, '2'), (None, '3')]
    


    如果要施加一種運算至集合中的每個項目, 也可以利用 List comprehension
    >>> L = [1,2,3,4,5]
    
    # 利用 for loop
    >>> for i in range(5):
     L[i] += 10
    >>> L
    [11, 12, 13, 14, 15]
    
    # 利用 List comprehension 做一樣的事
    
    >>> L = [x+10 for x in L]
    >>> L
    [21, 22, 23, 24, 25]
    
    

    更複雜的 List comprehension

    >>> [x+y for x in 'abc' for y in 'lmn']
    ['al', 'am', 'an', 'bl', 'bm', 'bn', 'cl', 'cm', 'cn']
    

    Ch.14 說明文件插曲

    無特別紀錄

    Python 學習手冊第三版筆記 (二)

    CH.4 Python 物作型態簡介

    第一次看到這段程式碼時,就笑倒在地上,好可愛的語法呀!
    >>> x = 'spam'
    >>> x * 8
    'spamspamspamspamspamspamspamspam'
    

  • dir 函式,可以傳回指定物件可用之所有屬性串列,包括了繼承來的




  • help 函式,配合 PyDoc 從物件中抽取出說明文件




  • re 的模組內,可以做 regular expression 的搜尋及比對




  • sorted 函式,是新的內建函式,可以用來把字典轉成序列並且排序




  • list / dict 不能直接用 "+",必須要用 append,但 Tuple 可以用 "+" 來合併物件





  • CH.5 數字

  • 底板除法 // ,小數部份一定會被去掉





  • >>> 1/2
    0
    >>> 1//2
    0
    >>> 1.0/2
    0.5
    >>> 1.0 // 2
    0.0
    

  • python 中以 0 開頭的數字都會預設是 8 進位





  • CH.6 動態定型簡介

  • copy 是淺層複製,deepcopy 是深層複製




  • is 是比較實作參照值的指標,而 == 是比較值





  • CH.7 字串

  • S[:] 是取出從頭到尾的項目,等於進行複製一份



  • S[I:J:K],也就是取出 S 之中,從 I 到 J-1、間隔K的所有項目



  • S[::-1] ,從右走至左,效果就是逆轉序列



  • ord(S) 會印出 S 的 ASCII 代碼,S 是單一字元



  • 字串格式:$[(name)][flags][width][.precesion] code



  • 辭典式字串格式 ex: "%(key1)d %(key2)s" % {"key1":1, "key2":"spam"}






  • CH.8 串列和辭典

  • 索引和切片都是直接修改主體,而不是產生新串列,這一點和字串不同

  • L[1:2] = [] 其實是刪除運算



  • 辭典並非序列,它是沒有順序的,所以不能用 index 來用 for 敘述予以繞行,但可以用反覆器來拿到所有的成員





  • CH.9 Tuple、檔案、以及其他一切

  • 開啟 file 之後,有 readline 及 readlines 兩個 API,一個是回傳字串,一個是回傳字串陣列


  • eval() 這個內建函式可以把字串視為可執行程式碼


  • pickle 可以讓我們直接在檔案中儲存任何 Python 物件


  • F = open('datafile.txt', 'w')
    import pickle
    pickle.dump(D, F)
    F.close()
    
    F = open('datafile.txt')
    E = pickle.load(F)
    

  • 另一種工作 struct 模組知道如何將物件打包成二進位資料


  • >>> F = open('data.bin', 'wb')
    >>> import struct
    >>> bytes = struct.pack('>i4sh', 7, 'spam', 8)
    >>> bytes
    '\x00\x00\x00\x07spam\x00\x08'
    >>> F.write(bytes)
    >>> F.close()
    >>> 
    >>> F = open('data.bin', 'rb')
    >>> data = F.read()
    >>> data
    '\x00\x00\x00\x07spam\x00\x08'
    >>> values = struct.unpack('>i4sh', data)
    >>> values
    

  • == 運算子測試值的相等性。但 is 是測試物件本體,測試兩者是否是相同物件,或是 A 是 B 的其中一種


  • >>> L1 = [1, ('a', 3)]
    >>> L2 = [1, ('a', 3)]
    >>> L1 == L2, L1 is L2
    (True, False)
    

  • 循環式資料結構會印出 [...] 來代表


  • >>> L = ['grail']
    >>> L.append(L)
    >>> L
    ['grail', [...]]
    


    Python 學習手冊第三版筆記 (一)

    CH.1 簡介

    這篇學習筆記是回頭再看一次才寫下來的,第一章的簡介我覺得作者這段描述的很好: Python 是一種混合體,其置於傳統描述語言 (Tcl、Perl) 以及系統開發語言 ( C、C++、Java) 之間。

    CH.2 Python 如何執行程式

    無

    CH.3 如何執行程式

    其它 Python 程式來啟動 (例如,exefile、os.popen、os.system) ,必須參考 Programming Python (O'Reilly)

    2011年10月18日 星期二

    How to see Python code in Source Insight



    資訊是從 http://wiki.python.org/moin/PythonEditors 找到的

    1. 下載 Source Insight 的一個CLF文件
    http://www.sourceinsight.com/public/languages/Python.CLF

    2. 打開 Source Insight, 選擇options->Preferences->Language->Import->將剛剛的clf文件導入進來



    3. options->Document options-> Add Type -> 輸入 Python

    4. 接下來就會多一個 Python 的 Document Type, 選擇它之後, 要把其它的東西都設定好
        a. Language => Python Language
        b. File filter => *.py
        c. turn on "Include when adding to projects"
        d. turn on some "Editing Options" if need


    這樣就大功告成啦!