程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

python 面向對象(二)類的繼承-多態-經典類和新式類-靜態方法,類方法,實例方法-python中的下劃線-常用魔術方法

編輯:Python

目錄

1. 類的繼承

1.1  面向對象的好處

 1.2  示例

1.3  super()方法

1.4   類和實例的關系

1.4.1  isinstance ()函數

1.4.2  示例:判斷類與實例的關系

2.多態

2.1   Python中的多態

2.2  Python中的多態- 注意

2.3   多態性的好處

3. 經典類和新式類

3.1. python2和python3的類型區別

3.2  類的多重繼承

3.2.1   類可以多重繼承

3.2.2  經典類和新式類的繼承順序

3.2.3    經典類與新式類的繼承繼承原理

3.2.4  C3算法

 4. 靜態方法,類方法,實例方法

4.1  實例方法

4.2  類方法 @classmethod

4.3  靜態方法  @staticmethod

4.4  各種方法的區別

5. python中的下劃線

5.1  以單下劃線開頭的

5.1.1  在類裡面

5.1.2  在模塊裡面

5.2  以雙下劃線開頭的

5.2.1  在類裡面

5.2.2  在模塊裡面

5.3  以雙下劃線開頭和雙下劃線結尾的

6.python中常見的魔術方法

 構造函數(__new__/__init__)

 析構函數(__del__)

 調用方法(__call__)

 獲取數據(__getitem__)

 刪除數據(__delitem__)

 設置數據(__setitem__)

 其他魔術方法


1. 類的繼承

1.1  面向對象的好處

面向對象的好處 面向對象的編程帶來的主要好處之一是代碼的重用,實現這種重用的方法之一是通過繼承機制。

 

繼承完全可以理解成類之間的類型和子類型關系。 • 可以節省很多的代碼,不需要寫,直接使用 • 衍生出不同的子類,大部分代碼一樣,部分代碼不一樣

 

############################################ 

 1.2  示例

 類的繼承 • 代碼:父類Parent/子類Child • 子類有__ini__: 執行自己的初始化函數 • 子類沒有__init__: 在子類實例化的時候會自動執行父類的構造函數
class Animal():
species = "animal"
count = 0
def __init__(self):
self.name = "animal"
Animal.count += 1
print("初始化animal...")
def breath(self):
print("i can breath")
def eat(self):
print("i can eat")
class Person(Animal):
# 重寫父類的屬性 species
species = "Person"
animal1 = Animal()
print(animal1.count)
# 初始化自己沒有init,會執行父類的__init__
p = Person()
print(p.species, p.count)

 print(p.name)

Person類沒有__init__,所以會調用父類Animal的__init__ 因為Animal的__init__有name屬性,所以不會報錯 print(d.name) 而Dog類自己有__init__,就不會再調用父類的__init__ 但是Dog類的__init__裡面沒有name屬性,所以會報錯
class Dog(Animal):
def __init__(self):
print("i am dog")
# 重寫父類eat方法
def eat(self):
print("dog is eating...")
d = Dog()
# # person類沒有init,會調用父類init
# # 父類init有name屬性,所以不會報錯
# print(p.name)
# # dog類自己有init,不會再調用父類init
# # dog類init裡面沒有name屬性,會報錯
# print(d.name)
d.eat()

############################################ 

1.3  super()方法

如果一個子類的__init__方法想使用父類的__init__中的屬性,這個時候可以使用super()方法

但是super()方法一般寫在最前面,因為如果寫在後面的話父類的__init__方法可能會覆蓋子類的

一些屬性。

class Pig(Animal):
count = 0 # 重寫父類屬性
def __init__(self):
# 調用父類的init
super().__init__()
# super().eat()
self.name = "pig"
Pig.count += 1
print("初始化pig")
print('*' * 20)
pig1 = Pig()
print(pig1.count, animal1.count)
********************
初始化animal...
初始化pig
1 3

############################################ 

1.4   類和實例的關系

