Python入門教程(三)


  按理說,這篇是收尾了。可能有一點術語,但大都是顧名思義的。重要概念【】在第二個標題下說明。html


函數式編程(縮寫:FP)python

  若是對此有興趣,能夠看scheme視頻教程(SICP公開課)(scheme是lisp的一種),也能夠直接看SICP中譯本。我只是「知其大略」罷了。算法

  語言若是有如下特徵之一,能夠被認爲有「函數式編程風格」(若是代碼使用了這些特徵,也說這些代碼「有函數式編程風格」):編程

  1:函數和數分庭抗禮,畢竟都是用符號表示而已。若是你在中學時對函數有了不錯的理解(測試:f(x)什麼意思?爲何這樣寫?),那麼會很容易明白下面這幾行代碼在作什麼:
數組

def f(x):
	return 2*x

g = f
print(g(3))

  2:容許用lambda 表達式。這個慢慢會理解的,意思是函數的本質在內容,不在名字,所以操做能夠不取名,直接用內容表示。用處主要在,某些函數(好比map)要求你傳入一個函數當作參數,你能夠不取名(用def定義必須取名)而一會兒傳入。在tk中遇到過用lambda強制傳參,呃……到時候上網查。ide

  3:能夠在函數內再定義函數,python中能夠這麼作,但官方不但願這麼作。函數內部的東西是對外隱藏的,所以若是在A函數內部定義B函數,那麼只有在A內部纔可使用B函數(變量也同樣)。可是Python做爲解釋性語言,每次調用函數就是把內部語句(包括def)全執行一遍,這很是浪費。咱們僅僅是想表示「B函數只能被A引用」,卻要負擔如此後果,彷佛不值得。可是並列放置,難免看起來沒有層次……(這對強迫症尤爲重要)函數式編程


面向對象(縮寫:OOP)函數

  也是一種風格,和FP不衝突,和FP也有交集。顧名思義,就是關注對象。說某種語言面向對象,主要是說有「類」這麼個概念。面向對象也被稱爲「貼近人的思考過程」,人就是面向對象的(能夠先不理解這個說法)。測試

  ……python的類真的不大好講。好比要儲存一個點,你能夠用一個字典(比元組好,由於不須要順序而且份量有名字),像這樣:ui

