面向對象技術簡介
- 類(Class): 用來描述具有相同的屬性和方法的對象的集合。它定義了該集合中每個對象所共有的屬性和方法。對象是類的執行個體。
- 類變量:類變量在整個執行個體化的對象中是公用的。類變量定義在類中且在函數體之外。類變量通常不作為執行個體變量使用。
- 資料成員:類變量或者執行個體變量用于處理類及其執行個體對象的相關的資料。
- 方法重寫:如果從父類繼承的方法不能滿足子類的需求,可以對其進行改寫,這個過程叫方法的覆寫(override),也稱為方法的重寫。
- 執行個體變量:定義在方法中的變量,隻作用于目前執行個體的類。
- 繼承:即一個派生類(derived class)繼承基類(base class)的字段和方法。繼承也允許把一個派生類的對象作為一個基類對象對待。例如,有這樣一個設計:一個Dog類型的對象派生自Animal類,這是模拟"是一個(is-a)"關系(例圖,Dog是一個Animal)。
- 執行個體化:建立一個類的執行個體,類的具體對象。
- 方法:類中定義的函數。
- 對象:通過類定義的資料結構執行個體。對象包括兩個資料成員(類變量和執行個體變量)和方法。
一、類
用來描述具有相同的屬性和方法的對象的集合。它定義了該集合中每個對象所共有的屬性和方法。對象是類的執行個體。
二、對象
對象是類的執行個體,比如字元串的基類是str(),那麼可以以下的操作來執行個體化這個類,進而擷取對象
name = str()
這樣就得到了一個那麼對象,它具有str類所擁有的的所有屬性和方法
例如: peple是一個類,x就是這個類的對象
class peple():
def __init__(self, name):
self.Name = name
def chi(self):
print(self.Name)
x = peple("yangyongming")
三、類對象
類對象支援兩種操作:字段引用和執行個體化。
字段引用使用和 Python 中所有的屬性(字段)引用一樣的标準文法:obj.name:如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*
# Created by YangYongming at 2018/11/26 12:27
# FileName: 1.py
# !/usr/bin/python3
class MyClass:
"""一個簡單的類執行個體"""
# 這是一個靜态字段
i = 12345
# 這是一個靜态方法
def f(self):
return 'hello world'
# 執行個體化類,得到一個類對象x
x = MyClass()
# 通路類的屬性和方法
print("MyClass 類的屬性 i 為:", x.i)
print("MyClass 類的方法 f 輸出為:", x.f())
View Code

MyClass 類的屬性 i 為: 12345
MyClass 類的方法 f 輸出為: hello world
Process finished with exit code 0
四、構造方法
用于初始化類的内容屬性的,Python提供的構造函數式 __init__()。
__init__()方法是可選的。
當該類被執行個體化的時候就會執行該函數,我們可以把要先初始化的屬性放到這個函數裡面,如下程式:

#!/usr/bin/env python
# -*- coding: utf-8 -*
# Created by YangYongming at 2018/11/26 12:27
# FileName: 1.py
class Complex:
# 這是一個構造方法
def __init__(self, realpart, imagpart):
# 這是一個普通字段
self.r = realpart
self.i = imagpart
# 執行個體化類
x = Complex(3.0, -4.5) # 在這個過程中構造函數就會被自動執行
print(x.r, x.i) # 輸出結果:3.0 -4.5
執行個體:

3.0 -4.5
Process finished with exit code 0
運作結果
五、類中的self參數
類中出現的self字樣代表類的執行個體(對象),而非類本身
類的方法與普通的函數隻有一個特别的差別——它們必須有一個額外的第一個參數名稱, 按照慣例它的名稱是 self,當然寫成其他的名稱也可以,但是還是寫成官方的好一些o(* ̄︶ ̄*)o,如下代碼:

#!/usr/bin/env python
# -*- coding: utf-8 -*
# Created by YangYongming at 2018/11/26 12:27
# FileName: 1.py
class Test:
def prt(self):
print(self)
print(self.__class__)
t = Test()
t.prt()
代碼

<__main__.Test object at 0x000001AA1763C9B0>
<class '__main__.Test'>
Process finished with exit code 0
運作結果
從執行結果可以很明顯的看出,self 代表的是類的執行個體,代表目前對象的位址,而 self.class 則指向類。
self 不是 python 關鍵字,我們把他換成 其他字元也是可以正常執行的:
在類中,任何加了self的字段或者方法,在類中的任何地方都可以調用,如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*
# Created by YangYongming at 2018/11/26 12:27
# FileName: 1.py
class Complex:
# 這是一個構造方法
def __init__(self, arg1, arg2):
# 這是一個普通字段
self.r = arg1
self.i = arg2
def one(self):
print(self.r)
def two(self):
self.one()
a = Complex(100, 200)
a.one()
a.two()
執行個體:self調用字段和方法,在任何地方

100
100
Process finished with exit code 0
六、類的成員
類的成員可以分為三大類:字段、方法和屬性
注:所有成員中,隻有普通字段的内容儲存對象中,即:根據此類建立了多少對象,在記憶體中就有多少個普通字段。而其他的成員,則都是儲存在類中,即:無論對象的多少,在記憶體中隻建立一份。
(1)、類的字段
類的字段有兩種,一個是普通字段,一個是靜态字段:
靜态字段:在類中,方法之外,通過類名來調用。
普通字段:常出現在構造方法内,通過對象來調用。
- 普通字段屬于對象
- 靜态字段屬于類

#!/usr/bin/env python
# -*- coding: utf-8 -*
# Created by YangYongming at 2018/11/26 12:27
# FileName: 1.py
class Foo:
# 這是一個靜态字段
i = "123456"
# 這是一個構造方法
def __init__(self, name):
# 這是一個普通字段
self.Name = name
# 執行個體化類
r = Foo("MING")
# 通路靜态字段
print(Foo.i)
# 通路普通字段
print(r.Name)
代碼執行個體

123456
MING
Process finished with exit code 0
通過以上代碼可以看出對于靜态字段的調用,通過類調用(也可以通過對象調用,但是不推薦使用對象來調用),因為在其他的語言中是不支援這種調用方式。
- 靜态字段在記憶體中隻儲存一份
- 普通字段在每個對象中都要儲存一份
應用場景: 通過類建立對象時,如果每個對象都具有相同的字段,那麼就使用靜态字段
(2)、類的方法
在類地内部,使用 def 關鍵字來定義一個方法。
方法包括:普通方法、靜态方法和類方法,三種方法在記憶體中都歸屬于類,差別在于調用方式不同。
- 普通方法:由對象調用;至少一個self參數;執行普通方法時,自動将調用該方法的對象指派給self;
- 類方法:由類調用; 至少一個cls參數;執行類方法時,自動将調用該方法的類複制給cls;使用@classmethod标注。
- 靜态方法:由類調用;無預設參數;使用@staticmethod标注。

#!/usr/bin/env python
# -*- coding: utf-8 -*
# Created by YangYongming at 2018/11/26 12:27
# FileName: 1.py
class peple():
live = "yes"
def __init__(self, name):
self.Name = name
def chi(self):
print(self.live)
print(self.Name)
x = peple("ming")
x.chi()
普通方法

yes
ming
Process finished with exit code 0
普通方法可以使用類中的任何屬性

#!/usr/bin/env python
# -*- coding: utf-8 -*
# Created by YangYongming at 2018/11/26 12:27
# FileName: 1.py
class peple():
live = "yes"
def __init__(self, name):
self.Name = name
@staticmethod
def chi():
print(live)
print(self.Name)
peple.chi()
靜态方法

Traceback (most recent call last):
File "C:/Users/Administrator/PycharmProjects/untitled2/Practice/11/1.py", line 17, in <module>
peple.chi()
File "C:/Users/Administrator/PycharmProjects/untitled2/Practice/11/1.py", line 15, in chi
print(live)
NameError: name 'live' is not defined
Process finished with exit code 1
靜态方法不能使用類中的任何屬性

#!/usr/bin/env python
# -*- coding: utf-8 -*
# Created by YangYongming at 2018/11/26 12:27
# FileName: 1.py
class peple():
live = "yes"
def __init__(self, name):
self.Name = name
@classmethod
def chi(cls):
print(cls.live)
@classmethod
def he(cls):
print(cls.Name)
peple.chi()
peple.he()

Traceback (most recent call last):
yes
File "C:/Users/Administrator/PycharmProjects/untitled2/Practice/11/1.py", line 22, in <module>
peple.he()
File "C:/Users/Administrator/PycharmProjects/untitled2/Practice/11/1.py", line 19, in he
print(cls.Name)
AttributeError: type object 'peple' has no attribute 'Name'
Process finished with exit code 1
類方法隻能使用類的靜态屬性(不帶self的),類不能使用方法屬性(帶self的)
相同點:對于所有的方法而言,均屬于類(非對象)中,是以,在記憶體中也隻儲存一份。
不同點:方法調用者不同、調用方法時自動傳入的參數不同。
(3)、類的屬性
。。。。。。
七、類成員修飾符
對于每一個類的成員而言都有兩種形式:
- 公有成員,在任何地方都能通路
- 私有成員,隻有在類的内部才能方法
私有成員和公有成員的定義不同:私有成員命名時,前兩個字元是下劃線。(特殊成員除外,例如:__init__、__call__、__dict__等)

#!/usr/bin/env python
# -*- coding: utf-8 -*
# Created by YangYongming at 2018/11/26 12:27
# FileName: 1.py
class ming:
chi = "這是公有靜态字段"
__he = "這是私有靜态字段"
def __init__(self):
self.name = "這是公有普通字段"
self.__foo = "這是私有普通字段"
def one(self):
return "這是公有普通方法"
def __two(self):
return "這是私有普通方法"
x = ming()
print(ming.chi)
print(x.name)
print(x.one())
示例代碼:類外通路公有成員

這是公有靜态字段
這是公有普通字段
這是公有普通方法
Process finished with exit code 0
類外通路私有成員:報錯
- 公有成員:對象可以通路;類内部可以通路;派生類中可以通路
- 私有成員:僅類内部可以通路;
通過以上代碼可以看出,普通方法、靜态方法使用成員修飾符後,該方法在類外部是無法通路的,在類内部是可以通路的,該種方法是不可以被繼承的
私有的成員也不是絕對不可以通路,我們可以通過下面這種方式通路:
class Mo():
def __one(self):
print("one")
obj = Mo()
obj._Mo__one() # 特殊的通路方式,但是不建議使用
# 運作結果
# one
八、類的特殊成員
(1). __doc__
表示類的描述資訊
(2). __init__
構造方法,通過類建立對象時,自動觸發執行。
(3). __del__
析構方法,當對象在記憶體中被釋放時,自動觸發執行。
注:此方法一般無須定義,因為Python是一門進階語言,程式員在使用時無需關心記憶體的配置設定和釋放,因為此工作都是交給Python解釋器來執行,是以,析構函數的調用是由解釋器在進行垃圾回收時自動觸發執行的。
(4). __call__
對象後面加括号,觸發執行。
注:構造方法的執行是由建立對象觸發的,即:對象 = 類名() ;而對于 __call__ 方法的執行是由對象後加括号觸發的,即:對象() 或者 類()()
(5). __dict__
類或對象中的所有成員
(6) . __str__
如果一個類中定義了__str__方法,那麼在列印 對象 時,預設輸出該方法的傳回值。
(7) . __class__
表示目前操作的對象的類是什麼
(8) . __module__
表示目前操作的對象在那個子產品
九、面向對象三大特性
面向對象的三大特性是指:封裝、繼承和多态。
(1)封裝
對于面向對象的封裝來說,其實就是使用構造方法将内容封裝到 對象 中,然後通過對象直接或者self間接擷取被封裝的内容。
調用被封裝的内容時,有兩種情況:
- 通過對象直接調用
- 通過self間接調用
(2)繼承
繼承,面向對象中的繼承和現實生活中的繼承相同,即:子可以繼承父的内容。
例如:
貓可以:喵喵叫、吃、喝、拉、撒
狗可以:汪汪叫、吃、喝、拉、撒
如果我們要分别為貓和狗建立一個類,那麼就需要為 貓 和 狗 實作他們所有的功能,如下所示:

class 貓:
def 喵喵叫(self):
print '喵喵叫'
def 吃(self):
# do something
def 喝(self):
# do something
def 拉(self):
# do something
def 撒(self):
# do something
class 狗:
def 汪汪叫(self):
print '喵喵叫'
def 吃(self):
# do something
def 喝(self):
# do something
def 拉(self):
# do something
def 撒(self):
# do something
僞代碼
上述代碼不難看出,吃、喝、拉、撒是貓和狗都具有的功能,而我們卻分别的貓和狗的類中編寫了兩次。如果使用 繼承 的思想,如下實作:
動物:吃、喝、拉、撒
貓:喵喵叫(貓繼承動物的功能)
狗:汪汪叫(狗繼承動物的功能)

class 動物:
def 吃(self):
# do something
def 喝(self):
# do something
def 拉(self):
# do something
def 撒(self):
# do something
# 在類後面括号中寫入另外一個類名,表示目前類繼承另外一個類
class 貓(動物):
def 喵喵叫(self):
print '喵喵叫'
# 在類後面括号中寫入另外一個類名,表示目前類繼承另外一個類
class 狗(動物):
def 汪汪叫(self):
print '喵喵叫'
僞代碼
是以,對于面向對象的繼承來說,其實就是将多個類共有的方法提取到父類中,子類僅需繼承父類而不必一一實作每個方法。
注:除了子類和父類的稱謂,你可能看到過 派生類 和 基類 ,他們與子類和父類隻是叫法不同而已。
學習了繼承的寫法之後,我們用代碼來是上述阿貓阿狗的功能:

class Animal:
def eat(self):
print "%s 吃 " %self.name
def drink(self):
print "%s 喝 " %self.name
def shit(self):
print "%s 拉 " %self.name
def pee(self):
print "%s 撒 " %self.name
class Cat(Animal):
def __init__(self, name):
self.name = name
self.breed = '貓'
def cry(self):
print '喵喵叫'
class Dog(Animal):
def __init__(self, name):
self.name = name
self.breed = '狗'
def cry(self):
print '汪汪叫'
# ######### 執行 #########
c1 = Cat('小白家的小黑貓')
c1.eat()
c2 = Cat('小黑的小白貓')
c2.drink()
d1 = Dog('胖子家的小瘦狗')
d1.eat()
代碼執行個體
那麼問題又來了,多繼承呢?
- 是否可以繼承多個類
- 如果繼承的多個類每個類中都定了相同的函數,那麼那一個會被使用呢?
1、Python的類可以繼承多個類,Java和C#中則隻能繼承一個類
2、Python的類如果繼承了多個類,那麼其尋找方法的方式有兩種,分别是:深度優先和廣度優先
- 當類是經典類時,多繼承情況下,會按照深度優先方式查找
- 當類是新式類時,多繼承情況下,會按照廣度優先方式查找
經典類和新式類,從字面上可以看出一個老一個新,新的必然包含了跟多的功能,也是之後推薦的寫法,從寫法上區分的話,如果 目前類或者父類繼承了object類,那麼該類便是新式類,否則便是經典類。

class D:
def bar(self):
print 'D.bar'
class C(D):
def bar(self):
print 'C.bar'
class B(D):
def bar(self):
print 'B.bar'
class A(B, C):
def bar(self):
print 'A.bar'
a = A()
# 執行bar方法時
# 首先去A類中查找,如果A類中沒有,則繼續去B類中找,如果B類中麼有,則繼續去D類中找,如果D類中麼有,則繼續去C類中找,如果還是未找到,則報錯
# 是以,查找順序:A --> B --> D --> C
# 在上述查找bar方法的過程中,一旦找到,則尋找過程立即中斷,便不會再繼續找了
a.bar()
經典類多繼承
經典類多繼承

class D(object):
def bar(self):
print 'D.bar'
class C(D):
def bar(self):
print 'C.bar'
class B(D):
def bar(self):
print 'B.bar'
class A(B, C):
def bar(self):
print 'A.bar'
a = A()
# 執行bar方法時
# 首先去A類中查找,如果A類中沒有,則繼續去B類中找,如果B類中麼有,則繼續去C類中找,如果C類中麼有,則繼續去D類中找,如果還是未找到,則報錯
# 是以,查找順序:A --> B --> C --> D
# 在上述查找bar方法的過程中,一旦找到,則尋找過程立即中斷,便不會再繼續找了
a.bar()
新式類多繼承
新式類多繼承
經典類:首先去A類中查找,如果A類中沒有,則繼續去B類中找,如果B類中麼有,則繼續去D類中找,如果D類中麼有,則繼續去C類中找,如果還是未找到,則報錯
新式類:首先去A類中查找,如果A類中沒有,則繼續去B類中找,如果B類中麼有,則繼續去C類中找,如果C類中麼有,則繼續去D類中找,如果還是未找到,則報錯
注意:在上述查找過程中,一旦找到,則尋找過程立即中斷,便不會再繼續找了
派生類執行基類構造方法的兩種方式

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Author : Jack.Ming
class Annimal:
def __init__(self, weight,hight):
self.Weight = weight
self.Hight = hight
class Cat(Annimal):
def __init__(self, name, weight, hight):
self.Name = name
super(Cat, self).__init__(weight ,hight) #方式1
def pri(self):
print(self.Name,self.Weight,self.Hight)
class Dog(Annimal):
def __init__(self, name, weight, hight):
self.Name = name
Annimal.__init__(self, weight,hight) #方式2
def pri(self):
print(self.Name,self.Weight,self.Hight)
A = Cat("cat", 100,200)
A.pri()
A = Dog("dog", 100,200)
A.pri()
(3)多态
Pyhon不支援Java和C#這一類強類型語言中多态的寫法,但是原生多态,其Python崇尚“鴨子類型”。

class F1:
pass
class S1(F1):
def show(self):
print 'S1.show'
class S2(F1):
def show(self):
print 'S2.show'
# 由于在Java或C#中定義函數參數時,必須指定參數的類型
# 為了讓Func函數既可以執行S1對象的show方法,又可以執行S2對象的show方法,是以,定義了一個S1和S2類的父類
# 而實際傳入的參數是:S1對象和S2對象
def Func(F1 obj):
"""Func函數需要接收一個F1類型或者F1子類的類型"""
print obj.show()
s1_obj = S1()
Func(s1_obj) # 在Func函數中傳入S1類的對象 s1_obj,執行 S1 的show方法,結果:S1.show
s2_obj = S2()
Func(s2_obj) # 在Func函數中傳入Ss類的對象 ss_obj,執行 Ss 的show方法,結果:S2.show
Python僞代碼實作Java或C#的多态
Python僞代碼實作Java或C#的多态

class F1:
pass
class S1(F1):
def show(self):
print 'S1.show'
class S2(F1):
def show(self):
print 'S2.show'
def Func(obj):
print obj.show()
s1_obj = S1()
Func(s1_obj)
s2_obj = S2()
Func(s2_obj)
Python “鴨子類型”
Python “鴨子類型”
參考:http://www.cnblogs.com/wupeiqi/articles/5017742.html
作者:楊永明
出處:https://www.cnblogs.com/ming5218/
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接。