2008年10月30日 星期四

開發軟體時的自我要求

RD與業務的戰爭


這應該是個永遠的議題




其實我也染了許多業務的惡習,就是凡事容易求快,習慣先求成果,卻少了嚴謹的規劃。雖然資歷上我還是個菜鳥Programmer,但短短時間內卻也因為這樣的陋習多了不少困擾

1. 新人進來時,什麼都要親自下海教授
當然不是說就不需要教育訓練了,只是如果能整理一個好的教學文件,這樣的困擾可以減低不少,也可以增加新人學習效率

2. 移交部分開發工作給同事
反過來想,若是我必須接手另一個同事開發到一半的工作,我會希望有完整的文件可以瀏覽,並且coding style良好

3. 隨著code體積龐大,測試時間就只能等比往上加
這我想我連一般的programmer的資格都不夠,若是照著TDD來走,就不會有現在這樣的窘境了
可以參考這個網站

4. 使用者使用不便
這可能還是需要時間來磨練,現階段的我大概只能靠提供完整一點的文件來解決這個問題


要解決以上的問題,可能需要好好制定一些自我要求的流程了




除了事前完整的SA,在coding階段還是要有以下動作

1. 功能基本流程圖
若SA階段切不夠細,這邊就必須再設計出來

2. 功能概念簡報
盡量可以將每個功能切割成5張簡報以內可以呈現

3. Test code

4. 命名方式整理
不管是變數還是函式,都需要統一其命名格式,當然如果該程式語言有一個標準更好

5. coding style整理
其實上述應該算在這條之中,但刻意獨立出來重視命名方式

6. 邏輯性註解以及TODO

7. 功能使用方式簡報

8. 版本Release的更動紀錄


目前先這樣,有想到再補


可能會有些矯枉過正,但先這樣實行看看吧!不做做看怎麼會知道呢!

2008年10月29日 星期三

Django的urls.py

在urls.py裡面,你會看到這樣的描述
(r'^admin/doc/', include('django.contrib.admindocs.urls'))


了解這樣的描述之前,要先了解urls.py的行為

其實講白了就是對照網址,看要給誰處理罷了。對照的方式為正規表示法,而且是由上到下,比對成功就丟給後面的function處理了,所以要小心設計,不然會有一些function苦無用武之地....

但為什麼上面的描述要加上include的字樣?

可以注意到前端的正規表示並未加上$(在正規表示法中,$代表結尾),因此它可以比對大範圍的admin/doc/....網址,比較有趣的是,include就是把....部份丟給另一個urls.py處理,因此每個app也可以擁有自己的urls.py,不一定要將所有的「轉址」放在專案根目錄的urls.py裡面

2008年10月26日 星期日

設置Django的Admin功能

其實我還不清楚這功能可以幹麼,哈!不過我看很多人都在想辦法設置它,所以我也先設置起來玩玩看!

1. 更改settings.py

共要改三個地方
DATABASE_ENGINE = 'sqlite3'
DATABASE_NAME = 'c:/django_www/data.db'


INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
)




2. 配置sqlite3與data.db

打開DOS Prompt,下達
manage.py syncdb


然後照著上面的提示輸入


3. 更改urls.py

直接把內容換成這樣
from django.conf.urls.defaults import *

# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
    
# Example:
    
# (r'^www/', include('www.foo.urls')),
    
# (r'^$', 'www.helloworld.index'),
    
# Uncomment the admin/doc line below and add 'django.contrib.admindocs' 
    
# to INSTALLED_APPS to enable admin documentation:
    
# (r'^admin/doc/', include('django.contrib.admindocs.urls')),

    
# Uncomment the next line to enable the admin:
    
(r'^admin/(.*)', admin.site.root),
)


然後就可以到http://localhost/admin/看看admin頁面了!

2008年10月24日 星期五

Python+Django+Apache

這篇文章講的非常清楚
http://thinkhole.org/wp/2006/04/03/django-on-windows-howto/


把四個需要安裝的整理出來

1. 安裝Python - version 2.5.2.2
http://www.activestate.com/store/download_file.aspx?binGUID=4656928e-d642-4aec-815e-b8d819b7cdbb

若無特殊需求,使用default一直下一步就可以了


2. 安裝Django - version 1.0
http://www.djangoproject.com/download/1.0/tarball/

下載完成之後,解壓縮後進入資料夾,打開cmd.exe,執行
python setup.py install

即可安裝完成


3. 安裝Apache,請到下載頁面選擇最新版本(xxx為版本)apache_xxx-win32-x86-no_ssl.msi
http://apache.ntu.edu.tw/httpd/binaries/win32/
(我下載的為apache_2.2.10-win32-x86-no_ssl.msi)