p1 = {'x' : 3, 'y' : 4}       #表示橫座標爲3,縱座標爲4的點
print(

  類和這很類似,以致於我不懂類的時候,把一個pygame程序中的(簡單的)類全用字典表示了。若是用類,能夠達到這種效果:(省略了定義類的步驟,因此不要嘗試運行)

p1 = Point(3,4)
print(p1.x)
print(p1.y)

  若是你須要好幾個點,那麼這無疑比字典好得多了。或者遇到這種狀況:

a = People('Jack', 7)
print(a.name)
print(a.age)

  這至少看着很優雅。這裏,People和Point是「類」,p1和a是對應類的「實例」,建立實例又稱「實例化」。我如今補上定義部分,應該很容易懂,至少對於一部分……

class Point:
	def __init__(self,a,b):
		self.x = a
		self.y = b
		
class People:
	def __init__(self,a,b):
		self.name = a
		self.age = b

  我一開始也不懂self是啥,並且教程說「能夠把self換成任何東西」。你必定但願寫這樣的代碼,或者假如是這樣你就能懂——(你知道init是英文「定義」的縮寫)

class Point:
	def init(a,b):
		x = a
		y = b
		
class People:
	def init(a,b):
		name = a
		age = b

  實不相瞞,若是不是python,好比C#,那麼就是這樣子的,並無self 。你徹底就能夠把self當成擺設,先用習慣,而後聽我下面的解釋(我認真的)。

  如下兩段代碼作的事是徹底相同的:

class People:
	def __init__(self,a,b):
		self.name = a
		self.age = b

x = People('Jack',7)
class People: pass
x = object.__new__(People)
x.name = 'Jack'
x.age = 7

  首先,name和age看似成了「固有屬性」,其實壓根沒有這回事。在默認狀況下,python中的類是能夠【隨時】添加【任意】屬性的。你能夠在上述(任一)代碼段後加上一句x.hello = 1,而後hello就也成了x的屬性。對於第二段代碼,第一行顯然是「建立了一個叫People 的類,但沒有內容」,第二行是建立了一個People的對象,但顯然它沒有屬性,畢竟定義類的時候什麼都沒寫,剩下兩行給這個對象追加了兩個屬性。其實你大概已經想到,建立和初始化是兩個過程,建立是建立空對象,初始化則添加屬性。而添加什麼屬性,就寫在初始化函數裏。self你也該明白個大概了,若是我再改一改寫法——(先後雙下劃線表示這個函數是官方的,比較特殊,初始化顯然如此)

class People:
	def self.__init__(a,b):
		self.name = a
		self.age = b

  若是寫成這樣,就「差很少是那個意思」了,把self全換成x,就是x的初始化過程。其實還有一重內幕,那就是用點表示屬性也是爲了好看,真實狀況是這樣的:

x.__init__(a,b)     #實際上是下一行的縮寫,相傳python真的會如出一轍地處理
type(x).__init__(x,a,b)    #type函數返回x的類,等價於下一行
People.__init__(x,a,b)

  既然python內部都把__init__當作有三個參數,那咱們定義的時候寫三個參數就合乎情理了。思考:假如你不寫self,你以爲python會怎麼錯誤地【試圖】運行你的東西?

  簡潔模式的People('Jack', 7),其實就是先搞一個空對象,而後(不讓你看地)執行了x.__init__('Jack', 7) 。請注意!這個函數的惟一特殊之處就是,會在用普通方法建立對象時被調用。你能夠在裏面寫任何東西,包括print,就像對待一個普通函數——而不是方法。你若是不用類而寫一個如出一轍的函數,除了不能用「.」來調用,其餘事情徹底相同。甚至你能夠建立完了繼續調用__init__,把x.__init__('Jack', 7)寫多少遍都沒問題(能夠在修改後用它還原),反正python在建立時執行了一遍就無論了。你能夠隨便寫多少個方法。不寫self的後果是,你依然能夠用People.f(a,b)調用之,原理就是剛纔的過程。


編程風格/範式

  能夠參考《冒號課堂——編程範式與OOP思想》,不過要注意python沒有變量類型,致使沒有參數類型,因此自己就是「泛型」;python的一切都是指針(沒有變量類型致使), a = 1 意味着在某處建立一個 1,而後把 a 指向那裏——而 b = a,表示把 a 儲存的位置(這裏是那個1的位置)複製給 b ,函數的參數天然本質上也是這些東西,可是這些地址通常都是不可見也不須要可見的;也所以(「不讓你看到任何與內存有關的東西」),python不自帶數組,python列表和lisp鏈表是一致的,等等。python不多在意底層算法,python使咱們更容易寫出程序結構。


迭代器

  試試 a = range(100) 後 print(a) ,你會發現很奇怪的結果。可是list(a) 就是一個從0到99的列表了,a也並不是全無用處,能夠用 for i in a取代for i in range(100) 。在老版的python中,range(100)就等於0-99的列表,若是打印就會顯示一個列表。可是隻是爲了重複100遍,徹底不必先創建一個如此長的列表,只須要計數就能夠了,這就是迭代器。它很像一個列表,但並無儲存每一項,而是每次計算下一項。這是一類專門用於迭代的對象,能夠用list()來轉換成列表,也能夠直接用for循環遍歷。除了range返回的對象有這種性質,map等函數也是如此。若是你想獲得列表卻獲得了奇怪的東西(不止一種),那頗有可能這個東西具有上述兩個性質,是迭代器。迭代器被稱爲「很具備python風格的用法」


修飾器(裝飾器)

  有一種有趣的函數,它的輸入是一個函數,輸出也是一個函數。這種函數就能夠做爲修飾器,修飾器有個特殊用法,能夠經過在某個def上一行寫@後跟修飾器名,就能夠把那個函數傳入修飾器,並用返回的新函數(「修飾後的函數」)覆蓋原有函數。好比這個例子:

def h(func):
	def func2(x):
		r = func(x)
		print("Be called:",func)
		return r
	return func2
	
	
@h
def f(x):
	return 2*x


	

a = f(3) + 1

  另外自行搜索@property,還有知乎回答這個連接解釋了@staticmethod 和 @classmethod,我是這個回答下的評論。(我感受本身是對的……)


內置函數

  就是python自帶的函數,好比print,input等等。

  (耐心查看) 

  Python 內置函數 | 菜鳥教程



  最後,你應該知道,python開發人員給咱們留下了一些忠告,雖然不是硬性規定——(運行如下代碼)

import this

  (完)

相關文章
相關標籤/搜索