1.4.1  isinstance ()函數

isinstance() 函數來判斷一個對象是否是一個已知的類型,類似 type()。

isinstance() 與 type() 區別:

type() 不會認為子類是一種父類類型,不考慮繼承關系。

isinstance() 會認為子類是一種父類類型,考慮繼承關系。

如果要判斷兩個類型是否相同推薦使用 isinstance()。

語法

以下是 isinstance() 方法的語法:

isinstance(object, classinfo)

############################################ 

1.4.2  示例:判斷類與實例的關系

# 類與實例的關系
print('*'*20)
print(isinstance(pig1, Pig), isinstance(pig1, Animal))
print(type(pig1))
a = str(10)
# 所謂的工廠函數其實本質就是類
# ’ABC‘.lower(),就是類調用lower方法
print(type(a), str.lower("ABC"), "ABC".lower())
********************
True True
<class '__main__.Pig'>
<class 'str'> abc abc

############################################ 

2.多態

2.1   Python中的多態

• 多態(Polymorphism)按字面的意思就是“多種狀態”。在面向對象語言中,接口的多種不同的 實現方式即為多態。 • Python是一種多態語言,崇尚鴨子類型。 • 在程序設計中,鴨子類型是動態類型的一種風格。 • 鴨子模型是指:"當看到一只鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那麼這只鳥就 可以被稱為鴨子。" • 我們並不關心對象是什麼類型,到底是不是鴨子,只關心行為
class zhifubao():
def pay(self):
print("this is zhifubao pay")
class weixin():
def pay(self):
print("this is weixin pay")
class bank():
def pay(self):
print("this is bank pay")
z = zhifubao()
w = weixin()
b = bank()
def pay(obj): # 接口的多種形態,接口的重用
obj.pay()
pay(z)
pay(w)
pay(b)
E:\python\python3.9.1\python.exe E:/tlbb/2022-05-28-python面向對象/05.多態.py
this is zhifubao pay
this is weixin pay
this is bank pay
Process finished with exit code 0

############################################ 

2.2  Python中的多態- 注意

• 多態以繼承和重寫父類方法為前提 • 多態是調用方法的技巧,不會影響到類的內部設計############################################ 

2.3   多態性的好處

• 增加了程序的靈活性 • 以不變應萬變,不論對象千變萬化,使用者都是同一種形式去調用,如func(animal) • 增加了程序額可擴展性 • 通過繼承animal類創建了一個新的類,使用者無需更改自己的代碼,還是用func(animal)去調用 • 多態:同一種事物的多種形態,動物分為人類,豬類(在定義角度) • 多態性:一種調用方式,不同的執行效果(多態性) • 實現接口重用############################################ 

3. 經典類和新式類

3.1. python2和python3的類型區別

python2裡面只有繼承了object的類才是新式類,其他的是經典類

python3裡面默認所有類都是繼承的object,所以python3都是新式類


# 經典類 通過type查看到的實例都是 instance
#       類和實例只能通過.__class__屬性
# 新式類 通過type查看到的實例類型就是類名

<class '__main__.A'>
# __main__代表當前模塊
# 代表當前模塊下的A類
[[email protected] lianxi]# python2
Python 2.7.5 (default, Oct 14 2020, 14:45:30)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-44)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> class A: pass
...
>>> a = A()
>>> type(a)
<type 'instance'>
>>> a.__class__
<class __main__.A at 0x7f2a02719258>
>>>
─────────────────────────────────────────────────────────────────
[[email protected] ~]# python3
Python 3.6.8 (default, Nov 16 2020, 16:55:22)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-44)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> class A: pass
...
>>> a = A()
>>> type(a)
<class '__main__.A'>
>>> a.__class__
<class '__main__.A'>
>>>

############################################ 

3.2  類的多重繼承

3.2.1   類可以多重繼承

• 定義類A • 定義類B,C繼承自A • 定義類D,繼承自D • 定義類E,繼承自E • 定義類F,繼承自F

 