原本的路徑應為 - C:\Program File\....\Apache2.2
但為了修改方便建議安裝至 - C:\Apache


4. 安裝mod_python,請選擇與Python版本與Apache版本相符的最新版本,此為讓Apache支援python的django模組
http://apache.stu.edu.tw/httpd/modpython/win/3.3.1/
(我下載的為mod_python-3.3.1.win32-py2.5-Apache2.2.exe)

在安裝mod_python的時候,他會要你選擇一個資料夾,請指定你Apache的安裝目錄


全都安裝好之後,http://localhost/應該就可以看到apache的首頁了


但我們想要看的應該是django架構的網站,而不是apache內建的首頁,因此要開始做一些設定

1. 先用django建立一個專案
安裝django成功之後,可以到這裡C:\Python25\scripts看到一個叫django-admin.py的檔案(當然實際還是得看安裝位置),它其實是django為了方便使用者所製作出來的一隻可帶參數執行檔。因此當我們要用django在C:\目錄底下建立一個新的專案,就打開DOS Prompt,並輸入這兩行

cd c:\
c:\Python25\scripts\django-admin.py startproject django_www


就可以看到在c:\產生了一個django_www的目錄了!這時候我們可以用django內建的web server來看看畫面,打開DOS Prompt,輸入
c:\django_www\manage.py runserver


然後再到這裡http://localhost:8000/就可以看到django所產生的首頁了!看完之後可以把django內建的web server停止,因為接下來我們就要開始使用Apache當作web server了!(不過不停止也不會影響就是了)



2. 跟Apache說我要開始使用django了!
首先要先把mod_python載入apache的服務,因此需要打開C:\Apache\conf\httpd.conf來編輯,先隨便找一個地方加入這一行
LoadModule python_module modules/mod_python.so


然後也是隨便找一個地方貼上
MaxRequestsPerChild 1
<Location "/">
    SetHandler python-program
    PythonPath "['C:/'] + sys.path"
    PythonHandler django.core.handlers.modpython
    SetEnv DJANGO_SETTINGS_MODULE django_www.settings
    PythonAutoReload Off
    PythonDebug On
</Location>
Alias /site_media C:/django_www/media
Alias /media C:/Python25/Lib/site-packages/django/contrib/admin/media
<Location "/site_media">
    SetHandler None
</Location>
<Location "/media>
    Order Deny,Allow
    Allow from all
    SetHandler None
</Location>


然後重新啟動Apache就可以在http://localhost/看到django的首頁了!

2008年10月23日 星期四

Python的doctest

我想了想,還是要來一個最簡單的doctest。每次我去看一些高手的網站,他們介紹模組都是用一個不算小的範例(果然是高手),但可能我要的只是一個最最簡單的範例。畢竟身為Python初學者的我,還不知道什麼是內建變數或是函式,也不知道是否要固定格式,我知道的只有最最基本的語法而已,所以每次看到一大串,我都還得分析過濾哪些才是真正的「模組範例」...

這是範例
mul.py
# coding=utf-8

'''
寫一個簡單的乘法功能
'''


import doctest

def mul(x, y):
    
'''
    >>> mul(100, 200)
    20000
    
    如果不小心範例寫錯了...就會回傳錯誤
    >>> mul(1, 2)
    1
    '''

    
    
# 這裡才是真正mul的內容
    
return x * y

# 這行的意思是指當此檔案為主程序時,才執行doctest.testmod()
if __name__ == '__main__':
    
doctest.testmod()


這是執行結果

發牌程式、doctest、unittest

記得之前曾經用Tcl寫過發牌程式,那時候大概的邏輯是
1. 產生52張牌,並依序放入一個list中
2. 產生0-51的亂數,當作index
3. 用index從list中取出牌
4. 從list中移除這張牌

其實也是沒寫幾行就搞定了。

但後來看到python的random模組提供了一個函式shuffle,這個函式的作用就是將list內的東西打亂,於是我又想到了發牌程式,其實這樣比較符合現實生活的「洗牌」與「發牌」行為。

邏輯是這樣的
1. 產生52張牌,並依序放入一個list中
2. 打亂52張牌的順序
3. 從第一張開始pop(取出並移除)出來


是不是很像人類在發牌呢?


但會寫這個主要是想要玩玩看doctest與unittest。

這兩樣在測試上都有其好用的地方,doctest非常適合一些簡易的測試,它可以讓註解等於測試,這樣很多習慣不良的開發者半強迫式的有了兩種習慣
1. 每一個function都會寫註解及範例
2. 每一個function都會寫測試程式

