日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

Python中菱形继承的MRO顺序及property属性

發布時間:2024/4/11 python 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Python中菱形继承的MRO顺序及property属性 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Python中菱形繼承的MRO順序及property屬性

文章目錄

  • Python中菱形繼承的MRO順序及property屬性
    • 一、Python中菱形繼承的MRO順序
      • 1. 單獨調用父類的方法
      • 2. 多繼承中super調用有所父類的被重寫的方法
      • 3. 單繼承中super
      • 4.類名.__mro__
      • 5.總結
      • 6.例
    • 二、類屬性和實例屬性
      • 1. 類屬性、實例屬性
      • 2. 實例方法、靜態方法和類方法
    • 三、property屬性
      • 1. 什么是property屬性
      • 2. 簡單的實例
      • 3. property屬性的有兩種方式
        • 3.1 裝飾器方式
        • 3.2 類屬性方式,創建值為property對象的類屬性
      • 4、綜上所述:
    • 四、with與“上下文管理器”
      • 1、引入
      • 2、什么是上下文(context)
      • 3、上下文管理器
      • 4、實現上下文管理器的另外方式
      • 5、總結

一、Python中菱形繼承的MRO順序

1. 單獨調用父類的方法

# coding=utf-8print("******多繼承使用類名.__init__ 發生的狀態******") class Parent(object):def __init__(self, name):print('parent的init開始被調用')self.name = nameprint('parent的init結束被調用')class Son1(Parent):def __init__(self, name, age):print('Son1的init開始被調用')self.age = ageParent.__init__(self, name)print('Son1的init結束被調用')class Son2(Parent):def __init__(self, name, gender):print('Son2的init開始被調用')self.gender = genderParent.__init__(self, name)print('Son2的init結束被調用')class Grandson(Son1, Son2):def __init__(self, name, age, gender):print('Grandson的init開始被調用')Son1.__init__(self, name, age) # 單獨調用父類的初始化方法Son2.__init__(self, name, gender)print('Grandson的init結束被調用')gs = Grandson('grandson', 12, '男') print('姓名:', gs.name) print('年齡:', gs.age) print('性別:', gs.gender)print("******多繼承使用類名.__init__ 發生的狀態******\n\n") 運行結果:******多繼承使用類名.__init__ 發生的狀態****** Grandson的init開始被調用 Son1的init開始被調用 parent的init開始被調用 parent的init結束被調用 Son1的init結束被調用 Son2的init開始被調用 parent的init開始被調用 parent的init結束被調用 Son2的init結束被調用 Grandson的init結束被調用 姓名: grandson 年齡: 12 性別: 男 ******多繼承使用類名.__init__ 發生的狀態******

2. 多繼承中super調用有所父類的被重寫的方法

print("******多繼承使用super().__init__ 發生的狀態******") class Parent(object):def __init__(self, name, *args, **kwargs): # 為避免多繼承報錯,使用不定長參數,接受參數print('parent的init開始被調用')self.name = nameprint('parent的init結束被調用')class Son1(Parent):def __init__(self, name, age, *args, **kwargs): # 為避免多繼承報錯,使用不定長參數,接受參數print('Son1的init開始被調用')self.age = agesuper().__init__(name, *args, **kwargs) # 為避免多繼承報錯,使用不定長參數,接受參數print('Son1的init結束被調用')class Son2(Parent):def __init__(self, name, gender, *args, **kwargs): # 為避免多繼承報錯,使用不定長參數,接受參數print('Son2的init開始被調用')self.gender = gendersuper().__init__(name, *args, **kwargs) # 為避免多繼承報錯,使用不定長參數,接受參數print('Son2的init結束被調用')class Grandson(Son1, Son2):def __init__(self, name, age, gender):print('Grandson的init開始被調用')# 多繼承時,相對于使用類名.__init__方法,要把每個父類全部寫一遍# 而super只用一句話,執行了全部父類的方法,這也是為何多繼承需要全部傳參的一個原因# super(Grandson, self).__init__(name, age, gender)super().__init__(name, age, gender)print('Grandson的init結束被調用')print(Grandson.__mro__)gs = Grandson('grandson', 12, '男') print('姓名:', gs.name) print('年齡:', gs.age) print('性別:', gs.gender) print("******多繼承使用super().__init__ 發生的狀態******\n\n") 運行結果:******多繼承使用super().__init__ 發生的狀態****** (<class '__main__.Grandson'>, <class '__main__.Son1'>, <class '__main__.Son2'>, <class '__main__.Parent'>, <class 'object'>) Grandson的init開始被調用 Son1的init開始被調用 Son2的init開始被調用 parent的init開始被調用 parent的init結束被調用 Son2的init結束被調用 Son1的init結束被調用 Grandson的init結束被調用 姓名: grandson 年齡: 12 性別: 男 ******多繼承使用super().__init__ 發生的狀態****** 注意: 以上2個代碼執行的結果不同 如果2個子類中都繼承了父類,當在子類中通過父類名調用時,parent被執行了2次 如果2個子類中都繼承了父類,當在子類中通過super調用時,parent被執行了1

