物件導向基礎

morra發表於2016-11-07

物件導向程式設計(Object Oriented Programming,OOP)

  1. 程式導向程式設計最易被初學者接受,其往往用一長段程式碼來實現指定功能,開發過程中最常見的操作就是貼上複製,即:將之前實現的程式碼塊複製到現需功能處。
  2. Java和C#只支援物件導向程式設計,而python比較靈活,即支援物件導向程式設計也支援函數語言程式設計。
  3. 物件導向三大特性:封裝繼承多型

一、封裝

封裝,顧名思義就是將內容封裝到某個地方,以後再去呼叫被封裝在某處的內容。
呼叫被封裝的內容時,有兩種情況:

  1. 通過物件直接呼叫
  2. 通過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)

五、舊式類與新式類

舊式類與新式類區別總結:

  1. 在python2.x的版本才有新式類和舊式類之分
  2. 新式類的存在是為了統一類(class)和型別(type)
  3. 舊式類定義class AA,新式類class AA(object)
  4. 在多重繼承的查詢和呼叫方法上,舊式類是深度優先,新式類是廣度優先
  5. 在python2.x版本中為了確保自己使用的是新式類,有以下方法:
    (a)__metaclass__ = type
    (b)自己的類都從內建類object直接或者間接地繼承
  6. 在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))

相關文章