但畢竟是註解型測試,要忠實執行TDD(Test-driven development),可能還是得依靠unittest。

底下是 發牌程式 + doctest 與 unittest 兩段code

這是發牌程式 + doctest
dealer.py
# coding=utf-8

'''
只是一個簡單的發牌程式
'''


import doctest
import random

class dealer():
    
def __init__(self):
        
'''
        產生一副撲克牌
        '''

        
self.reset()
    
    
def reset(self):
        
'''
        重新產生一副撲克牌
        >>> d = dealer()
        >>> nothing = d.deal(1)
        >>> d.show().__len__() == 51
        True
        >>> d.reset()
        >>> d.show().__len__() == 52
        True
        '''

        
self.pokers = []
        
        
for pattern in ['spade', 'hearts', 'diamond', 'club']:
            
for number in range(1, 14):
                
self.pokers.append(pattern + str(number))
        
self.pokers.sort()
    
    
def shuffle(self):
        
'''
        洗剩下的撲克牌
        >>> d = dealer()
        >>> d.shuffle()
        >>> first = d.show()
        >>> first.__len__() == 52
        True
        >>> d.reset()
        >>> second = d.show()
        >>> second.__len__() == 52
        True
        >>> first == second
        False
        '''

        
random.shuffle(self.pokers)
    
    
def deal(self, number=1):
        
'''
        指定數目從牌庫中發出剩下的撲克牌
        >>> d = dealer()
        >>> nothing = d.deal(1)
        >>> d.show().__len__() == 51
        True
        >>> nothing = d.deal(51)
        >>> d.deal(1)
        'None'
        '''

        
        
# 如果指定的數目大於剩下的牌數,就把所有剩下的牌發出去
        
if self.pokers.__len__() < number:
            
number = self.pokers.__len__()
        
        
result = []
        
for i in range(number):
            
poker = self.pokers.pop()
            
result.append(poker)
        
        
if result.__len__() != 0:
            
result.sort()
            
return result
        
else:
            
return 'None'
    
    
def show(self):
        
'''
        把牌庫中剩下的撲克牌印出來
        '''

        
return self.pokers

if __name__ == '__main__':
    
doctest.testmod()



這是unittest
test_dealer.py
# coding=utf-8

'''
test for dealer.py
'''


import unittest
import dealer

class DealerTestCase(unittest.TestCase):
    
def setUp(self):
        
self.d = dealer.dealer()
        
self.original = list(self.d.show())
    
    
def testshuffle(self):
        
self.d.shuffle()
        
        
pokers = self.d.show()
        
self.assertNotEqual(self.original, pokers)
        
        
pokers.sort()
        
self.assertEqual(self.original, pokers)
    
    
def testreset(self):
        
self.d.reset()
        
        
pokers = self.d.show()
        
self.assertEqual(self.original, pokers)
    
    
def testdeal(self):
        
for i in range(52):
            
poker = self.d.deal()
            
self.assert_(poker[0] in self.original)
            
self.assert_(poker[0] not in self.d.show())
        
poker = self.d.deal()
        
self.assertEqual(poker, 'None')
    
    
def tearDown(self):
        
pass

if __name__ == '__main__':
    
suite = unittest.makeSuite(DealerTestCase)
    
unittest.TextTestRunner(verbosity=2).run(suite)


檔名記得跟我取的一樣喔!

將這兩個檔案都放在同一個資料夾,便可執行test_dealer.py!

2008年10月22日 星期三

讓Python開發速度提升的IPython

IPython是為了Python所開發的的一種互動式介面(Interactive Shell)

大部分的直譯式語言都會有這樣的介面,但通常都只有簡易的命令式對答,其實在使用上並不是這麼方便,但Tcl就有一個非常成功的範例-「Tkcon」

可記錄歷史訊息,可利用tab自動完成字詞,可對GUI做一些簡單的調整與設計...等

而IPython就是一種功能強大的互動式介面,讓Python's Programmer可以更快速的開發,可以讓像我這樣的初學者更快的進入狀況

安裝方法如下:

照順序下載安裝以下兩樣套件

IPython的主程式
http://ipython.scipy.org/dist/ipython-0.9.1.win32-setup.exe
Ipython的相依套件 - pyreadline
http://ipython.scipy.org/dist/pyreadline-1.5-win32-setup.exe

安裝完成之後,便可以開始試試它的威力了!

2008年10月18日 星期六

用最笨的方法幫自己的部落格code block排版

其實也就是將空白轉成網頁用的特殊空白編碼罷了..

不知道怎麼使用pre來讓我的網頁排版,看了好多網站,似乎沒有我可以用的..!