3. 單繼承中super

print("******單繼承使用super().__init__ 發生的狀態******") class Parent(object):def __init__(self, name):print('parent的init開始被調用')self.name = nameprint('parent的init結束被調用')class Son1(Parent):def __init__(self, name, age):print('Son1的init開始被調用')self.age = agesuper().__init__(name) # 單繼承不能提供全部參數print('Son1的init結束被調用')class Grandson(Son1):def __init__(self, name, age, gender):print('Grandson的init開始被調用')super().__init__(name, age) # 單繼承不能提供全部參數print('Grandson的init結束被調用')gs = Grandson('grandson', 12, '男') print('姓名:', gs.name) print('年齡:', gs.age) #print('性別:', gs.gender) print("******單繼承使用super().__init__ 發生的狀態******\n\n")

4.類名.mro

可以使用類名.__mro__的方式查看super在多繼承中init的調用順序,其結果是有C3算法決定的,在多繼承中保證公共基類只被執行一次

5.總結

  • super().__init__相對于類名.init,在單繼承上用法基本無差
  • 但在多繼承上有區別,super方法能保證每個父類的方法只會執行一次,而使用類名的方法會導致方法被執行多次,具體看前面的輸出結果(super相當于一種解決菱形繼承問題的方法)
  • 多繼承時,使用super方法,對父類的傳參數,應該是由于python中super的C3算法導致的原因,必須把參數全部傳遞,否則會報錯
  • 單繼承時,使用super方法,則不能全部傳遞,只能傳父類方法所需的參數,否則會報錯
  • 多繼承時,相對于使用類名.__init__方法,要把每個父類全部寫一遍
  • 而使用super方法,只需寫一句話便執行了全部父類的方法,這也是為何多繼承需要全部傳參的一個原因

6.例

以下的代碼的輸出將是什么? 說出你的答案并解釋。

class Parent(object):x = 1class Child1(Parent):passclass Child2(Parent):passprint(Parent.x, Child1.x, Child2.x) Child1.x = 2 print(Parent.x, Child1.x, Child2.x) Parent.x = 3 print(Parent.x, Child1.x, Child2.x)答案, 以上代碼的輸出是: 1 1 1 1 2 1 3 2 3
  • 使你困惑或是驚奇的是關于最后一行的輸出是 3 2 3 而不是 3 2 1。為什么改變了 Parent.x 的值還會改變Child2.x 的值,但是同時 Child1.x 值卻沒有改變?
  • 這個答案的關鍵是,在 Python 中,類變量在內部是作為字典處理的。
  • 如果一個變量的名字沒有在當前類的字典中發現,將搜索祖先類(比如父類)直到被引用的變量名被找到(如果這個被引用的變量名既沒有在自己所在的類又沒有在祖先類中找到,會引發一個 AttributeError 異常 )
  • 因此,在父類中設置 x = 1 會使得類變量 x 在引用該類和其任何子類中的值為 1。這就是因為第一個 print 語句的輸出是 1 1 1。
  • 隨后,如果任何它的子類重寫了該值(例如,我們執行語句 Child1.x = 2),然后,該值僅僅在子類中被改變。這就是為什么第二個 print 語句的輸出是 1 2 1。
  • 最后,如果該值在父類中被改變(例如,我們執行語句 Parent.x = 3),這個改變會影響到任何未重寫該值的子類當中的值(在這個示例中被影響的子類是 Child2,)。這就是為什么第三個 print 輸出是 3 2 3。
  • 這里也說明的python中的繼承和C++中的繼承的區別,C++ 中的繼承是完全把父類當中的所有都拷貝一份到子類當中,而python中的繼承相當于是引用指向的

二、類屬性和實例屬性

1. 類屬性、實例屬性

它們在定義和使用中有所區別,而最本質的區別是內存中保存的位置不同,

  • 實例屬性屬于對象
  • 類屬性屬于類
class Province(object):# 類屬性country = '中國'def __init__(self, name):# 實例屬性self.name = name# 創建一個實例對象 obj = Province('山東省') # 直接訪問實例屬性 print(obj.name) # 直接訪問類屬性 Province.country

