<b>什麼是面向對象程式設計?</b>
面向對象程式設計(object oriented programming,oop,面向對象程式設計)是一種計算機程式設計架構。python就是這種程式設計語言。
面向對象程式設計中的概念主要包括:對象、類、繼承、動态綁定、封裝、多态性、消息傳遞、方法。
1)對象:類的實體,比如一個人。
2)類:一個共享相同結構和行為的對象的集合。通俗的講就是分類,比如人是一類,動物是一類。
3)繼承:類之間的關系,比如貓狗是一類,他們都有四條腿,狗繼承了這個四條腿,擁有了這個屬性。
4)動态綁定:在不修改源碼情況下,動态綁定方法來給執行個體增加功能。
5)封裝:把相同功能的類方法、屬性封裝到類中,比如人兩條腿走路,狗有四條腿走路,兩個不能封裝到一個類中。
6)多态性:一個功能可以表示不同類的對象,任何對象可以有不同的方式操作。比如一個狗會走路、會跑。
7)消息傳遞:一個對象調用了另一個對象的方法。
8)方法:類裡面的函數,也稱為成員函數。
對象=屬性+方法。
屬性:變量。
方法:函數。
執行個體化:建立一個類的具體執行個體對象。比如一條泰迪。
<b>什麼是類?</b>
類是對對象的抽象,對象是類的實體,是一種資料類型。它不存在記憶體中,不能被直接操作,隻有被執行個體化對象時,才會變的可操作。
類是對現實生活中一類具有共同特征的事物的抽象描述。
<b>6.1 類和類方法文法</b>
# 類
class classname():
pass
# 類中的方法
def funcname(self):
pass
self代表類本身。類中的所有的函數的第一個參數必須是self。
<b>6.2 類定義與調用</b>
#!/usr/bin/python
# -*- coding: utf-8 -*-
class myclass():
x = 100
def func(self, name):
return "hello %s!" % name
def func2(self):
return self.x
mc = myclass() # 類執行個體化,綁定到變量mc
print mc.x # 類屬性引用
print mc.func("xiaoming") # 調用類方法
print mc.func2()
# python test.py
100
hello xiaoming!
上面示例中,x變量稱為類屬性,類屬性又分為類屬性和執行個體屬性:
1)類屬性屬于類本身,通過類名通路,一般作為全局變量。比如mc.x
2)如果類方法想調用類屬性,需要使用self關鍵字調用。比如self.x
3)執行個體屬性是執行個體化後對象的方法和屬性,通過執行個體通路,一般作為局部變量。下面會講到。
4)當執行個體化後可以動态類屬性,下面會講到。
類方法調用:
1)類方法之間調用:self.<方法名>(參數),參數不需要加self
2)外部調用:<執行個體名>.<方法名>
<b>6.3 類的說明</b>
給類添加注釋,提高可閱讀性,可通過下面方式檢視。
方法1:
>>> class myclass:
... """
... 這是一個測試類.
... pass
...
>>> print myclass.__doc__
這是一個測試類.
>>>
方法2:
>>> help(myclass)
help on class myclass in module __main__:
class myclass
| 這是一個測試類.
<b>6.4 類内置方法</b>
<b>内置方法</b>
<b>描述</b>
__init__(self, ...)
初始化對象,在建立新對象時調用
__del__(self)
釋放對象,在對象被删除之前調用
__new__(cls, *args, **kwd)
執行個體的生成操作,在__init__(self)之前調用
__str__(self)
在使用print語句時被調用,傳回一個字元串
__getitem__(self, key)
擷取序列的索引key對應的值,等價于seq[key]
__len__(self)
在調用内建函數len()時被調用
__cmp__(str, dst)
比較兩個對象src和dst
__getattr__(s, name)
擷取屬性的值
__setattr__(s, name, value)
設定屬性的值
__delattr__(s, name)
删除屬性
__gt__(self, other)
判斷self對象是否大于other對象
__lt__(self, other)
判斷self對象是否小于other對象
__ge__(self, other)
判斷self對象是否大于或等于other對象
__le__(self, other)
判斷self對象是否小于或等于other對象
__eq__(self, other)
判斷self對象是否等于other對象
__call__(self, *args)
把執行個體對象作為函數調用
<b>6.5 初始化執行個體屬性</b>
很多類一般都有初始狀态的,常常定義對象的共同特性,也可以用來定義一些你希望的初始值。
python類中定義了一個構造函數__init__,對類中的執行個體定義一個初始化對象,常用于初始化類變量。當類被執行個體化,第二步自動調用的函數,第一步是__new__函數。
__init__構造函數也可以讓類傳參,類似于函數的參數。
__init__構造函數使用:
def __init__(self):
self.name = "xiaoming"
def func(self):
return self.name
mc = myclass()
print mc.func()
xiaoming
__init__函數定義到類的開頭.self.name變量是一個執行個體屬性,隻能在類方法中使用,引用時也要這樣self.name。
類傳參:
def __init__(self, name):
self.name = name
def func(self, age):
return "name: %s,age: %s" %(self.name, age)
mc = myclass('xiaoming') # 第一個參數是預設定義好的傳入到了__init__函數
print mc.func('22')
name: xiaoming, age: 22
<b>6.6 類私有化(私有屬性)</b>
<b> 6.6.1 單下劃線</b>
實作子產品級别的私有化,以單下劃線開頭的變量和函數隻能類或子類才能通路。當from modulename import * 時将不會引入以單下劃線卡頭的變量和函數。
_age = 21
def __init__(self, name=none):
self._name = name
return "name: %s, age: %s" %(self._name, age)
mc = myclass('xiaoming')
print mc.func('22')
print mc._name
print mc._age
21
_age和self._name變量其實就是做了個聲明,說明這是個内部變量,外部不要去引用它。
<b> 6.6.2 雙下劃線</b>
以雙下劃線開頭的變量,表示私有變量,受保護的,隻能類本身能通路,連子類也不能通路。避免子類與父類同名屬性沖突。
__age = 21
def __init__(self, name=none):
self.__name = name
def func(self, age):
return "name: %s, age: %s" %(self.__name, age)
print mc.__name
print mc.__age
# python test.py
traceback (most recent call last):
file "test.py", line 12, in <module>
print mc.__name
attributeerror: myclass instance has no attribute '__name'
可見,在單下劃線基礎上又加了一個下劃線,同樣方式類屬性引用,出現報錯。說明雙下劃線變量隻能本身能用。
如果想通路私有變量,可以這樣:
__age = 21
self.__name = name
return "name: %s, age: %s" %(self.__name, age)
print mc._myclass__name
print mc._myclass__age
self.__name變量編譯成了self._myclass__name,以達到不能被外部通路的目的,并沒有真正意義上的私有。
<b> 6.6.3 特殊屬性(首尾雙下劃線)</b>
一般儲存對象的中繼資料,比如__doc__、__module__、__name__:
"""
這是一個測試類說明的類。
# dic()傳回對象内變量、方法
>>> dir(myclass)
['__doc__', '__module__']
>>> myclass.__doc__
'\n\t\xd5\xe2\xca\xc7\xd2\xbb\xb8\xf6\xb2\xe2\xca\xd4\xc0\xe0\xcb\xb5\xc3\xf7\xb5\xc4\xc0\xe0\xa1\xa3\n\t'
>>> myclass.__module__
'__main__'
>>> myclass.__name__
'myclass'
這裡用到了一個新内置函數dir(),不帶參數時,傳回目前範圍内的變量、方法的清單。帶參數時,傳回參數的屬性、方法的清單。
python自己調用的,而不是使用者來調用。像__init__ ,你可以重寫。
部落格位址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
qq群:323779636(shell/python運維開發群)
<b>6.7 類的繼承</b>
子類繼承父類,子類将繼承父類的所有方法和屬性,提高代碼重用。
<b>1)簡單繼承</b>
class parent():
return "name: %s, age: %s" %(self.name, age)
class child(parent):
mc = child('xiaoming')
print mc.name
<b> 2)子類執行個體初始化</b>
如果子類重寫了構造函數,那麼父類的構造函數将不會執行:
self.name_a = "xiaoming"
def funca(self):
return "function a: %s" % self.name_a
self.name_b = "zhangsan"
def funcb(self):
return "function b: %s" % self.name_b
mc = child()
print mc.name_b
print mc.funcb()
print mc.funca()
zhangsan
function b: zhangsan
file "test2.py", line 17, in <module>
print mc.funca()
file "test2.py", line 7, in funca
return "function a: %s" % self.name_a
attributeerror: child instance has no attribute 'name_a'
抛出錯誤,提示調用funca()函數時,沒有找到name_a屬性,也就說明了父類的構造函數并沒有執行。
如果想解決這個問題,可通過下面兩種方法:
方法1:調用父類構造函數
parent.__init__(self)
function a: xiaoming
方法2:使用supper()函數繼承
class parent(object):
super(child, self).__init__()
<b>6.8 多重繼承</b>
每個類可以擁有多個父類,如果調用的屬性或方法在子類中沒有,就會從父類中查找。多重繼承中,是依次按順序執行。
類簡單的繼承:
class a:
self.var1 = "var1"
self.var2 = "var2"
def a(self):
print "a..."
class b:
def b(self):
print "b..."
class c(a,b):
c = c()
c.a()
c.b()
print c.var1
print c.var2
a...
b...
var1
var2
類c繼承了a和b的屬性和方法,就可以像使用父類一樣使用它。
子類擴充方法,直接在子類中定義即可:
def test(self):
print "test..."
c.test()
test...
在這說明下經典類和新式類。
經典類:預設沒有父類,也就是沒繼承類。
新式類:有繼承的類,如果沒有,可以繼承object。在python3中已經預設繼承object類。
經典類在多重繼承時,采用從左到右深度優先原則比對,而新式類是采用c3算法(不同于廣度優先)進行比對。兩者主要差別在于周遊父類算法不同,具體些請在網上查資料。
<b>6.9 方法重載</b>
直接定義和父類同名的方法,子類就修改了父類的動作。
def __init__(self, name='xiaoming'):
def func(self, age=22):
<b>6.10 修改父類方法</b>
在方法重載中調用父類的方法,實作添加功能。
print "------"
print parent.func(self, age) # 調用父類方法
mc.func('22')
------
還有一種方式通過super函數調用父類方法:
print super(child, self).func(age)
file "test2.py", line 15, in <module>
mc.func('22')
file "test2.py", line 11, in func
print super(child, self).func(age)
typeerror: must be type, not classobj
抛出錯誤,因為super繼承隻能用于新式類,用于經典類就會報錯。
那我們就讓父類繼承object就可以使用super函數了:
print super(child, self).func(age) # 調用父類方法。在python3中super參數可不用寫。
<b>6.11 屬性通路的特殊方法</b>
有四個可對類對象增删改查的内建函數,分别是getattr()、hasattr()、setattr()、delattr()。
<b> 6.11.1 getattr()</b>
傳回一個對象屬性或方法。
>>> class a:
... def __init__(self):
... self.name = 'xiaoming'
... def method(self):
... print "method..."
>>> c = a()
>>> getattr(c, 'name', 'not find name!')
'xiaoming'
>>> getattr(c, 'namea', 'not find name!')
>>> getattr(c, 'method', 'not find method!')
<bound method a.method of <__main__.a instance at 0x93fa70>>
>>> getattr(c, 'methoda', 'not find method!')
'not find method!'
<b> 6.11.2 hasattr()</b>
判斷一個對象是否具有屬性或方法。傳回一個布爾值。
>>> hasattr(c, 'name')
true
>>> hasattr(c, 'namea')
false
>>> hasattr(c, 'method')
>>> hasattr(c, 'methoda')
<b> 6.11.3 setattr()</b>
給對象屬性重新指派或添加。如果屬性不存在則添加,否則重新指派。
>>> hasattr(c, 'age')
>>> setattr(c, 'age', 22)
>>> c.age
22
<b> 6.11.4 delattr()</b>
删除對象屬性。
>>> delattr(c, 'age')
>>> hasattr(c, 'age')
<b>6.12 類裝飾器</b>
與函數裝飾器類似,不同的是類要當做函數一樣調用:
class deco:
def __init__(self, func):
self._func = func
self._func_name = func.__name__
def __call__(self):
return self._func(), self._func_name
@deco
def f1():
return "hello world!"
print f1()
('hello world!', 'f1')
<b>6.13 類内置裝飾器</b>
下面介紹類函數裝飾器,在實際開發中,感覺不是很常用。
<b> 6.10.1 @property</b>
@property屬性裝飾器的基本功能是把類中的方法當做屬性來通路。
在沒使用屬性裝飾器時,類方法是這樣被調用的:
... def __init__(self, a, b):
... self.a = a
... self.b = b
... def func(self):
... print self.a + self.b
>>> c = a(2,2)
>>> c.func()
4
>>> c.func
<bound method a.func of <__main__.a instance at 0x7f6d962b1878>>
使用屬性裝飾器就可以像屬性那樣通路了:
... def __init__(self, a, b):
... self.a = a
... self.b = b
... @property
... def func(self):
... print self.a + self.b
file "<stdin>", line 1, in <module>
typeerror: 'nonetype' object is not callable
<b> 6.10.2 @staticmethod</b>
@staticmethod是靜态方法裝飾器,可以通過類對象通路,也可以通過執行個體化後類對象執行個體通路。
執行個體方法的第一個參數是self,表示是該類的一個執行個體,稱為類對象執行個體。
而使用靜态方法裝飾器,第一個參數就不用傳入執行個體本身(self),那麼這個方法當做類對象,由python自身處理。
看看普通方法的用法:
>>> class a:
... def staticmethod(self):
... print "not static method..."
>>> c = a()
>>> c.staticmethod()
not static method...
使用靜态方法則是這麼用:
>>> class a:
... @staticmethod
... def staticmethod():
... print "static method..."
>>> a.staticmethod() # 可以通過類調用靜态方法
static method...
>>> c = a()
>>> c.staticmethod() # 還可以使用普通方法調用
靜态方法和普通的非類方法作用一樣,隻不過命名空間是在類裡面,必須通過類來調用。一般與類相關的操作使用靜态方法。
<b> 6.10.3 @classmethod</b>
<b> </b>@classmethod是類方法裝飾器,與靜态方法裝飾器類似,也可以通過類對象通路。主要差別在于類方法的第一個參數要傳入類對象(cls)。
... @classmethod
... def classmethod(cls):
... print "class method..."
... print cls.__name__
>>> a.classmethod()
class method...
a
<b>6.14 __call__方法</b>
可以讓類中的方法像函數一樣調用。
... def __call__(self, x):
... print "call..."
... print x
>>> c(123)
call...
123
... def __call__(self, *args, **kwargs):
... print args
... print kwargs
>>> c(1,2,3,a=1,b=2,c=3)
(1, 2, 3)
{'a': 1, 'c': 3, 'b': 2}