這是一段Tcl的code,其實也只是寫給自己而已。
set file [open [lindex [glob *.html] 0] r]
set body [read $file]
close $file

regexp {<div[^>]+>(.*?)</div>} $body dummy body

set result ""
foreach char [split $body ""] {
    
if {$char == "<"} {set stop 1}
    
if {$char == ">"} {set stop 0}
    
    
if {!$stop && $char == " "} {set char "&nbsp;"}
    
    
append result $char
}

set result "<div class=\"code\">${result}</div>"
set file [open result.txt w]
puts $file $result
close $file

exit



我想之後還是會面臨很多先進所提出的「code 過長」造成顯示爆表的問題吧,不過那時候再說囉!

Notepad++與Blogger的火花

我相信大部分使用Notepad++編輯程式碼的人,除了小巧好用的原因,也會很喜歡它的明亮色系。

這樣的明亮色系卻只能放在Notepad++編輯器內實在有點可惜,如果能讓網誌也可以輕鬆擁有這種樣式就好了!

當你有這種想法,你可以考慮到Notepad++官方網站Download這個外掛模組NppExport

安裝完成後,重新啟動Notepad++,就會看到外掛模組內多了一個NppExport。




照著圖上的指示,就可以將你的code變成html/css的檔案。

檔案產生出來之後,再使用Notepad++打開,將內的內容copy到你網誌的css樣式檔案中(或是宣告css的block)



再往下看,把紅色框框這段貼到你編輯部落格時,想要放code的地方就可以了!



但這樣只完成了80%,由於NppExport要完全正常的呈現在每一個部落格還是有難處的,因此還是要試著調整一下,才可以讓你的code看起來水水的。


完成範例(這是有修改過的)
http://slavetotech.blogspot.com/2008/10/pythonhttp.html

2008年10月17日 星期五

Python與Debug

只要有IDE(Integrated Development Environment)的程式語言,通常都可以仰賴Debug Mode來完成Debug的大業,只是IDE往往都是架構在GUI上,因此沒有顯示器的系統可能就無法享受這樣的便利性了。

而且有些程式語言要安裝IDE,實在是非常非常的麻煩,還要準備足夠的硬體資源來應付這樣的怪物。

至於Python Debug Mode完全是用Python Code來撰寫,還真的完全發揮了跨平台的實力,可以輕鬆移植到任何可安裝Python Interpreter的作業系統,而且不需要GUI就可以使用。

底下是一個簡單的範例

# coding=utf-8
def add(x):
    
value = '1'
    
return x + value

add(1)


執行這個檔案會出現以下的訊息

可以發現其實還是不知道錯誤到底在哪。

這時候若是直接打開檔案,當程式不多的時候,可以輕鬆的用肉眼判斷加法運算是不可以把數字與字串加在一起的,但程式很大的時候呢?

這時候就需要用到Debug Mode了


進入到command line模式(Windows=cmd.exe)
用Debug Mode執行檔案
python -m pdb add.py





然後下達這些指令(圖片底下為逐行解釋)




在(Pdb)模式下

設置breakpoint 3,可以比照第一次執行檔案時,發生錯誤的行數
b 3



開始執行程式直到breakpoint
r



開始檢查此行究竟發生何事?
x + value



印出x
x



印出value
value

原來是value為字串


試著將value更改為int型態
value = int(value)



繼續執行程式
r

成功return 2!

2008年10月13日 星期一

利用python的http套件將網路上的圖片抓下來

一個簡單的範例
# coding=utf-8

# 載入urllib2模組,可以處理http
import urllib2

# urllib2.urlopen會回傳一個http object
token = urllib2.urlopen('http://l.yimg.com/tw.yimg.com/i/tw/hp/spirit/yahoo_logo.gif')

# 開啟一個二進制檔案
f = open('yahoo_logo.gif', 'wb')

# 把圖片的內容放進這個檔案中
f.write(token.read())

# 關閉檔案
f.close()



這樣就會產生一個叫yahoo_logo.gif的檔案在執行路徑下了

利用Tcl的http套件將網路上的圖片抓下來

一個簡單的範例

# 載入http套件
package require http

# http::geturl會回傳一個token,這個token其實是一個流水號的陣列
set token [http::geturl http://l.yimg.com/tw.yimg.com/i/tw/hp/spirit/yahoo_logo.gif]

# 開啟一個檔案
set f [open yahoo_logo.gif w]

# 設定檔案用二進制的方式處理
fconfigure $f -translation binary

# 將剛剛圖片的內容放入檔案中
puts -nonewline $f [http::data $token]

# 關閉檔案
close $f


這樣就會產生一個叫yahoo_logo.gif的檔案在執行路徑下了