1. 程式人生 > >圖解Python 【第五篇】:面向對象-類-初級基礎篇

圖解Python 【第五篇】:面向對象-類-初級基礎篇

value 查找 補充 有變 區分 args exist 發生 type

由於類的內容比較多,分為類-初級基礎篇和類-進階篇

類的內容總覽圖:


本節內容一覽圖:
技術分享

今天只講類的基礎的面向對象的特性


前言總結介紹:

面向對象是一種編程方式,此編程方式的實現是基於對 對象 的使用

  • 是一個模板,模板中包裝了多個“函數”供使用(可以講多函數中公用的變量封裝到對象中)
  • 對象,根據模板創建的實例(即:對象),實例用於調用被包裝在類中的函數,對象是一個類的實例
  • 實例(instance):一個對象的實例化實現。
  • 標識(identity):每個對象的實例都需要一個可以唯一標識這個實例的標記。
  • 實例屬性(instance attribute):一個對象就是一組屬性的集合。
  • 實例方法(instance method):所有存取或者更新對象某個實例一條或者多條屬性的函數的集合
  • 類屬性(classattribute):屬於一個類中所有對象的屬性,不會只在某個實例上發生變化
  • 類方法(classmethod):那些無須特定的對性實例就能夠工作的從屬於類的函數。
  • 面向對象三大特性:封裝繼承多態

關於編程方式請點擊這裏


技術分享一、基礎

面向對象編程是一種編程方式,此編程方式的落地需要使用 “類” 和 “對象” 來實現,所以,面向對象編程其實就是對 “類” 和 “對象” 的使用。

  類就是一個模板,模板裏可以包含多個函數,函數裏實現一些功能

  對象則是根據模板創建的實例,通過實例對象可以執行類中的函數

技術分享

  • class是關鍵字,表示類
  • 創建對象,類名稱後加括號即可

類中的函數第一個參數必須是self(詳細見:類的三大特性之封裝)
   類中定義的函數叫做 “方法”


先上圖說說類的各種寫法以及意思(可以略過)

技術分享


一、類的成員

類的成員分為三類:字段方法屬性

技術分享

:所有成員中,只有普通字段的內容保存對象中,即:根據此類創建了多少對象,在內存中就有多少個普通字段。而其他的成員,則都是保存在類中,即:無論對象的多少,在內存中只創建一份。

1、字段

字段:區分普通字段和靜態字段,在定義和使用中有所區別,最本質的區別是內存中保存的位置不同

  • 普通字段屬於對象
  • 靜態字段屬於
技術分享技術分享
class Province:

    # 靜態字段
    country = ‘中國‘

    def __init__(self, name):

        # 普通字段
        self.name = name


# 直接訪問普通字段
obj = Province(‘河北省‘)
print obj.name

# 直接訪問靜態字段
Province.country
字段的定義和使用(普通字段及靜態字段)

註意:特殊方法“init”前後有兩個下劃線!!!

註意到__init__方法的第一個參數永遠是self,表示創建的實例本身,因此,在__init__方法內部,就可以把各種屬性綁定到self,因為self就指向創建的實例本身。

有了__init__方法,在創建實例的時候,就不能傳入空的參數了,必須傳入與__init__方法匹配的參數,但self不需要傳,Python解釋器自己會把實例變量傳進去:

區別:

和普通的函數相比,在類中定義的函數只有一點不同,就是第一個參數永遠是實例變量self,並且,調用時,不用傳遞該參數。除此之外,類的方法和普通函數沒有什麽區別,所以,你仍然可以用默認參數、可變參數、關鍵字參數和命名關鍵字參數

由上述代碼可以看出普通字段需要通過對象來訪問 靜態字段通過類訪問,在使用上可以看出普通字段和靜態字段的歸屬是不同的。其內容在內存中的存儲方式類似於下圖:

技術分享

由上圖可見:

  • 靜態字段在內存中只保存一份
  • 普通字段在每個對象中都要保存一份

應用場景:通過類創建對象時,如果每個對象都具有相同的字段,那麽就使用靜態字段

字段補充內容:

技術分享
class Province:
    country = "中國"

    def __init__(self, name):
        self.name = name

    def show(self):
        print(self.name)

country:已經在內存存儲完成
name:還沒有對象進行實例化,在內存中還沒有存儲
技術分享

2、方法

技術分享

方法包括:普通方法、靜態方法和類方法,三種方法在內存中都歸屬於類,區別在於調用的方式不同。

  • 普通方法:由對象調用;至少一個self參數;執行普通方法時,自動將調用該方法的對象賦值給self
  • 類方法:由調用; 至少一個cls參數,另外添加了一個@classmethod的關鍵字;執行類方法時,自動將調用該方法的復制給cls
  • 靜態方法:由調用;無默認參數,另外添加了一個@staticmethod的關鍵字;