class A():
def test(self):
print("from A")
class B(A):
def test(self):
print("from B")
class C(A):
def test(self):
print("from C")
class D(B):
def test(self):
print("from D")
class E(C):
def test(self):
print("from E")
class F(D, E):
def test(self):
print("from F")
f = F()
f.test()

經典類: F--D--B--A--E--C

新式類: F--D--B--E--C--A

############################################ 

3.2.2  經典類和新式類的繼承順序

• Python的類如果繼承了多個類,那麼其尋找方法的方式有兩種,分別是:深度優先和廣度優先

 ############################################ 

3.2.3    經典類與新式類的繼承繼承原理

• MRO(Method Resolution Order):方法解析順序 • MRO是在Python類的多重繼承中,解決當定義了多個同名的方法/屬性時,為避免產生歧義,保證 用戶找到正確的對象所實現的一種算法。 • 對於你定義的每一個類,Python會計算出一個方法解析順序(MRO)列表,這個MRO列表就是一 個簡單的所有基類的線性順序列表• Python2.2以前的版本:經典類(classic class)時代 • MRO的方法為DFS(深度優先搜索(子節點順序:從左到右)) • Python2.2版本:新式類(new-style class)誕生,這時有兩種MRO的方法 • 經典類MRO為DFS(深度優先搜索(子節點順序:從左到右)) • 新式類MRO為BFS(廣度優先搜索(子節點順序:從左到右)) • Python2.3到Python2.7:經典類、新式類和平發展 • 從Python2.3開始新式類的MRO取而代之的是C3算法 • Python3到至今:新式類一統江湖 • Python3開始就只存在新式類了,采用的MRO也依舊是C3算法############################################ 

3.2.4  C3算法

# c3算法
# 首先將自身類加入本序列,然後再對繼承序列的元素依次判斷
# 若某個元素不在其他序列或者他是所有繼承序列的第一個,那麼就把這個元素提取到本序列

############################################  

 4. 靜態方法,類方法,實例方法

class A:
name = "class A"
def __init__(self): # 自動調用
self.country = "china"
# 實例方法,第一參數代表實例本身
def normal_method(self, name):
# 方法的裡面,即可以訪問類屬性,又可以訪問實例屬性
print("normal:")
print(self.name, name)
# 類方法 cls代表類
@classmethod # 裝飾器 被裝飾器裝飾過的方法稱為類方法
def class_method(cls, name):
# 類方法可以類屬性
print("classmethod")
print(cls, cls.name)
@staticmethod # 靜態方法
def static_method(name):
# 靜態方法可以通過類名去訪問類屬性
print("static_method", name, A.name)
a1 = A()
# 靜態方法,實例方法,類方法都能通過實例去調用
a1.normal_method("a1")
a1.class_method("a1")
a1.static_method("a1")
# 也可以通過類去調用
# 類調用實例方法需要傳入實例參數
A.normal_method(a1, "A")
A.class_method("A")
A.static_method("A")

############################################  

4.1  實例方法

實例方法,第一參數代表實例本身

在實例方法裡面即可以訪問類屬性,又可以訪問實例屬性

可以通過self訪問實例屬性
 # 實例方法,第一參數代表實例本身
def normal_method(self, name):
# 方法的裡面,即可以訪問類屬性,又可以訪問實例屬性
print("normal:")
print(self.name, name)

############################################  

4.2  類方法 @classmethod

@classmethod  --> 裝飾器

被裝飾器裝飾過的方法稱為類方法

類方法可以訪問類屬性

可以通過cls訪問類屬性(希望取的值不受實例影響時使用)
 # 類方法 cls代表類
@classmethod # 裝飾器 被裝飾器裝飾過的方法稱為類方法
def class_method(cls, name):
# 類方法可以類屬性
print("classmethod")
print(cls, cls.name)

############################################  

4.3  靜態方法  @staticmethod

靜態方法可以通過類名去訪問類屬性

 @staticmethod # 靜態方法
