Sharing

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]

沒有留言: