圖解Python 【第五篇】:面向對象-類-初級基礎篇
由於類的內容比較多,分為類-初級基礎篇和類-進階篇
類的內容總覽圖:
本節內容一覽圖:
今天只講類的基礎的面向對象的特性
前言總結介紹:
面向對象是一種編程方式,此編程方式的實現是基於對 類 和 對象 的使用
- 類 是一個模板,模板中包裝了多個“函數”供使用(可以講多函數中公用的變量封裝到對象中)
- 對象,根據模板創建的實例(即:對象),實例用於調用被包裝在類中的函數,對象是一個類的實例
- 實例(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 是 78View 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.showPython偽代碼實現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 【第五篇】:面向對象-類-初級基礎篇