技術分享技術分享
class Foo:

    def __init__(self, name):
        self.name = name

    def ord_func(self):
        """ 定義普通方法,至少有一個self參數 """

        # print self.name
        print ‘普通方法‘

    @classmethod
    def class_func(cls):
        """ 定義類方法,至少有一個cls參數 """

        print ‘類方法‘

    @staticmethod
    def static_func():
        """ 定義靜態方法 ,無默認參數"""

        print ‘靜態方法‘


# 調用普通方法
f = Foo()
f.ord_func()

# 調用類方法
Foo.class_func()

# 調用靜態方法
Foo.static_func()
方法的定義及調用(普通方法,靜態方法及類方法)

技術分享

相同點:對於所有的方法而言,均屬於類,在內存中也只保存一份

不同點:方法的調用者不同,調用方法時自動傳入的參數不同

3、屬性

了解了Python類中的方法,屬性就相對容易理解了,因為Python中的屬性其實就是普通方法的變種。

對於屬性要學習的知識點:

  • 屬性的基本使用
  • 屬性的定義方式(2種)

3.1 屬性的基本使用

技術分享技術分享
# ############### 定義 ###############
class Foo:

    def func(self):
        pass

    # 定義屬性
    @property
    def prop(self):
        pass
# ############### 調用 ###############
foo_obj = Foo()

foo_obj.func()
foo_obj.prop   #調用屬性
屬性的定義及調用

技術分享

由屬性的定義和調用要註意一下幾點:

  • 定義時,在普通方法的基礎上添加 @property 裝飾器;
  • 定義時,屬性僅有一個self參數
  • 調用時,無需括號
    方法:foo_obj.func()
    屬性:foo_obj.prop

註意:屬性存在意義是:訪問屬性時可以制造出和訪問字段完全相同的假象

屬性由方法變種而來,如果Python中沒有屬性,方法完全可以代替其功能。

實例:對於主機列表頁面,每次請求不可能把數據庫中的所有內容都顯示到頁面上,而是通過分頁的功能局部顯示,所以在向數據庫中請求數據時就要顯示的指定獲取從第m條到第n條的所有數據(即:limit m,n),這個分頁的功能包括:

  • 根據用戶請求的當前頁和總數據條數計算出 m 和 n
  • 根據m 和 n 去數據庫中請求數據
技術分享技術分享
# ############### 定義 ###############
class Pager:
    
    def __init__(self, current_page):
        # 用戶當前請求的頁碼(第一頁、第二頁...)
        self.current_page = current_page
        # 每頁默認顯示10條數據
        self.per_items = 10 


    @property
    def start(self):
        val = (self.current_page - 1) * self.per_items
        return val

    @property
    def end(self):
        val = self.current_page * self.per_items
        return val

# ############### 調用 ###############

p = Pager(1)
p.start 就是起始值,即:m
p.end   就是結束值,即:n
代碼實現

從上述可見,Python的屬性的功能是:屬性內部進行一系列的邏輯計算,最終將計算結果返回。

2、屬性的定義方式(2種)

屬性的定義方式:

  • 裝飾器:在方法上應用裝飾器
  • 靜態字段:在類中定義值為property對象的靜態字段

裝飾器方式:在類的普通方法上應用@property裝飾器

我們知道Python中的類有經典類和新式類,新式類的屬性比經典類的屬性豐富。( 如果類繼object,那麽該類是新式類 )

補充:經典類和新式類

技術分享
經典類,具有一種@property裝飾器(如上一步實例)

技術分享技術分享
# ############### 定義 ###############    
class Goods:

    @property
    def price(self):
        return "wupeiqi"
# ############### 調用 ###############
obj = Goods()
result = obj.price  # 自動執行 @property 修飾的 price 方法,並獲取方法的返回值
經典類案例

新式類,具有三種@property裝飾器

技術分享技術分享
# ############### 定義 ###############
class Goods(object):

    @property
    def price(self):
        print ‘@property‘

    @price.setter
    def price(self, value):
        print ‘@price.setter‘

    @price.deleter
    def price(self):
        print ‘@price.deleter‘

# ############### 調用 ###############
obj = Goods()

obj.price          # 自動執行 @property 修飾的 price 方法,並獲取方法的返回值

obj.price = 123    # 自動執行 @price.setter 修飾的 price 方法,並將  123 賦值給方法的參數

