2012年5月29日 星期二

[Python]如何在Python排序(Python Sorting)

Python提供兩種內建排序的function分別是sort()和sorted()
這兩個function都可以用來排序一個list
差別在於sorted()會回傳一個排序好新的list
sort()會直接修改原始的list並排序完成
以下是一個範例:
>>> a = [2, 4, 3, 5, 1]
>>> sorted(a)
[1, 2, 3, 4, 5]
>>> a
[2, 4, 3, 5, 1]
>>> a.sort()
>>> print a
[1, 2, 3, 4, 5]
從以上範例可以知道,如果你確定原始的list不需要保留下來的話,可以使用sort()來排序
如果要保留原本的list,就用sorted()來產生一個排序好的新list

這兩個function還有另一個不一樣的地方
sort()只能用在list上排序,而sorted()可用來排序任何的iterable(string, dictionary, tuple...)
下列範例用sorted()來排序一個dictionary
>>> sorted({1: 'D', 3: 'B', 2: 'B', 6: 'E', 5: 'A'})
[1, 2, 3, 5, 6]

sorted()提供了一些參數可以讓我們決定要怎麼排序
當我們把reverse設成True的時候,會把排序的結果由預設的ascending改成descending
strs = ['aa', 'BB', 'zz', 'CC']
print sorted(strs)  # ['BB', 'CC', 'aa', 'zz'] 
print sorted(strs, reverse=True)   # ['zz', 'aa', 'CC', 'BB']
可以設定一個function給key這個參數,在排序之前會自動對每個element call一次key所指定的function
透過這個方法可以做一些比較複雜一點的排序
sorted("aa BB cc ZZ".split())                       #['BB', 'ZZ', 'aa', 'cc']   (case sensitive)
sorted("aa BB cc ZZ".split(), key=str.lower)        #['aa', 'BB', 'cc', 'ZZ']   (case-insensitive)
如果要根據某些欄位來做sort,也可以用key這個參數
要注意的是,key只接受function當作參數。下列範例使用lambda來傳入一個匿名函式
students = [
        ('john', 'A', 15),
        ('jane', 'B', 12),
        ('dave', 'B', 10),
]
sorted(students, key = lambda x : x[2])   # sort by age
#[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
為了方便使用者使用,Python提供operator module,不用自己撰寫key的function
以下是使用operator module中的itemgetter()的範例
from operator import itemgetter, attrgetter
sorted(student_tuples, key=itemgetter(2))
#[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
如果sort的物件是class object的話,也可以使用operator module中的attrgetter()來做sort
class Student:
        def __init__(self, name, grade, age):
                self.name = name
                self.grade = grade
                self.age = age
        def __repr__(self):
                return repr((self.name, self.grade, self.age))

student_objects = [
        Student('john', 'A', 15),
        Student('jane', 'B', 12),
        Student('dave', 'B', 10),
]

sorted(student_objects, key=attrgetter('age'))
#[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
如果不使用operator module的話,也可以自己用lambda來寫一個key function
sorted(student_objects, key=lambda student: student.age)
#[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
如果要先比成績再比年齡的話,用operator module很簡單就可以做到
sorted(student_objects, key=attrgetter('grade', 'age'))
#[('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)]
在Python 2.x時候sorted還有提供cmp這個參數
不過在Python 3.0之後被拿掉了,使用Python 3.0的時候要注意不要用到舊的語法

2012年5月28日 星期一

[Python]for loop, list comprehension, map效能比較

在Python中要走訪一個list有很多種方法,常見的有for loop, list comprehension, map
但是究竟哪一種方法效能比較好呢?以下是一個範例程式可以做個比較:
import time, sys
reps = 10000
repsList = range(reps)

def tester(func, *args):
    startTime = time.time()
    for i in range(reps):
        func(*args)
        elapsed = time.time() - startTime
        return elapsed

def forLoop():
    res = []
    for x in repsList:
        res.append(abs(x))

def listComprehension():
    res = [abs(x) for x in repsList]

def mapFunction():
    res = list(map(abs, repsList))

print (sys.version)
tests = (forStatement, listComprehension, mapFunction)
for testfunc in tests:
    print (testfunc.__name__.ljust(20), '=>', tester(testfunc))
這段範例程式會用三種方法分別建立一個內含10000個element的list,並且對每個element執行內建函式abs()
以下是程式執行結果,執行環境為Python 3.2.2
3.2.2 (default, Dec 23 2011, 11:21:15)
[GCC 4.6.1]
forLoop              => 0.0016720294952392578
listComprehension    => 0.0008997917175292969
mapFunction          => 0.0007519721984863281
由以上執行結果可知map最快、list comprehension次之、for loop大概要多花前面兩種方法一倍的時間才能執行完

為什麽map和list comprehension會跑這麼快呢?
其實是因為這兩種方法都有經過Python直譯器的最佳化,在執行時很接近C程式碼跑得速度
而for loop沒有經過最佳化直接在Python直譯器中執行,自然就跑得比較慢

以上範例是針對Python內建函式(abs)所做的測試,如果改用user自己定義的函式結果會不會不一樣呢?
下列範例會把list中的element都加上1
import time, sys
reps = 10000
repsList = range(reps)

def tester(func, *args):
    startTime = time.time()
    for i in range(reps):
        func(*args)
        elapsed = time.time() - startTime
        return elapsed

def forLoop():
    res = []
    for x in repsList:
        res.append(x + 1)

def listComprehension():
    res = [x + 1 for x in repsList]

def mapFunction():
    res = list(map((lambda x : x + 1), repsList))

print (sys.version)
tests = (forLoop, listComprehension, mapFunction)
for testfunc in tests:
    print (testfunc.__name__.ljust(20), '=>', tester(testfunc))
3.2.2 (default, Dec 23 2011, 11:21:15)
[GCC 4.6.1]
forLoop              => 0.0016200542449951172
listComprehension    => 0.0009100437164306641
mapFunction          => 0.0018360614776611328
執行結果出乎意料,map竟然變得比fop loop還慢。而list comprehension還是一樣快了快一倍
由此結果可知,Python直譯器當map裡頭放的不是內建函式時,並不會做最佳化的動作
當使用user自己定義的函式時,用list comprehension才能得到最佳的效能

這篇文章結論如下:
1. 當用到內建函式時,使用map可以得到最佳的效能
2. 除了1的case以外,盡量使用list comprehension才可以得到最佳的效能
3. for loop不管在什麽情況下都是跑最慢的
4. 最重要的是除非有特殊效能的考量,否則以上這三種方法盡量優先考慮Python程式的可讀性

Reference: Learning Python, 4e

2012年5月17日 星期四

[Python]Python和C之間的差異 - Python的型別和物件

這篇文章主要是想幫助那些從C轉換跑道到Python來的程式設計師
首先先解釋一個差異很大的地方,Python的型別(types)和物件(objects)

在Python中所有的東西都是object

string是object,int是object,function是object,連code都是object
這點和C或是C++非常的不同
我們可以用一個內建的function type()來得知一個object的type
>>> s = "sway"           #"sway"是一個string的object
>>> type(s)        
<type 'str'>
>>> i = 2                #2是一個int的object
>>> type(i)
<type 'int'> 
>>> def hello(): pass    #hello()是一個function的object  
...
>>> type(hello)
<type 'function'>
>>> type(hello.__code__) #hello()的code是存在一個code的object 
<type 'code'>

Python是動態型別(Dynamic Typing)的程式語言,和C和C++這類的編譯語言不一樣
C和C++變數的型別都在編譯時候就決定好了
而Python則是在執行期(Run Time)才決定type
>>> s = "sway"    #這時候s的type是string
>>> type(s)

>>> s = 2         #這時候s的type變成int了
>>> type(s)








由上面範例可以帶出一個Python很重要的觀念
Python variable的type完全被它所對應到的object所決定的
這在Python中稱為reference
每一個variable其實就像是C裡頭的pointer
先建立object之後,才用variable指向新建立的object,相當於給object一個別名
因此Python的variable其實就是reference

當我們執行i = 2的時候,其實Python會先建立一個integer的object叫做2
之後再產生一個variable並且把它指向2這個integer的object
Python的variable其實就只是一個name而已,我們透過這個name去間接access到object
這點是Python和C、C++很不一樣的地方,這也是大多數C programmer剛轉過來不太適應的地方
由於Python的這種特性,很多神奇的事情就發生了
>>> a = [1, 2, 3]
>>> b = a
>>> b[0] = 4
>>> print a
[4, 2, 3]
很多人看到這邊可能會覺得很奇怪,明明是修改b的值怎麼連a的值也被改到了?