由上述代碼可以看出【實例屬性需要通過對象來訪問】【類屬性通過類訪問】,在使用上可以看出實例屬性和類屬性的歸屬是不同的。

其在內容的存儲方式類似如下圖:

由上圖看出:

  • 類屬性在內存中只保存一份
  • 實例屬性在每個對象中都要保存一份

應用場景:

通過類創建實例對象時,如果每個對象需要具有相同名字的屬性,那么就使用類屬性,用一份既可

2. 實例方法、靜態方法和類方法

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

  • 實例方法:由對象調用;至少一個self參數;執行實例方法時,自動將調用該方法的對象賦值給self;
  • 類方法:由類調用; 至少一個cls參數;執行類方法時,自動將調用該方法的類賦值給cls;
  • 靜態方法:由類調用;無默認參數;
class Foo(object):def __init__(self, name):self.name = namedef ord_func(self):""" 定義實例方法,至少有一個self參數 """# print(self.name)print('實例方法')@classmethoddef class_func(cls):""" 定義類方法,至少有一個cls參數 """print('類方法')@staticmethoddef static_func():""" 定義靜態方法 ,無默認參數"""print('靜態方法')f = Foo("中國") # 調用實例方法 f.ord_func()# 調用類方法 Foo.class_func()# 調用靜態方法 Foo.static_func()

對比

  • 相同點:對于所有的方法而言,均屬于類,所以 在內存中也只保存一份
  • 不同點:方法調用者不同、調用方法時自動傳入的參數不同。

三、property屬性

1. 什么是property屬性

一種用起來像是使用的實例屬性一樣的特殊屬性,可以對應于某個方法

# ############### 定義 ############### class Foo:def func(self):pass# 定義property屬性@propertydef prop(self):pass# ############### 調用 ############### foo_obj = Foo() foo_obj.func() # 調用實例方法 foo_obj.prop # 調用property屬性

property屬性的定義和調用要注意一下幾點:

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

2. 簡單的實例

對于京東商城中顯示電腦主機的列表頁面,每次請求不可能把數據庫中的所有內容都顯示到頁面上

而是通過分頁的功能局部顯示,所以在向數據庫中請求數據時就要顯示的指定獲取從第m條到第n條的所有數據 這個分頁的功能包括:

根據用戶請求的當前頁和總數據條數計算出 m 和 n

根據m 和 n 去數據庫中請求數據

# ############### 定義 ############### class Pager:def __init__(self, current_page):# 用戶當前請求的頁碼(第一頁、第二頁...)self.current_page = current_page# 每頁默認顯示10條數據self.per_items = 10 @propertydef start(self):val = (self.current_page - 1) * self.per_itemsreturn val@propertydef end(self):val = self.current_page * self.per_itemsreturn val# ############### 調用 ############### p = Pager(1) p.start # 就是起始值,即:m p.end # 就是結束值,即:n

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

3. property屬性的有兩種方式

  • 裝飾器 即:在方法上應用裝飾器
  • 類屬性 即:在類中定義值為property對象的類屬性

3.1 裝飾器方式

在類的實例方法上應用@property裝飾器

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

經典類,具有一種@property裝飾器

# ############### 定義 ############### class Goods:@propertydef price(self):return "laowang" # ############### 調用 ############### obj = Goods() result = obj.price # 自動執行 @property 修飾的 price 方法,并獲取方法的返回值 print(result)

新式類,具有三種@property裝飾器
#coding=utf-8

# ############### 定義 ############### class Goods:"""python3中默認繼承object類以python2、3執行此程序的結果不同,因為只有在python3中才有@xxx.setter @xxx.deleter"""@propertydef price(self):print('@property')@price.setterdef price(self, value):print('@price.setter')@price.deleterdef 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@propertydef price(self):# 實際價格 = 原價 * 折扣new_price = self.original_price * self.discountreturn new_price@price.setterdef price(self, value):self.original_price = value@price.deleterdef price(self):del self.original_priceobj = Goods() obj.price # 獲取商品價格 obj.price = 200 # 修改商品原價 del obj.price # 刪除商品原價

3.2 類屬性方式,創建值為property對象的類屬性

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

class Foo:def get_bar(self):return 'laowang'BAR = property(get_bar)obj = Foo() reuslt = obj.BAR # 自動調用get_bar方法,并獲取方法的返回值 print(reuslt)

property方法中有個四個參數

  • 第一個參數是方法名,調用 對象.屬性 時自動觸發執行方法
  • 第二個參數是方法名,調用 對象.屬性 = XXX 時自動觸發執行方法
  • 第三個參數是方法名,調用 del 對象.屬性 時自動觸發執行方法
  • 第四個參數是字符串,調用 對象.屬性.doc ,此參數是該屬性的描述信息
#coding=utf-8 class Foo(object):def get_bar(self):print("getter...")return 'laowang'def set_bar(self, value): """必須兩個參數"""print("setter...")return 'set value' + valuedef del_bar(self):print("deleter...")return 'laowang'BAR = property(get_bar, set_bar, del_bar, "description...")obj = Foo()obj.BAR # 自動調用第一個參數中定義的方法:get_bar obj.BAR = "alex" # 自動調用第二個參數中定義的方法:set_bar方法,并將“alex”當作參數傳入 desc = Foo.BAR.__doc__ # 自動獲取第四個參數中設置的值:description... print(desc) del obj.BAR # 自動調用第三個參數中定義的方法:del_bar方法

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

class Goods(object):def __init__(self):# 原價self.original_price = 100# 折扣self.discount = 0.8def get_price(self):# 實際價格 = 原價 * 折扣new_price = self.original_price * self.discountreturn new_pricedef set_price(self, value):self.original_price = valuedef del_price(self):del self.original_pricePRICE = property(get_price, set_price, del_price, '價格屬性描述...')obj = Goods() obj.PRICE # 獲取商品價格 obj.PRICE = 200 # 修改商品原價 del obj.PRICE # 刪除商品原價

4、綜上所述:

  • 定義property屬性共有兩種方式,分別是【裝飾器】和【類屬性】,而【裝飾器】方式針對經典類和新式類又有所不同。
  • 通過使用property屬性,能夠簡化調用者在獲取數據的流程

四、with與“上下文管理器”

1、引入

  • 對于系統資源如文件、數據庫連接、socket而言,應用程序打開這些資源并執行完業務邏輯之后,必須做的一件事就是要關閉(斷開)該資源。
  • 比如 Python 程序打開一個文件,往文件中寫內容,寫完之后,就要關閉該文件,否則會出現什么情況呢?
  • 極端情況下會出現 “Too many open files” 的錯誤,因為系統允許你打開的最大文件數量是有限的。
  • 同樣,對于數據庫,如果連接數過多而沒有及時關閉的話,就可能會出現 “Can not connect to MySQL server Too many connections”,因為數據庫連接是一種非常昂貴的資源,不可能無限制的被創建。

來看看如何正確關閉一個文件。

普通版: def m1():f = open("output.txt", "w")f.write("python之禪")f.close()

這樣寫有一個潛在的問題,如果在調用 write 的過程中,出現了異常進而導致后續代碼無法繼續執行,close 方法無法被正常調用,因此資源就會一直被該程序占用者釋放。那么該如何改進代碼呢?

進階版: def m2():f = open("output.txt", "w")try:f.write("python之禪")except IOError:print("oops error")finally:f.close()
  • 改良版本的程序是對可能發生異常的代碼處進行 try 捕獲,使用 try/finally 語句
  • 該語句表示如果在 try 代碼塊中程序出現了異常,后續代碼就不再執行,而直接跳轉到 except 代碼塊
  • 無論如何,finally 塊的代碼最終都會被執行。因此,只要把 close 放在 finally 代碼中,文件就一定會關閉。
高級版: def m3():with open("output.txt", "r") as f:f.write("Python之禪")
  • 一種更加簡潔、優雅的方式就是用 with 關鍵字。
  • open 方法的返回值賦值給變量 f,當離開 with 代碼塊的時候,系統會自動調用 f.close() 方法,
  • with 的作用和使用 try/finally 語句是一樣的。

2、什么是上下文(context)

上下文在不同的地方表示不同的含義,要感性理解。context其實說白了,和文章的上下文是一個意思,在通俗一點,我覺得叫環境更好。…

林沖大叫一聲“啊也!”…

問:這句話林沖的“啊也”表達了林沖怎樣的心里?

答:啊你媽個頭啊!

看,一篇文章,給你摘錄一段,沒前沒后,你讀不懂,因為有語境,就是語言環境存在,一段話說了什么,要通過上下文(文章的上下文)來推斷。

3、上下文管理器

任何實現了 __enter__() 和 __exit__() 方法的對象都可稱之為上下文管理器,上下文管理器對象可以使用 with 關鍵字。顯然,文件(file)對象也實現了上下文管理器。

那么文件對象是如何實現這兩個方法的呢?我們可以模擬實現一個自己的文件類,讓該類實現 __enter__() 和 __exit__() 方法。