del obj.price      # 自動執行 @price.deleter 修飾的 price 方法
新式類案例

註:經典類中的屬性只有一種訪問方式,其對應被 @property 修飾的方法
新式類中的屬性有三種訪問方式,並分別對應了三個被@property、@方法名.setter、@方法名.deleter修飾的方法

由於新式類中具有三種訪問方式,我們可以根據他們幾個屬性的訪問特點,分別將三個方法定義為對同一個屬性:獲取、修改、刪除

技術分享技術分享
class Goods(object):

    def __init__(self):
        # 原價
        self.original_price = 100
        # 折扣
        self.discount = 0.8

    @property
    def price(self):
        # 實際價格 = 原價 * 折扣
        new_price = self.original_price * self.discount
        return new_price

    @price.setter
    def price(self, value):
        self.original_price = value

    @price.deltter
    def price(self, value):
        del self.original_price

obj = Goods()
obj.price         # 獲取商品價格
obj.price = 200   # 修改商品原價
del obj.price     # 刪除商品原價

實例
案例

靜態字段方式,創建值為property對象的靜態字段

當使用靜態字段的方式創建屬性時,經典類和新式類無區別

技術分享技術分享
class Foo:

    def get_bar(self):
        return ‘wupeiqi‘

    BAR = property(get_bar)

obj = Foo()
reuslt = obj.BAR        # 自動調用get_bar方法,並獲取方法的返回值
print reuslt
案例

property的構造方法中有個四個參數

  • 第一個參數是方法名,調用 對象.屬性 時自動觸發執行方法
  • 第二個參數是方法名,調用 對象.屬性 = XXX 時自動觸發執行方法
  • 第三個參數是方法名,調用 del 對象.屬性 時自動觸發執行方法
  • 第四個參數是字符串,調用 對象.屬性.__doc__ ,此參數是該屬性的描述信息
技術分享技術分享
class Foo:

    def get_bar(self):
        return ‘wupeiqi‘

    # *必須兩個參數
    def set_bar(self, value): 
        return return ‘set value‘ + value

    def del_bar(self):
        return ‘wupeiqi‘

    BAR = property(get_bar, set_bar, del_bar, ‘description...‘)

obj = Foo()

obj.BAR              # 自動調用第一個參數中定義的方法:get_bar
obj.BAR = "alex"     # 自動調用第二個參數中定義的方法:set_bar方法,並將“alex”當作參數傳入
del Foo.BAR          # 自動調用第三個參數中定義的方法:del_bar方法
obj.BAE.__doc__      # 自動獲取第四個參數中設置的值:description...
案例

由於靜態字段方式創建屬性具有三種訪問方式,我們可以根據他們幾個屬性的訪問特點,分別將三個方法定義為對同一個屬性:獲取、修改、刪除

技術分享技術分享
class Goods(object):

    def __init__(self):
        # 原價
        self.original_price = 100
        # 折扣
        self.discount = 0.8

    def get_price(self):
        # 實際價格 = 原價 * 折扣
        new_price = self.original_price * self.discount
        return new_price

    def set_price(self, value):
        self.original_price = value

    def del_price(self, value):
        del self.original_price

    PRICE = property(get_price, set_price, del_price, ‘價格屬性描述...‘)

obj = Goods()
obj.PRICE         # 獲取商品價格
obj.PRICE = 200   # 修改商品原價
del obj.PRICE     # 刪除商品原價
案例

註意:Python WEB框架 Django 的視圖中 request.POST 就是使用的靜態字段的方式創建的屬性

技術分享技術分享
class WSGIRequest(http.HttpRequest):
    def __init__(self, environ):
        script_name = get_script_name(environ)
        path_info = get_path_info(environ)
        if not path_info:
            # Sometimes PATH_INFO exists, but is empty (e.g. accessing
            # the SCRIPT_NAME URL without a trailing slash). We really need to
            # operate as if they‘d requested ‘/‘. Not amazingly nice to force
            # the path like this, but should be harmless.
            path_info = ‘/‘
        self.environ = environ
        self.path_info = path_info
        self.path = ‘%s/%s‘ % (script_name.rstrip(‘/‘), path_info.lstrip(‘/‘))
        self.META = environ
        self.META[‘PATH_INFO‘] = path_info
        self.META[‘SCRIPT_NAME‘] = script_name
        self.method = environ[‘REQUEST_METHOD‘].upper()
        _, content_params = cgi.parse_header(environ.get(‘CONTENT_TYPE‘, ‘‘))
        if ‘charset‘ in content_params:
            try:
                codecs.lookup(content_params[‘charset‘])
            except LookupError:
                pass
            else:
                self.encoding = content_params[‘charset‘]
        self._post_parse_error = False
        try:
            content_length = int(environ.get(‘CONTENT_LENGTH‘))
        except (ValueError, TypeError):
            content_length = 0
        self._stream = LimitedStream(self.environ[‘wsgi.input‘], content_length)
        self._read_started = False
        self.resolver_match = None

    def _get_scheme(self):
        return self.environ.get(‘wsgi.url_scheme‘)

    def _get_request(self):
        warnings.warn(‘`request.REQUEST` is deprecated, use `request.GET` or ‘
                      ‘`request.POST` instead.‘, RemovedInDjango19Warning, 2)
        if not hasattr(self, ‘_request‘):
            self._request = datastructures.MergeDict(self.POST, self.GET)
        return self._request

    @cached_property
    def GET(self):
        # The WSGI spec says ‘QUERY_STRING‘ may be absent.
        raw_query_string = get_bytes_from_wsgi(self.environ, ‘QUERY_STRING‘, ‘‘)
        return http.QueryDict(raw_query_string, encoding=self._encoding)
    
    # ############### 看這裏看這裏  ###############
    def _get_post(self):
        if not hasattr(self, ‘_post‘):
            self._load_post_and_files()
        return self._post

    # ############### 看這裏看這裏  ###############
    def _set_post(self, post):
        self._post = post

    @cached_property
    def COOKIES(self):
        raw_cookie = get_str_from_wsgi(self.environ, ‘HTTP_COOKIE‘, ‘‘)
        return http.parse_cookie(raw_cookie)

    def _get_files(self):
        if not hasattr(self, ‘_files‘):
            self._load_post_and_files()
        return self._files

    # ############### 看這裏看這裏  ###############
    POST = property(_get_post, _set_post)
    
    FILES = property(_get_files)
    REQUEST = property(_get_request)

Django源碼
實際應用案例

所以,定義屬性共有兩種方式,分別是【裝飾器】和【靜態字段】,而【裝飾器】方式針對經典類和新式類又有所不同。

其他案例:

技術分享技術分享
class Pager:
    def __init__(self, all_count):
        self.all_count = all_count

    @property  # 定義屬性
    def all_pager(self):
        a1, a2 = divmod(self.all_count, 10)
        if a2 == 0:
            return a1
        else:
            return a1 + 1

    @all_pager.setter               # 給方法添加字段的賦值功能
    def all_pager(self, value):
        print(value)

    @all_pager.deleter              # 給方法添加字段的刪除功能
    def all_pager(self):
        print("del all_pager")


p = Pager(101)
# 不定義屬性
# p.all_count                 # 調用字段
# result = p.all_pager()      # 調用方法
# print(result)

# 定義屬性
ret = p.all_pager           # 使用調用字段的形式 調用方法
print(ret)

p.all_pager = 200
print(p.all_pager)         # 使用調用字段的形式 調用方法

del p.all_pager
應用裝飾器定義方式 技術分享技術分享
class Pager:
    def __init__(self, all_count):
        self.all_count = all_count

    def f1(self):
        return 123

    def f2(self, value):
        return value

    def f3(self):
        return "f3"

    foo = property(fget=f1, fset=f2, fdel=f3)


p = Pager(101)

result = p.foo
print(result, "1")

p.foo = "alex"
result = p.foo
print(result, "2")

del p.foo
靜態字段定義方式

技術分享二、面向對象三大特性

面向對象的三大特性是指:封裝、繼承和多態。

一、封裝

封裝,顧名思義就是將內容封裝到某個地方,以後再去調用被封裝在某處的內容。

所以,在使用面向對象的封裝特性時,需要:

  • 將內容封裝到某處
  • 從某處調用被封裝的內容

第一步:將內容封裝到某處

技術分享

self 是一個形式參數,當執行 obj1 = Foo(‘wupeiqi‘, 18 ) 時,self 等於 obj1

當執行 obj2 = Foo(‘alex‘, 78 ) 時,self 等於 obj2

所以,內容其實被封裝到了對象 obj1 和 obj2 中,每個對象中都有 name 和 age 屬性,在內存裏類似於下圖來保存。

技術分享

第二步:從某處調用被封裝的內容

調用被封裝的內容時,有兩種情況:

  • 通過對象直接調用
  • 通過self間接調用

1、通過對象直接調用被封裝的內容

上圖展示了對象 obj1 和 obj2 在內存中保存的方式,根據保存格式可以如此調用被封裝的內容:對象.屬性名

技術分享
class Foo:
 
    def __init__(self, name, age):
        self.name = name
        self.age = age
 
obj1 = Foo(wupeiqi, 18) 
print obj1.name    # 直接調用obj1對象的name屬性
print obj1.age     # 直接調用obj1對象的age屬性
 
obj2 = Foo(alex, 73)
print obj2.name    # 直接調用obj2對象的name屬性
print obj2.age     # 直接調用obj2對象的age屬性
View Code

2、通過self間接調用被封裝的內容

執行類中的方法時,需要通過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(wupeiqi, 18)
obj1.detail()  # Python默認會將obj1傳給self參數,即:obj1.detail(obj1),所以,此時方法內部的 self = obj1,即:self.name 是 wupeiqi ;self.age 是 18
  
obj2 = Foo(alex, 73)
obj2.detail()  # Python默認會將obj2傳給self參數,即:obj1.detail(obj2),所以,此時方法內部的 self = obj2,即:self.name 是 alex ; self.age 是 78
View Code

綜上所述,對於面向對象的封裝來說,其實就是使用構造方法將內容封裝到 對象 中,然後通過對象直接或者self間接獲取被封裝的內容。


二、繼承

繼承,面向對象中的繼承和現實生活中的繼承相同,即:子可以繼承父的內容。

在OOP程序設計中,當我們定義一個class的時候,可以從某個現有的class繼承,新的class稱為子類(Subclass),而被繼承的class稱為基類父類或超類(Base class、Super class)。

技術分享

例如:

  貓可以:喵喵叫、吃、喝、拉、撒

  狗可以:汪汪叫、吃、喝、拉、撒

如果我們要分別為貓和狗創建一個類,那麽就需要為 貓 和 狗 實現他們所有的功能,如下所示:

技術分享技術分享
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()
代碼實例

所以,對於面向對象的繼承來說,其實就是將多個類共有的方法提取到父類中,子類僅需繼承父類而不必一一實現每個方法。

小結

繼承可以把父類的所有功能都直接拿過來,這樣就不必重零做起,子類只需要新增自己特有的方法,也可以把父類不適合的方法覆蓋重寫

註:除了子類和父類的稱謂,你可能看到過 派生類 和 基類 ,他們與子類和父類只是叫法不同而已。

技術分享

學習了繼承的寫法之後,我們用代碼來是上述阿貓阿狗的功能:

技術分享技術分享
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類,那麽該類便是新式類,否則便是經典類。

新式類和經典類聲明的最大不同在於,所有新式類必須繼承至少一個父類。如果沒有可繼承的類,則可繼承 object 類。object 是“所有類之母” ,它位於所有類繼承結構的最上層。如果沒有直接或間接的子類化一個對象,那麽就定義了一個經典類。即如果沒有指定一個父類,或者如果所子類化的基本類沒有父類,這樣就是創建了一個經典類。

在 Python3 中定義的類,默認就是新式類,而在 Python2 中要定義一個新式類則必須繼承 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類中找,如果還是未找到,則報錯

註意:在上述查找過程中,一旦找到,則尋找過程立即中斷,便不會再繼續找了

重載方法

可以隨時重載父類的方法。 重載父方法的一個原因是:您可能希望在子類中使用特殊或不同的方法功能

基本重載方法

下表列出了可以在自己的類中覆蓋的一些通用方法 -

編號方法描述調用示例
1 __init__ ( self [,args...] ) 構造函數(帶任意可選參數) obj = className(args)
2 __del__( self ) 析構函數,刪除一個對象 del obj
3 __repr__( self ) 可評估求值的字符串表示 repr(obj)
4 __str__( self ) 可打印的字符串表示 str(obj)
5 __cmp__ ( self, x ) 對象比較 cmp(obj, x)

三、多態

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#的多態 技術分享技術分享
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中,實例的變量名如果以__開頭,就變成了一個私有變量(private),只有內部可以訪問,外部不能訪問

以上就是本節對於面向對象初級知識的介紹,總結如下:

用例子來說明:

技術分享


聲明:

本人在學習老男孩python自動化網絡課程後,結合所學整理做次筆記,本文內容多出

Alex老師博客:http://www.cnblogs.com/alex3714/articles/5740985.html

武沛齊老師博客:http://www.cnblogs.com/wupeiqi/articles/5453708.html

感謝老男孩教育老師Alex,武沛齊老師,本文多從二位老師文章中結合整理

http://www.cnblogs.com/wupeiqi/p/4766801.html

http://www.yiibai.com/python/python_classes_objects.html


圖解Python 【第五篇】:面向對象-類-初級基礎篇