def static_method(name):
# 靜態方法可以通過類名去訪問類屬性
print("static_method", name, A.name)

############################################  

4.4  各種方法的區別

 各種方法的區別(@staticmethod、@classmethod) • 實例方法不能通過類名調用,但是靜態方法和類方法是可以(不實例化訪問) • 實例方法:可以通過self訪問實例屬性 • 類方法:可以通過cls訪問類屬性(希望取的值不受實例影響時使用) • 靜態方法:不可以訪問,通過傳值的方式

############################################  

5. python中的下劃線

• 標識符是用來標識某種對象的名稱。 • 在命名標識符時,需要遵循一定規則。標識符的第一個字符必須是字母(大小寫均可),或者是一個下劃 線("_")。 • 以下劃線開頭的標識符是有特殊意義的

############################################  

5.1  以單下劃線開頭的

5.1.1  在類裡面

這類成員變量叫做保護變量,意思是只有類對象和子類對象自己能訪問到這些變量

5.1.2  在模塊裡面

如果你寫了代碼“from <模塊/包名> import *”,那麼以“_”開頭的模塊和包都不會被導 入,除非模塊或包中的“__all__”列表顯式地包含了模塊和包。 • 這有點類似於慣例,為了使其他人(或你自己)使用這些代碼時將會知道以“_”開頭的名稱只供內部 使用。正如Python文檔中所述:以下劃線“_”為前綴的名稱(如_spam)應該被視為API中非公開的 部分(不管是函數、方法還是數據成員)。此時,應該將它們看作是一種實現細節,在修改它們時無 需對外部通知。

############################################  

5.2  以雙下劃線開頭的

5.2.1  在類裡面

• 類:只有類對象自己能訪問,連子類對象也不能訪問到這個數據。強行訪問“對象名._類名 __xxx“這樣的方式

5.2.2  在模塊裡面

不能模糊導入模塊  即 不能用“ from xxx import *“導入包/模塊。

• 名稱(具體為一個方法名)前雙下劃線(“__“)的用法並不是一種慣例,對解釋器來說它有特定 的意義。Python中的這種用法是為了避免與子類定義的名稱沖突。Python文檔指出,“__spam” 這種形式(至少兩個前導下劃線,最多一個後續下劃線)的任何標識符將會被 “_classname__spam”這種形式原文取代,在這裡“classname”是去掉前導下劃線的當前類 名。
class P:
"""
this is P
"""
_min = 1 # 保護屬性
__max = 10 # 私有屬性
def __init__(self):
self.name = "sc"
self.age = 4
self.__desc = "it"
def __make(self):
print("這是一個私有方法")
print(self.__desc)
def _protectmake(self):
print("這是一個保護方法")
def show(self):
print(self.__max, self.__desc)
class Child(P):
def show(self):
print(self.__max)
#
#
p = P()
c = Child()
# 保護屬性和普通屬性沒有區別
print(c._min, p._min, Child._min, P._min)
# 訪問私有屬性 子類訪問不到私有成員
# print(c.__make)
# 類對象也不能訪問私有成員
# print(p.__max)
# p.__make()
# 想訪問__make()要加上類名 p._P__make()
# 私有成員只能在類的內部訪問
p.show()
print('*' * 20)
print(c._min)
# print(dir(p))
# '_P__desc', '_P__make', '_P__max',
# python中的私有都是偽私有,實際其實就是將雙下劃線開頭的標識符
# 改了一個名字存儲 _類名__標識符
E:\python\python3.9.1\python.exe E:/tlbb/2022-05-28-python面向對象/08.python中的下劃線.py
1 1 1 1
10 it
********************
1
Process finished with exit code 0

############################################  

5.3  以雙下劃線開頭和雙下劃線結尾的

 以雙下劃線開頭和結尾的( __foo__ ) • 代表Python中特殊方法專用的標識。其實,這只是一種慣例,對Python系統來說,這將確保不會 與用戶自定義的名稱沖突。 • 例如__init__()代表類的構造函數。 • 雖然你也可以編寫自己的特殊方法名,但不建議用戶使用這種命名方式。__dict__ 查看命名空間__class__ 查看對象屬於哪個類__name__查看類的名稱 __module__查看所在哪個模塊 __doc__ 文檔注釋, 類, 函數 的文檔注釋都會放到__doc__裡面
# 常見的以雙下劃線開頭和以雙下劃線結尾的特殊變量
# __dict__ # 查看命名空間
print(P.__dict__)
print(p.__dict__)
print(P.__name__)
# __class__ 查看對象屬於哪個類
print(p.__class__)
# __module__ 查看所在哪個模塊
print(P.__module__)
#
# __doc__文檔注釋, 類,函數的文檔注釋都會放到 __doc__裡面
print(P.__doc__)
E:\python\python3.9.1\python.exe E:/tlbb/2022-05-28-python面向對象/08.python中的下劃線.py
{'__module__': '__main__', '__doc__': '\n this is P\n ', '_min': 1, '_P__max': 10, '__init__': <function P.__init__ at 0x0000021A1689DC10>, '_P__make': <function P.__make at 0x0000021A168E1940>, '_protectmake': <function P._protectmake at 0x0000021A168E19D0>, 'show': <function P.show at 0x0000021A168E1A60>, '__dict__': <attribute '__dict__' of 'P' objects>, '__weakref__': <attribute '__weakref__' of 'P' objects>}
{'name': 'sc', 'age': 4, '_P__desc': 'it'}
P
<class '__main__.P'>
__main__
this is P
Process finished with exit code 0

############################################  

6.python中常見的魔術方法

魔術方法 :一般以雙下劃線開頭和雙下劃線結尾

而且是有特殊含義的方法,一般不需要手動去調用

它會在某種特定場景下自動執行

 構造函數(__new__/__init__)

• __new__:創建實例 • __init__:初始化實例

 析構函數(__del__)

• 在實例釋放、銷毀的時候自動執行的,通常用於做一些收尾工作, 如關閉一些數據庫連接,關閉 打開的臨時文件
class A:
def __del__(self):
print("this is A.del")
a1 = A()
del a1
print("xxxxxx")

 調用方法(__call__)

• 把類實例化後的對象當做函數來調用的時候自動被調用
class A:
def __call__(self, name):
print(f"i am A.__call__,name is {name}")
a1 = A()
a1("sc")
def func1():
pass
print(dir(func1))

自定義異常類

class NotIsIntException(Exception):
def __str__(self):
return 'NotIsIntException類型不是整數'
n = input('請輸入一個整數')
try:
if n.isdigit():
print(n)
else:
raise NotIsIntException
except NotIsIntException as ni:
print(ni)

############################################  

 獲取數據(__getitem__)

• a[key]來訪問對象,a[1:3]切片時調用__getitem__

 刪除數據(__delitem__)

• del r[1] 時調用__delitem__方法

 設置數據(__setitem__)

• r['e']=213時調用__setitem__
class A:
def __init__(self):
self.data = {}
def __getitem__(self, key):
print("get data")
return self.data.get(key, 0)
def __setitem__(self, key, value):
print("set data:", key, value)
self.data[key] = value
def __delitem__(self, key):
print("delete data")
del(self.data[key])
a1 = A()
print(a1["key1"])
a1["key1"] = "xxxxxxx"
del a1["key1"]

############################################  

 其他魔術方法

• __eq__(self, other) 定義了等號的行為, == • __ne__(self, other) 定義了不等號的行為, != • __lt__(self, other) 定義了小於號的行為, < • __gt__(self, other) 定義了大於等於號的行為, >= • __add__(self, other) +運算 • __mul__(self, other) *運算 • __len__(self, other) 獲得長度以加法為例:
class B:
def __init__(self, num):
self.num = num
def __add__(self, x):
print("this is add")
return self.num + x
a = B(4)
print(a + 6)


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved