物件導向程式設計(Object Oriented Programming,OOP)
- 程式導向程式設計最易被初學者接受,其往往用一長段程式碼來實現指定功能,開發過程中最常見的操作就是貼上複製,即:將之前實現的程式碼塊複製到現需功能處。
- Java和C#只支援物件導向程式設計,而python比較靈活,即支援物件導向程式設計也支援函數語言程式設計。
- 物件導向三大特性:封裝、繼承、多型。
一、封裝
封裝,顧名思義就是將內容封裝到某個地方,以後再去呼叫被封裝在某處的內容。
呼叫被封裝的內容時,有兩種情況:
- 通過物件直接呼叫
- 通過self間接呼叫
#1. 通過物件直接呼叫
class Foo:
def __init__(self, name, age):
self.name = name
self.age = age
obj1 = Foo('morra', 18)
print obj1.name # 直接呼叫obj1物件的name屬性
print obj1.age # 直接呼叫obj1物件的age屬性
#2. 通過self間接呼叫
class Foo:
def __init__(self, name, age):
self.name = name
self.age = age
def detail(self):
print self.name
print self.age
obj1 = Foo('morra', 18)
obj1.detail() # Python預設會將obj1傳給self引數,所以,此時方法內部的 self = obj1
綜上所述,對於物件導向的封裝來說,其實就是使用__init__方法將內容封裝到物件中,然後通過物件直接或者self間接獲取被封裝的內容。
二、繼承
繼承,就是將多個類共有的方法提取到父類中,子類僅需繼承父類而不必一一實現每個方法。父類也叫基類,子類也叫派生類。
class Animals: #基類
def __init__(self, name):
self.name = name
def eat(self):
pass
def drink(self):
pass
class Dog(Animals): #派生類
def __init__(self, name):
self.name = name
def bark(self):
print('汪')
obj_dog = Dog('morra')
obj_dog.eat()
obj_dog.bark()
(1) 單繼承
優先順序是,先子類後父類
(2) 多繼承
python可以同時繼承多個類(C# java是不可以的)。
優先順序是,先子類後父類,父類裡面先左再右,廣度優先。
#新式類的廣度優先查詢
class D(object):
def bar1(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 --> B --> C --> D
# 在上述查詢bar方法的過程中,一旦找到,則尋找過程立即中斷,便不會再繼續找了
a.bar()
(3) 執行父類的構造方法
#方法一:super,通過python多繼承的原則查詢
class Animal:
def __init__(self):
print('A構造方法')
self.ty = "動物"
class Cat(Animal):
def __init__(self):
print('B構造方法')
self.n = "貓"
super(Cat,self).__init__() #執行父類的構造方法,推薦用super()
c = Cat()
print(c.__dict__)
# ---------
# B構造方法
# A構造方法
# {'n': '貓', 'ty': '動物'}
#方法二:Animal,直接指定基類(不建議使用)
class Animal:
def __init__(self):
print('A構造方法')
self.ty = "動物"
class Cat(Animal):
def __init__(self):
print('B構造方法')
self.n = "貓"
Animal.__init__(self) #不推薦
c = Cat()
print(c.__dict__)
# ---------
# B構造方法
# A構造方法
# {'n': '貓', 'ty': '動物'}
三、多型
Pyhon不支援Java和C#這一類強型別語言中多型的寫法,但是原生多型,其Python崇尚“鴨子型別”。
python的“鴨子型別”:
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)
四、反射在物件導向裡的應用
class Foo:
def __init__(self,name):
self.name = name 物件.屬性=變數
pass
def show(self):
pass
obj = Foo('morra')
r = hasattr(Foo, 'show') #只能找類裡的成員
print(r) # True
r = hasattr(obj, 'name') #在物件中可以找自己的屬性,就是self.name中的name屬性
print(r) # True
r = hasattr(obj, 'show') #物件中也可以找類的成員,是通過python裡的類物件指標來實現的
print(r) # True
m = __import__('s1',fromlist=True) #通過反射獲取模組
class_name = getattr(m,"Foo") #在模組中獲取Foo類
obj = class_name('morra') #例項化
val = getattr(obj,'name') #獲取類中的name屬性
print(val)
五、舊式類與新式類
舊式類與新式類區別總結:
- 在python2.x的版本才有新式類和舊式類之分
- 新式類的存在是為了統一類(class)和型別(type)
- 舊式類定義class AA,新式類class AA(object)
- 在多重繼承的查詢和呼叫方法上,舊式類是深度優先,新式類是廣度優先
- 在python2.x版本中為了確保自己使用的是新式類,有以下方法:
(a)__metaclass__ = type
(b)自己的類都從內建類object直接或者間接地繼承 - 在Python3裡面,不存在這些問題了,因為所有的類都是object類的子類(隱式),python3中都是新式類,即使用廣度優先的查詢方法
(1) 定義形式
#python2.7中的舊式類
class AA():
pass
a = AA()
print(type(a)) #<type 'instance'>
print(type(AA)) #<type 'classobj'>
#python2.7中的新式類
class AA(object):
pass
a = AA()
print(type(a)) #<class '__main__.AA'>
print(type(AA)) #<type 'type'>
#python3中的新式類
class AA(object):
pass
a = AA()
print(type(a)) ## <class '__main__.AA'>
print(type(AA)) # <class 'type'>
(2) 多重繼承時的查詢規則
#舊式類的深度優先查詢
class D:
def bar1(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 --> B --> D --> C
# 在上述查詢bar方法的過程中,一旦找到,則尋找過程立即中斷,便不會再繼續找了
a.bar()
#新式類的廣度優先查詢
class D(object):
def bar1(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 --> B --> C --> D
# 在上述查詢bar方法的過程中,一旦找到,則尋找過程立即中斷,便不會再繼續找了
a.bar()
六、補充
(1) isinstance(obj, cls)
檢查是否obj是否是類 cls 的物件
class Foo(object):
pass
obj = Foo()
isinstance(obj, Foo)
(2) issubclass(sub, super)
檢查sub類是否是 super 類的派生類
class Foo(object):
pass
class Bar(Foo):
pass
issubclass(Bar, Foo)
(3) __base__父類查詢
用 __base__ 屬性來查詢某個類的父類
class father:
pass
class child(father):
pass
print(father) #<class '__main__.father'>
print(child.__base__) #<class '__main__.father'>
(4) __class__查詢物件所屬的類和類名
a = [1, 2, 3]
print(a.__class__) #<class 'list'>,查詢物件所屬的類
print(type(a)) #<class 'list'>
print(a.__class__.__name__) #list,查詢物件所屬類的類名
(5) 查詢物件的屬性
除了使用dir()來查詢物件的屬性之外,我們可以使用下面內建(built-in)函式來確認一個物件是否具有某個屬性:
hasattr(obj, attr_name) # attr_name是一個字串
例如:
a = [1,2,3]
print(hasattr(a,'append'))
(6) 查詢函式的引數
import inspect
print(inspect.getargspec(func))