關于函數的事情,總是說不完的,下面就羅列一些編寫函數的注意事項。特別聲明,這些事項不是我總結的,我是從一本名字為《Learning Python》的書里面抄過來的,順便寫成了漢語,當然,是按照自己的視角翻譯的,里面也夾雜了一些自己的觀點。看官也可以理解為源于《Learning Python》但又有點兒不同。
* 函數具有獨立性。也就是常說的不要有太強的耦合性。要讓函數能夠獨立于外部的東西。參數和return語句就是實現這種獨立性的最好方法。
* 盡量不要使用全局變量,這也是讓函數具有低耦合度的方法。全局變量雖然進行了函數內外通信,但是它強化了函數對外部的依賴,常常讓函數的修改和程序調試比較麻煩。
* 如果參數的對象是可變類型的數據,在函數中,不要做對它的修改操作。當然,更多時候,參數傳入的最好是不可變的。
* 函數實現的功能和目標要單一化。每個函數的開頭,都要有簡短的一句話來說明本函數的功能和目標。
* 函數不要太大,能小則小,根據前一條的原則,功能目標單一,則代碼條數就小了。如果感覺有點大,看看能不能拆解開,分別為幾個函數。
* 不要修改另外一個模塊文件中的變量。這跟前面的道理是一樣的,目的是降低耦合性。
## 小試一下遞歸
對于在python中使用遞歸,我一項持謹慎態度,能不用就不用,為什么呢?一方面深恐自己學藝不精,另外,遞歸不僅消耗資源,而且很多時候速度也不如for循環快。
不過,做為程序員,遞歸還是需要了解的。這里就列舉一個簡單的例子。
~~~
>>> def newsum(lst):
... if not lst:
... return 0
... else:
... return lst[0] + newsum(lst[1:])
...
>>> newsum([1,2,3])
6
~~~
這是一個對list進行求和的函數(看官可能想到了,不是在python中有一個sum內置函數來求和么?為什么要自己寫呢?是的,在實際的編程中,沒有必要自己寫,用sum就可以了。這里用這個例子,純粹是為了說明遞歸,沒有編程實踐的意義),當然,我沒有判斷傳給函數的參數是否為完全由數字組成的list,所以,如果輸入的list中字母,就會編程這樣了:
~~~
>>> newsum([1,2,3,'q'])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in newsum
File "<stdin>", line 5, in newsum
File "<stdin>", line 5, in newsum
File "<stdin>", line 5, in newsum
TypeError: cannot concatenate 'str' and 'int' objects
~~~
這就是本函數的缺憾了。但是,為了說明遞歸,我們就顧不了這么多了。暫且忽略這個缺憾。看官注意上面的函數中,有一句:return lst(0)+newsum(lst[1:]),在這句話中,又調用了一邊函數本身。對了,這就遞歸,在函數中調用本函數自己。當然,區別在于傳入的參數有變化了。為了清除函數的調用流程,我們可以將每次傳入的參數打印出來:
~~~
>>> def newsum(lst):
... print lst
... if not lst:
... return 0
... else:
... return lst[0] + newsum(lst[1:])
...
>>> newsum([1,2,3])
[1, 2, 3]
[2, 3]
[3]
[]
6
~~~
這就是遞歸了。
其實,看官或許已經想到了,即使不用sum,也可以用for來事項上述操作。
~~~
>>> lst = [1,2,3]
>>> sum_result = 0
>>> for x in lst: sum_result += x
...
>>> sum_result
6
~~~
## 銘記:函數是對象
還記得,在第一部分學習的時候,不斷強調的:**變量無類型,數據有類型**,那時候遇到的數據包括字符串、數值、列表、元組、字典、文件,這些東西,都被視為對象。函數跟它們類似,也是對象。因此就可以像以前的對象一樣進行賦值、傳遞給其它函數、嵌入到數據結構、從一個函數返回給另一個函數等等面向對象的操作。當然,函數這個對象也有特殊性,就是它可以由一個函數表達式后面的括號中的列表參數調用。
~~~
>>> def newsum(lst): #依然以這個遞歸的函數為例
... print lst
... if not lst:
... return 0
... else:
... return lst[0] + newsum(lst[1:])
...
>>> lst = [1,2,3]
>>> newsum(lst) #這是前面已經常用的方法
[1, 2, 3]
[2, 3]
[3]
[]
6
>>> recusion_fun = newsum #通過賦值語句,讓變量recusion_fun也引用了函數newsum(lst)對象
>>> recusion_fun(lst) #從而變量能夠實現等同函數調用的操作
[1, 2, 3]
[2, 3]
[3]
[]
6
~~~
再看一個例子,在這個例子中,一定要謹記函數是對象。看官曾記否?在list中,可以容納任何對象,那么,是否能夠容納一個函數中呢?
~~~
>>> fun_list = [(newsum,[1,2,3]),(newsum,[1,2,3,4,5])]
>>> for fun,arg in fun_list:
... fun(arg)
...
[1, 2, 3]
[2, 3]
[3]
[]
6
[1, 2, 3, 4, 5]
[2, 3, 4, 5]
[3, 4, 5]
[4, 5]
[5]
[]
15
~~~
函數,真的就是對象啊。
既然是對象,就可以用dir(object)方式查看有關信息嘍:
~~~
>>> dir(newsum)
['__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__doc__', '__format__', '__get__', '__getattribute__', '__globals__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name']
>>> dir(newsum.__code__)
['__class__', '__cmp__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename', 'co_firstlineno', 'co_flags', 'co_freevars', 'co_lnotab', 'co_name', 'co_names', 'co_nlocals', 'co_stacksize', 'co_varnames']
>>> newsum.__code__.__doc__
'code(argcount, nlocals, stacksize, flags, codestring, constants, names,\n varnames, filename, name, firstlineno, lnotab[, freevars[, cellvars]])\n\nCreate a code object. Not for the faint of heart.'
>>> newsum.__code__.co_varnames
('lst',)
>>> newsum.__code__.co_argcount
1
~~~
所以,各位看官,在使用函數的時候,首先要把它放在對象的層面考量,它不是什么特殊的東西,盡管我們使用了不少篇幅講述它,但它終歸還是一個對象。
- 第零部分 獨上高樓,望盡天涯路
- 嘮叨一些關于Python的事情
- 為什么要開設本欄目
- 第一部分 積小流,至江海
- Python環境安裝
- 集成開發環境(IDE)
- 數的類型和四則運算
- 啰嗦的除法
- 開始真正編程
- 初識永遠強大的函數
- 玩轉字符串(1):基本概念、字符轉義、字符串連接、變量與字符串關系
- 玩轉字符串(2)
- 玩轉字符串(3)
- 眼花繚亂的運算符
- 從if開始語句的征程
- 一個免費的實驗室
- 有容乃大的list(1)
- 有容乃大的list(2)
- 有容乃大的list(3)
- 有容乃大的list(4)
- list和str比較
- 畫圈還不簡單嗎
- 再深點,更懂list
- 字典,你還記得嗎?
- 字典的操作方法
- 有點簡約的元組
- 一二三,集合了
- 集合的關系
- Python數據類型總結
- 深入變量和引用對象
- 賦值,簡單也不簡單
- 坑爹的字符編碼
- 做一個小游戲
- 不要紅頭文件(1): open, write, close
- 不要紅頭文件(2): os.stat, closed, mode, read, readlines, readline
- 第二部分 窮千里目,上一層樓
- 正規地說一句話
- print能干的事情
- 從格式化表達式到方法
- 復習if語句
- 用while來循環
- 難以想象的for
- 關于循環的小伎倆
- 讓人歡喜讓人憂的迭代
- 大話題小函數(1)
- 大話題小函數(2)
- python文檔
- 重回函數
- 變量和參數
- 總結參數的傳遞
- 傳說中的函數條規
- 關于類的基本認識
- 編寫類之一創建實例
- 編寫類之二方法
- 編寫類之三子類
- 編寫類之四再論繼承
- 命名空間
- 類的細節
- Import 模塊
- 模塊的加載
- 私有和專有
- 折騰一下目錄: os.path.<attribute>
- 第三部分 昨夜西風,亭臺誰登
- 網站的結構:網站組成、MySQL數據庫的安裝和配置、MySQL的運行
- 通過Python連接數據庫:安裝python-MySQLdb,連接MySQL
- 用Pyton操作數據庫(1):建立連接和游標,并insert and commit
- 用Python操作數據庫(2)
- 用Python操作數據庫(3)
- python開發框架:框架介紹、Tornado安裝
- Hello,第一個網頁分析:tornado網站的基本結構剖析:improt模塊、RequestHandler, HTTPServer, Application, IOLoop
- 實例分析get和post:get()通過URL得到數據和post()通過get_argument()獲取數據
- 問候世界:利用GAE建立tornado框架網站
- 使用表單和模板:tornado模板self.render和模板變量傳遞
- 模板中的語法:tornado模板中的for,if,set等語法
- 靜態文件以及一個項目框架
- 模板轉義
- 第四部分 暮然回首,燈火闌珊處
- requests庫
- 比較json/dictionary的庫
- defaultdict 模塊和 namedtuple 模塊
- 第五部分 Python備忘錄
- 基本的(字面量)值
- 運算符
- 常用的內建函數
- 擴展閱讀(來自網絡文章)
- 人生苦短,我用Python