其實原因很簡單,因為b = a這行程式碼其實只是把b指到a所指向的object而已
因此當執行完這行程式碼後,a和b就都指向同一個object
不論是改a或是改b,最後都會對應到同一個object
這是Python初學者最容易詢問的問題之一,其實只要搞懂Python的object架構,一切就會變得很直覺

上面範例如果object是一個不可改變(immutable)的object,情況就有點不一樣了。不可改變(immutable)的object像是:string, integer......
因為object是不可改變的,當你嘗試去更改object的時候,會自動建立新的物件,而不會更動到所指向的object。
聽起來有點難懂,看以下範例就知道了
>>> a = 2
>>> b = a
>>> id(a)                #id(a)可以取得a指向物件的id
17500064
>>> id(b)
17500064                 #a 和 b指向同一個物件(integer 2)
>>> b = 4
>>> print a
2
>>> id(b)                #b指向一個新的物件(integer 4)
17500016
由以上範例可以知道,兩個reference指向同一個不可改變的object時。因為object不可改變的特性,透過其中一個reference試圖去修改object都會自動建立一個新的物件

相反地,兩個reference指向同一個可改變(mutable)的object時。因為我們可以直接修改object的關係,所以透過其中一個reference去修改object不會建立新的物件。會直接修改到指向的object,因此透過任何一個reference去取值看起來就好像兩邊都被改過一樣。

All "variables" are references. 這是Python和C很不一樣的地方。

2012年5月1日 星期二

大話處理器 : 了解DSP.CPU及MCU基礎架構


這本書是一本講解處理器內部運作原理的書,看得出來作者試圖用很多生活化的例子來解釋CPU內部運作的原理。大致上可以把這本書看成是一本簡單的計算機組織入門書,整本書的內容絕大多數都是從Computer Organization and Design以及Computer Architecture: A Quantitative Approach這兩本書而來。這本書的內容比較淺,作者跳過了很多細節。因此只適合完全不懂計算機組織的人來念,否則會覺得書中寫得內容太淺了。

書中前面幾章在講解電腦的歷史,以及介紹一些常見的CPU和DSP,這部份我覺得還蠻不錯的。這本書最大的缺點就在於翻譯品質,可能是因為兩岸用語不同的關係,許多地方寫得中文名詞都有點奇怪。這點在很多大陸翻譯書都會出現,國內出版社真的該好好改善這一部份。

總而言之,這本書可以帶你初淺的認識CPU的內部運作原理。不過詳細的細節還是建議去閱讀計算機組織的聖經 Computer Organization and Design以及Computer Architecture: A Quantitative Approach 這兩本書,這兩本書才能真正帶你瞭解CPU的世界是怎麼運作的。

2012年4月19日 星期四

[Python]自己定義例外處理

Python允許使用者自己定義一個新的例外處理
要建立一個新的例外處理很簡單:
class TestException(Exception): pass
上面程式碼定義一個新的例外處理叫TestException,並且繼承自一個Exception的Class
什麽時候要定義一個新的例外處理呢? 當要從好幾層迴圈離開的時候就很好用
以下是一個從許多表格中找出特定值的範例:
found = False
for record in table:
    for field in record:
        for item in field:
            if item == target:
                found = True
                break
        if found:
            break
    if found:
        break
if found:
    print "found"
else:
    print "not found"
傳統的寫法要從最內層迴圈一層一層break出來,程式碼變得很冗長
這時候可以定義一個新的例外處理,當找到target的時候就raise我們新定義的例外處理
如此一來程式碼可以變得比較簡潔
class FoundException(Exception): pass
try:
    for record in table:
        for field in record:
            for item in field:
                if item == target:
                    raise FoundException()
except FoundException:
    print "found"
else:
    print "not found"

2012年4月18日 星期三

[Python]目前執行的OS平台

在Python中要知道目前執行的OS平台很簡單
只需要去檢查sys.platform這個變數就可以了
sys.platform會保存目前執行OS平台的名字("win32" or "linux2")
因此我們可以用一行簡單的程式碼就知道目前執行的OS平台是什麽
isWindows = True if sys.platform.startswith("win") else False
這行程式碼在windows上執行時,osPlatform的值會被設成True
在其他OS執行的時候(ex: Linux, FreeBSD...),會被設定成False

2012年3月28日 星期三

嵌入式系統開機流程

1. CPU到指定的memory address抓取第一行instruction執行,有兩種常見的作法
    (1)CPU會把PC設定為一個特定的address,從這邊開始執行
    (2)CPU會把Interrupt Vector Table設定為特定的address,並且發出Reset interrupt
         在Reset的ISR中執行某個開機的function

2. 初始化CPU (IVT的addres, SP)

3. 把code和data從ROM或是 Flash搬到RAM上執行,通常使用DMA搬
    (有些code可直接在external flash上執行就不需要搬)

4. 初始化板子相關設定和參數

5. 初始化系統(ex 執行OS、GUI)

6. 執行主程式,很多嵌入式系統會執行一個無窮迴圈

2012年3月27日 星期二

在Windows底下dump DLL

問題:
今天碰到一個bug,有一個.net程式用到_controlfp_s這個function
在Win7底下是放在msvcrt.dll,但是在Win XP中的msvcrt.dll卻找不到這個function

找到此問題的方法: 
使用dumpbin把DLL的function name dump出來看
(有點像Linux底下的object dump)

步驟:
1.執行Visual Studio Command Prompt

2.C:\WINDOWS\system32>dumpbin /exports msvcrt.dll | more

解決方法:
更換XP和 Win7都有的function,或是在程式當中附上DLL檔

2012年3月14日 星期三

約耳續談軟體:探究軟體經營的根本實學

這本書是『約耳趣談軟體:來自專案管理的現場實錄』的續集,同樣是由充滿幽默感的約耳所撰寫。大部分文章其實之前在網路上都有人翻譯。約耳還是延續他一貫幽默的風格,很輕鬆就能讀完他。和前一本不同的地方在於,這本書主要的主題是在探討如何管理一間軟體公司,其實蠻適合主管和老闆來看看。這本書可以看到約耳對待員工以及如何管理他的公司,看了會很令人羨慕,面試時候不但有豪華轎車到機場接送,還免費住在高級的旅館並且在高級的旅館舉辦party。辦公室裡頭有私人的辦公室,坐得是一張900美金的椅子。就如同約耳文章所寫得,最好的管理就是找最優秀的人進來,讓他們認同你並放手讓這些專家自己去做。當然約耳的徵才標準也是非常高,他同時也花了幾章在抱怨目前大學課程(Java學校帶來的危害,耶魯大學的演講),相信能進去他的公司都是數一數二的高手。

其中有幾章作者特別替微軟的IE還有Office的格式做出解釋,還蠻值得一看得,有些東西真的是因為有歷史包袱在,結果就是現在使用者看到的這樣。



2012年2月22日 星期三

松本行弘的程式世界:成為一流程式設計師的14種思考術


這本書是Ruby的開發者Matz所寫得書。第一眼看到可能會覺得是Ruby的教學書,但其實這完全不是一本Ruby教學書。本書主要是以開發一個程式語言的角度來探討一些程式上的問題,作者並以Ruby的角度來講解如何解決這些問題並和其他程式語言做比較。比如說講到多重繼承,作者先探討多重繼承在C++和Java的設計上分別會有那些問題,最後再講解為什麽Ruby要使用Mix-in來實做多重繼承。

當然還有很多其他的內容,比如說探討Design Pattern,作者就用Ruby示範了一些Pattern的實做,有些Pattern改用Ruby這種動態語言來實做會變得非常直覺好懂。其他包含MVC, Unicode, Regular Expression, Floating Point......作者一開始都先簡單講解其他語言的設計方法,最後再說明為什麽Ruby要這樣設計,看完以後會覺得Ruby真的實做了很多新的東西,並且吸收了其他程式語言好得設計方式。

看這本書前其實不太需要先學會Ruby,作者在用Ruby寫範例Code的時候都會順便解釋Ruby的語法,像我本身並不會Ruby,但是大部分範例都可以看得懂。如果你是像我一樣對如何設計一個程式語言很有興趣的話,這本書是市面上少見在探討語言本身性質的書,非常值得一看!