Python 函數式編程和面向對象編程
函數式編程
函數:function。
函數式:functional,一種編程範式。函數式編程是一種抽象計算機的編程模式。
函數!= 函數式(如計算!=計算機)
如下是不同語言的抽象 層次不同
- 高階函數
能接收函數做參數的函數:
1.變量可以指向函數
2.函數的參數可以接收變量
3.一個函數可以接收另一個函數作為參數
例子
接收abs函數,定義一個函數,接收x,y,z三個參數。其中x,y是數值,z是函數 。
def add(x,y,z):
return z(x)+z(y)
print add(-2,-3,abs)
其他高階函數:map()函數、reduce()函數、filter()函數
PS:Python的函數不但可以返回int、str、list、dict
等數據類型,還可以返回函數!
閉包
像這種內層函數引用了外層函數的變量(參數也算變量),然後返回內層函數的情況,稱為閉包(Closure)。
特點是返回的函數還引用了外層函數的局部變量,所以,要正確使用閉包,就要確保引用的局部變量在函數返回後不能變。舉例如下:
# 希望一次返回3個函數,分別計算1x1,2x2,3x3:
def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs
f1, f2, f3 = count()
你可能認為調用f1(),f2()和f3()結果應該是1,4,9,但實際結果全部都是 9(請自己動手驗證)。
原因就是當count()函數返回了3個函數時,這3個函數所引用的變量 i 的值已經變成了3。由於f1、f2、f3並沒有被調用,所以,此時他們並未計算 i*i,當 f1 被調用時:
>>> f1()
9 # 因為f1現在才計算i*i,但現在i的值已經變為3
因此,返回函數不要引用任何循環變量,或者後續會發生變化的變量。
匿名函數(lambda )
>>> map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])
[1, 4, 9, 16, 25, 36, 49, 64, 81]
通過對比可以看出,匿名函數lambda x: x * x 實際上就是:
def f(x):
return x * x
關鍵字lambda 表示匿名函數,冒號前面的 x 表示函數參數(匿名函數有個限制,就是只能有一個表達式,不寫return,返回值就是該表達式的結果,返回函數的時候,也可以返回匿名函數。)
裝飾器 decorator
Python 的 decorator 本質上就是一個高階函數,它接收一個函數作為參數,然後,返回一個新函數。
使用 decorator 用 Python 提供的 @ 語法,這樣可以避免手動編寫 f = decorate(f) 這樣的代碼,極大簡化Python代碼。
模塊
導入系統自帶的模塊 math:
import math
如果我們只希望導入用到的 math 模塊的某幾個函數,而不是所有函數,可以用下面的語句:
from math import pow, sin, log
如果遇到名字沖突怎麽辦?
如果使用 import 導入模塊名,由於必須通過模塊名引用函數名,因此不存在沖突;
如果使用from...import
導入log
函數,勢必引起沖突。這時,可以給函數起個“別名”來避免沖突:
from math import log
from logging import log as logger # logging的log現在變成了logger
print log(10) # 調用的是math的log
logger(10, ‘import from logging‘) # 調用的是logging的log
動態導入模塊
下面代碼先嘗試從cStringIO
導入,如果失敗了(比如cStringIO
沒有被安裝),再嘗試從StringIO
導入。
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
利用import ... as ...
,還可以動態導入不同名稱的模塊
try:
import json
except ImportError:
import simplejson as json
PS:
- Python的新版本會引入新的功能,但是實際上這些功能在上一個老版本中就已經存在了。要“試用”某一新的特性,就可以通過導入
__future__
模塊的某些功能來實現。 - 如何區分包和普通目錄:包下面有個
__init__.py
每層都有。
面向對象編程
定義類並創建實例
定義一個Person類如下
class Person(object):
pass
(object),表示該類是從哪個類繼承下來的。
創建實例
xiaoming = Person()
如何讓每個實例擁有各自不同的屬性?
由於Python是動態語言,對每一個實例,都可以直接給他們的屬性賦值,例如,給xiaoming
這個實例加上name、gender和birth
屬性:
xiaoming = Person()
xiaoming.name = ‘Xiao Ming‘
xiaoming.gender = ‘Male‘
xiaoming.birth = ‘1990-1-1‘
給xiaohong
加上的屬性不一定要和xiaoming
相同:
xiaohong = Person()
xiaohong.name = ‘Xiao Hong‘
xiaohong.school = ‘No. 1 High School‘
xiaohong.grade = 2
`實例的屬性可以像普通變量一樣進行操作:
xiaohong.grade = xiaohong.grade + 1
初始化實例屬性
class Person(object):
def __init__(self, name, gender, birth):
self.name = name
self.gender = gender
self.birth = birth
__init__()
方法的第一個參數必須是 self
(也可以用別的名字,但建議使用習慣用法)。後續參數則可以自由指定,和定義函數沒有任何區別。
相應地,創建實例時,就必須要提供除 self 以外的參數:
xiaoming = Person(‘Xiao Ming‘, ‘Male‘, ‘1991-1-1‘)
xiaohong = Person(‘Xiao Hong‘, ‘Female‘, ‘1992-2-2‘)
定義實例方法
class Person(object):
def __init__(self, name):
self.__name = name
def get_name(self): #它的第一個參數永遠是 self,指向調用該方法的實例本身
return self.__name
定義類方法(類似Java的靜態方法)
class Person(object):
count = 0
@classmethod
def how_many(cls): #類方法
return cls.count
def __init__(self, name):
self.name = name
Person.count = Person.count + 1
print Person.how_many()
p1 = Person(‘Bob‘)
print Person.how_many()
訪問限制
如果一個屬性由雙下劃線開頭(__),該屬性就無法被外部訪問(相當於private)。但是,如果一個屬性以"__xxx__"
的形式定義,那它又可以被外部訪問了,以"__xxx__"
定義的屬性在 Python 的類中被稱為特殊屬性,有很多預定義的特殊屬性可以使用,通常我們不要把普通屬性用"__xxx__"
定義。
- 題目
請定義Person類的__init__
方法,除了接受 name、gender 和 birth
外,還可接受任意關鍵字參數,並把他們都作為屬性賦值給實例。
要定義關鍵字參數,使用**kw
;
除了可以直接使用self.name = ‘xxx‘
設置一個屬性外,還可以通過 setattr(self, ‘name‘, ‘xxx‘)
設置屬性。
- 參考代碼:
class Person(object):
def __init__(self, name, gender, birth, **kw):
self.name = name
self.gender = gender
self.birth = birth
for k, v in kw.iteritems():
setattr(self, k, v)
xiaoming = Person(‘Xiao Ming‘, ‘Male‘, ‘1990-1-1‘, job=‘Student‘)
print xiaoming.name
print xiaoming.job
類的繼承
繼承一個類
class Person(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
定義 Student 類時,只需要把額外的屬性加上,例如score:
class Student(Person):
def __init__(self, name, gender, score):
super(Student, self).__init__(name, gender)
self.score = score
一定要用super(Student, self).__init__(name, gender)
去初始化父類,否則,繼承自Person
的Student
將沒有name 和 gender
。函數super(Student, self)
將返回當前類繼承的父類,即 Person
,然後調用__init__()
方法,註意self
參數已在super()
中傳入,在__init__()
中將隱式傳遞,不需要寫出(也不能寫)
判斷類型
函數isinstance()可以判斷一個變量的類型
>>> isinstance(p, Person)
獲取對象信息
除了用isinstance()
判斷它是否是某種類型的實例外,type() 函數獲取變量的類型,它返回一個 Type 對象。可以用dir()
函數獲取變量的所有屬性: dir(s)
。
Python 函數式編程和面向對象編程