class File():def __init__(self, filename, mode):self.filename = filenameself.mode = modedef __enter__(self):print("entering")self.f = open(self.filename, self.mode)return self.fdef __exit__(self, *args):print("will exit")self.f.close()

enter() 方法返回資源對象,這里就是你將要打開的那個文件對象,exit() 方法處理一些清除工作。

因為 File 類實現了上下文管理器,現在就可以使用 with 語句了。

with File('out.txt', 'w') as f:print("writing")f.write('hello, python')

這樣,你就無需顯示地調用 close 方法了,由系統自動去調用,哪怕中間遇到異常 close 方法也會被調用。

4、實現上下文管理器的另外方式

Python 還提供了一個 contextmanager 的裝飾器,更進一步簡化了上下文管理器的實現方式。通過 yield 將函數分割成兩部分,yield 之前的語句在 __enter__ 方法中執行,yield 之后的語句在 __exit__ 方法中執行。緊跟在 yield 后面的值是函數的返回值

from contextlib import contextmanager@contextmanager def my_open(path, mode):f = open(path, mode)yield ff.close() 調用with my_open('out.txt', 'w') as f:f.write("hello , the simplest context manager")

5、總結

Python 提供了 with 語法用于簡化資源操作的后續清除操作,是 try/finally 的替代方法,實現原理建立在上下文管理器之上。此外,Python 還提供了一個 contextmanager 裝飾器,更進一步簡化上下管理器的實現方式。

總結

以上是生活随笔為你收集整理的Python中菱形继承的MRO顺序及property属性的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: www,久久久 | 色香色香欲天天天影视综合网 | 在线视频亚洲 | 午夜三级在线 | 日韩一级免费 | 浪潮av网站 | 色婷婷久久 | 成人免费毛片足控 | 亚洲伦理自拍 | 成人av网站大全 | 羞羞网站在线观看 | 又大又粗欧美黑人aaaaa片 | 国产经典自拍 | 91精品国产欧美一区二区 | 女优色图| 国产精品免费大片 | 香蕉国产精品 | 日本在线观看a | 亚洲 国产 日韩 欧美 | 嫩草亚洲| 六月婷婷七月丁香 | 精品色哟哟 | 欧美偷拍少妇精品一区 | 奇米影视777在线观看 | 大尺度在线观看 | 五月天综合视频 | 狂野少女电影在线观看国语版免费 | 亚洲视频欧美视频 | 亚洲理论在线观看 | 香蕉视频97 | 日日操狠狠干 | 日本高清黄色 | 国产精品毛片一区 | 亚州一级| 成人一二区 | 亚洲少妇视频 | 一级黄色片一级黄色片 | 亚洲少妇毛片 | 51精品国产人成在线观看 | 亚洲视频二区 | 国模福利视频 | 陪读偷伦初尝小说 | 中日韩毛片 | 小草av| 亚洲一卡二卡在线 | 欧美 中文字幕 | 魔性诱惑 | 国产免费一区二区三区最新6 | 女女同性女同一区二区三区九色 | 五月天色网站 | 色多多网站 | 日韩午夜片 | 久久成人a | 欧美日本中文字幕 | 国产精品久久久久久久久久久久久久久久久 | 成人午夜av | 91精品国产综合久久久久 | 国产精品国产三级国产aⅴ浪潮 | 免费看黄色的网址 | 成人一级免费视频 | 国产不卡在线观看视频 | 制服诱惑一区二区 | 欧美性生活在线视频 | 精品国产1区2区 | 两根大肉大捧一进一出好爽视频 | 91最新在线视频 | 波多野一区二区 | 成人在线观看免费高清 | av集中营| 日本成人中文字幕 | 91精品一区二区三区四区 | 国产喷水吹潮视频www | 一级免费在线观看 | 国产成人 综合 亚洲 | 免费看黄色a级片 | 日韩欧美爱爱 | 性活交片大全免费看 | 欧美激情性做爰免费视频 | 久久久www免费人成人片 | 免费网站在线高清观看 | 欧美成人综合网站 | www夜夜| 日韩精品在线观看免费 | 国产精品18| 久久久精品人妻一区二区三区四 | 娇小tube性极品娇小 | 无码免费一区二区三区免费播放 | 欧美亚一区二区三区 | 法国性xxxx精品hd | 国产一区 在线播放 | 日韩精品视频在线观看免费 | 欧美亚洲激情视频 | 色婷婷久久一区二区三区麻豆 | 红桃视频一区 | 亚洲AV永久无码国产精品国产 | 性欧美69 | 国产伦精品一区二区三区妓女下载 | 少女逼逼 | 大黑人交xxx极品hd |