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

歡迎訪問 生活随笔!

生活随笔

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

python

深入理解python面向对象_转:Python3 面向对象,较为深入的两个理解

發布時間:2023/12/2 python 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 深入理解python面向对象_转:Python3 面向对象,较为深入的两个理解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一,

1. 類的聲明和創建

對于 Python 函數來說,聲明與定義類沒什么區別,因為他們是同時進行的,定義(類體)緊跟在聲明(含 class 關鍵字的頭行[header line])和可選(但總是推薦使用)的文檔字符串后面。同時,所有的方法也必須同時被定義。

請注意 Python 并不支持純虛函數(像 C++)或者抽象方法(如在 JAVA 中),這些都強制程序員在子類中定義方法。作為替代方法,你可以簡單地在基類方法中引發 NotImplementedError 異常,這樣可以獲得類似的效果。

2. 有關類的屬性

(1)查看類的屬性

要知道一個類有哪些屬性,有兩種方法。最簡單的是使用 dir()內建函數(也可以查看實例屬性)。另外是通過訪問類的字典屬性__dict__,這是所有類都具備的特殊屬性之一。

python

>>> class HaHa:

"""Haha to you!"""

variable1 = "Good"

variable2 = "Nice"

def change(self):

self.variable1 = "Bad"

>>> dir(HaHa)

['__doc__', '__module__', 'change', 'variable1', 'variable2']

>>> HaHa.__dict__

{'variable1': 'Good', '__module__': '__main__', 'variable2': 'Nice', '__doc__': 'Haha to you!', 'change': }

1

2

3

4

5

6

7

8

9

10

11

dir()返回的僅是對象的屬性的一個名字列表, 而__dict__返回的是一個字典,它的鍵(keys)是屬性名,鍵值(values)是相應的屬性對象的數據值。

(2)類的特殊屬性

C.__name__????? 類C的名字(字符串)

C.__doc__?????? 類C的文檔字符串

C.__bases__???? 類C的所有父類構成的元組

C.__dict__????? 類C的屬性

C.__module__??? 類C定義所在的模塊(1.5 版本新增)

C.__class__???? 實例C對應的類(僅新式類中)

1

2

3

4

5

6

3. 對象

(1)Understanding __new__ and __init__

Understanding __new__ and __init__

(2)__del__()方法

有一個相應的特殊解構器(destructor)方法名為__del__()。然而,由于 Python 具有垃圾對象回收機制(靠引用計數),這個函數要直到該實例對象所有的引用都被清除掉后才會執行。

Python 中的解構器是在實例釋放前提供特殊處理功能的方法,它們通常沒有被實現,因為實例很少被顯式釋放。

要注意,解構器只能被調用一次,一旦引用計數為 0,則對象就被清除了。

總結:

不要忘記首先調用父類的__del__()。

調用 del x 不表示調用了 x.__del__() —–它僅僅是減少 x 的引用計數。

如果你有一個循環引用或其它的原因,讓一個實例的引用逗留不去, 該對象的__del__()可能永遠不會被執行。

__del__()未捕獲的異常會被忽略掉 (因為一些在__del__()用到的變量或許已經被刪除了)。

不要在__del__()中干與實例沒任何關系的事情。

除非你知道你正在干什么,否則不要去實現__del__()。

如果你定義了__del__(),并且實例是某個循環的一部分,垃圾回收器將不會終止這個循環——你需要自已顯式調用 del。

(3)跟蹤對象

Python 沒有提供任何內部機制來跟蹤一個類有多少個實例被創建了,或者記錄這些實例是些什么東西。如果需要這些功能,你可以顯式加入一些代碼到類定義或者__init__()和__del__()中去。最好的方式是使用一個靜態成員來記錄實例的個數。 靠保存它們的引用來跟蹤實例對象是很危險的,因為你必須合理管理這些引用,不然,你的引用可能沒辦法釋放(因為還有其它的引用)!

class InstCt(object):

count = 0 # count is class attr

def __init__(self): # increment count

InstCt.count += 1

def __del__(self): # decrement count

InstCt.count -= 1

def howMany(self): # return count

return InstCt.count

>>> a = InstTrack()

>>> b = InstTrack()

>>> b.howMany()

2

>>> a.howMany()

2

>>> del b

>>> a.howMany()

1

>>> del a

>>> InstTrack.count

0

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

(4)類、實例的其它內建函數

issubclass() 布爾函數判斷一個類是另一個類的子類或子孫類。它有如下語法:issubclass(sub, sup)

isinstance() 布爾函數在判定一個對象是否是另一個給定類的實例時,非常有用。它有如下語法:isinstance(obj1, obj2)

hasattr(), getattr(),setattr(), delattr()這些函數顧名思義,不做解釋。

4. 靜態方法和類方法

有兩種方式聲明靜態方法和類方法:

使用staticmethod()和 classmethod()內建函數

class TestStaticMethod:

def foo():

print 'calling static method foo()'

foo = staticmethod(foo)

class TestClassMethod:

def foo(cls):

print 'calling class method foo()'

print 'foo() is part of class:', cls.__name__

foo = classmethod(foo)

1

2

3

4

5

6

7

8

9

10

使用裝飾器

class TestStaticMethod:

@staticmethod

def foo():

print 'calling static method foo()'

class TestClassMethod:

@classmethod

def foo(cls):

print 'calling class method foo()'

print 'foo() is part of class:', cls.__name__

1

2

3

4

5

6

7

8

9

10

靜態方法和類方法的區別:

靜態方法沒有cls參數,所以它既不能訪問實例變量,也不能訪問類變量。

5. 繼承

(1)__bases__類屬性

我們可以通過此屬性獲得父類的信息。語法:ClassName.__bases__

(2)方法覆蓋(overriding)

Code example :

>>> class Parent(object):

def foo(self):

print 'Parent foo'

>>> class Son(Parent):

def foo(self):? # 父類的foo方法被覆蓋

print 'Son foo'

>>> son = Son()

>>> son.foo()

Son foo

1

2

3

4

5

6

7

8

9

10

11

12

13

被覆蓋了的父類方法可以通過super()函數在子類中調用.

Code Example :

>>> class NewSon(Parent):

def foo(self):

super(NewSon, self).foo()

>>> new_son = NewSon()

>>> new_son.foo()

Parent foo

1

2

3

4

5

6

7

8

注意:

子類覆蓋父類的__init__()方法后,如果你想調用父類的此方法,你必須顯示調用!Python默認不會幫我們做這件事。

(3)多重繼承

方法解釋順序(MRO)

經典類采用的是深度優先算法(python2.2之前),而新式類采用的是廣度優先算法。因為在新式類中使用深度優先,會出現菱形效應。

假設我們有如下繼承結構的類:

這里寫圖片描述

左邊為經典類情況下,右邊為新式類情況下。在新式類下B,C都繼承自object類。

在新式類(右邊繼承結構)中采用舊的深度優先算法,假設在D的實例d中調用foo()方法,對于此方法的搜索順序是D->B->A->C;采用廣度優先算法,搜索順序為D->B->C-A。因為D繼承了B、C,多數情況下我們更希望首先搜索的是C而不是A,因為假設A、C中都有foo()方法時,你可能覺得A中的foo()方法太過通用了。很典型的就是__init__()方法。

(4)從標準類派生

比如你想從float類派生出一個子類,這都是很常見的需求。下面看兩個例子:

繼承float類

>>> class RoundFloat(float):

...???? def __new__(cls, val):

...???????????? return super(RoundFloat, cls).__new__(cls, round(val, 2))

...

>>> RoundFloat(1.5955)

1.6

1

2

3

4

5

6

我們派生了一個可以自動四舍五入到兩位小數的RoundFloat類。

繼承dict類

>>> class SortedKeyDict(dict):

...???? def keys(self):

...???????????? return sorted(super(SortedKeyDict, self).keys())

...

>>> dict1 = SortedKeyDict((('wang', 1), ('jiang', 2), ('guo', 3), ('han', 4)))

>>> dict1.keys()? # 排序了

['guo', 'han', 'jiang', 'wang']

>>> [key for key in dict1]? # 散列順序

['guo', 'jiang', 'wang', 'han']

1

2

3

4

5

6

7

8

9

6. 特殊方法定制類

Python中有很多特殊方法,它們是以__開頭和結尾的。使用它們可以實現:

模擬標準類型

重載操作符

1

2

3

4

5

6

7. 私有化

Python 為類元素(屬性和方法)的私有性提供初步的形式。由雙下劃線開始的屬性在運行時被“混淆”(mixin),所以直接訪問是不允許的。

混淆會在名字前面加下劃線和類名。比如,以例NumStr類中的 self.__num 屬性為例,被“混淆”后,用于訪問這個數據值的標識就變成了self._NumStr__num。把類名加上后形成的新的“混淆”結果將可以防止在祖先類或子孫類中的同名沖突。

8. 包裝和授權

(1)包裝(wrapping)

定義:

對一個已存在的對象進行包裝,不管它是數據類型,還是一段代碼,可以是對一個已存在的對象,增加新的,刪除不要的,或者修改其它已存在的功能。你可以包裝任何類型作為一個類的核心成員,以使新對象的行為模仿你想要的數據類型中已存在的行為,并且去掉你不希望存在的行為。

包裝類:

你可以包裝類,但是實際上沒有必要。因為你完全可以通過派生實現相同的效果。

(2)授權

簡介:

授權是包裝的一個特性,采用已存在的功能以達到最大限度的代碼重用。包裝一個類型通常是對已存在的類型的一些定制。授權的過程,即是所有更新的功能都是由新類的某部分來處理,但已存在的功能就授權給已存在的對象的默認屬性。

實現授權:

實現授權的關鍵點就是覆蓋__getattr__()方法,在代碼中包含一個對 getattr()內建函數的調用。特別地,調用 getattr()以得到默認對象屬性(數據屬性或者方法)并返回它以便訪問或調用。特殊方法__getattr__()的工作方式是, 當搜索一個屬性時, 任何局部對象首先被找到 (定制的對象)。如果搜索失敗了,則__getattr__()會被調用,然后調用 getattr()得到一個對象的默認行為。

我們來實現一個包裝文件對象的例子:

>>> class UpperFile(object):

...???? def __init__(self, fn, mode='r', buf=-1):

...???????????? self.file = open(fn, mode, buf)

...???? def __str__(self):

...???????????? return str(self.file)

...???? def __repr__(self):

...???????????? return '%s' % self.file

...???? def write(self, line):

...???????????? self.file.write(line.upper())

...???? def __getattr__(self, attr):

...???????????? return getattr(self.file, attr)

...

>>> f = UpperFile(r'C:\test.txt', 'w')

>>> f.write('abcde')

>>> f.close()

>>> f

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

我們包裝了open()函數返回的文件對象。改寫了write()方法。當使用包裝后的類實例化的對象調用write()方法時,使用的是修改的方法;當調用close()方法時,因為我們并沒有改動此方法,則授權給原始文件對象,調用它的close()方法。

9. 新式類的高級特性

(1)工廠函數

在python中,所有的內建轉換函數都是工廠函數。當這些函數被調用時,實際上是對相應的類型實例化。

類型測試:

使用

isinstance(obj, int)

1

也可以使用

isinstance(obj, (int, bool))

1

檢測obj是否是int或者bool類型。

但要注意:盡管 isinstance()很靈活,但它沒有執行“嚴格匹配”比較—-如果 obj 是一個給定類型的實例或其子類的實例,也會返回 True。但如果想進行嚴格匹配,你仍然需要使用 is 操作符:

type(obj) is int

1

(2)__slots__類屬性

__dict__屬性跟蹤所有實例屬性,以字典格式存儲(屬性名為key,屬性值為value)。字典會占據大量內存,如果你有一個屬性數量很少的類,但有很多實例,那么正好是這種情況。為內存上的考慮,用戶現在可以使用__slots__屬性來替代__dict__。

__slots__是一個類變量,由一序列型對象組成,由所有合法標識構成的實例屬性的集合來表示。它可以是一個列表,元組或可迭代對象。也可以是標識實例能擁有的唯一的屬性的簡單字符串。任何試圖創建一個其名不在__slots__中的名字的實例屬性都將導致 AttributeError 異常

Code Example :

>>> class SlottedClass(object):

...???? __slots__ = ('foo', 'bar')

...

>>> c = SlottedClass()

>>> c.foo = 12

>>> c.xx = 12

Traceback (most recent call last):

File "", line 1, in

AttributeError: 'SlottedClass' object has no attribute 'xx'

1

2

3

4

5

6

7

8

9

這種特性的主要目的是節約內存。其副作用是某種類型的”安全”,它能防止用戶隨心所欲的動態增加實例屬性。帶__slots__屬性的類定義不會存在__dict__了。

(3)特殊方法__getattribute__()

請注意,這個方法不是上面我們在授權中提到的__getattr__()方法。

當有屬性被訪問時,不管這個屬性會不會被找到,__getattribute__()函數都會被調用。

如果類同時定義了__getattribute__()及__getattr__()方法,除非明確從__getattribute__()調用,或__getattribute__()引發了 AttributeError 異常,否則后者不會被調用。

如果你將要在__getattribute__()中訪問這個類或其祖先類的屬性,請務必小心。因為其實你是在__getattribute__()中調用__getattribute__(),你將會進入無窮遞歸。

(4)描述符

關于描述符,請移步這里:Python描述符

(5)元類:Metaclasses 和__metaclass__

在python中,類其實也是對象,它由元類創建。典型的應用場景是:ORM。這里不詳細展開,你可以參見:

深刻理解Python中的元類(metaclass)

二,

Python中至少有三種比較常見的方法類型,即實例方法,類方法、靜態方法。它們是如何定義的呢?如何調用的呢?它們又有何區別和作用呢?且看下文。

首先,這三種方法都定義在類中。下面我先簡單說一下怎么定義和調用的。(PS:實例對象的權限最大。)

實例方法

定義:第一個參數必須是實例對象,該參數名一般約定為“self”,通過它來傳遞實例的屬性和方法(也可以傳類的屬性和方法);

調用:只能由實例對象調用。

類方法

定義:使用裝飾器@classmethod。第一個參數必須是當前類對象,該參數名一般約定為“cls”,通過它來傳遞類的屬性和方法(不能傳實例的屬性和方法);

調用:實例對象和類對象都可以調用。

靜態方法

定義:使用裝飾器@staticmethod。參數隨意,沒有“self”和“cls”參數,但是方法體中不能使用類或實例的任何屬性和方法;

調用:實例對象和類對象都可以調用。

實例方法

簡而言之,實例方法就是類的實例能夠使用的方法。這里不做過多解釋。

類方法

使用裝飾器@classmethod。

原則上,類方法是將類本身作為對象進行操作的方法。假設有個方法,且這個方法在邏輯上采用類本身作為對象來調用更合理,那么這個方法就可以定義為類方法。另外,如果需要繼承,也可以定義為類方法。

如下場景:

假設我有一個學生類和一個班級類,想要實現的功能為:

執行班級人數增加的操作、獲得班級的總人數;

學生類繼承自班級類,每實例化一個學生,班級人數都能增加;

最后,我想定義一些學生,獲得班級中的總人數。

思考:這個問題用類方法做比較合適,為什么?因為我實例化的是學生,但是如果我從學生這一個實例中獲得班級總人數,在邏輯上顯然是不合理的。同時,如果想要獲得班級總人數,如果生成一個班級的實例也是沒有必要的。

classClassTest(object):

__num =0

@classmethod

defaddNum(cls):

cls.__num += 1@classmethod

defgetNum(cls):

return cls.__num

#這里我用到魔術方法__new__,主要是為了在創建實例的時候調用累加方法。

def __new__(self):

ClassTest.addNum()

return super(ClassTest, self).__new__(self)

classStudent(ClassTest):

def __init__(self):

self.name = ''a =Student()

b =Student()

print(ClassTest.getNum())

靜態方法

使用裝飾器@staticmethod。

靜態方法是類中的函數,不需要實例。靜態方法主要是用來存放邏輯性的代碼,邏輯上屬于類,但是和類本身沒有關系,也就是說在靜態方法中,不會涉及到類中的屬性和方法的操作。可以理解為,靜態方法是個獨立的、單純的函數,它僅僅托管于某個類的名稱空間中,便于使用和維護。

譬如,我想定義一個關于時間操作的類,其中有一個獲取當前時間的函數。

importtime

classTimeTest(object):

def __init__(self, hour, minute, second):

self.hour =hour

self.minute =minute

self.second =second

@staticmethod

defshowTime():

return time.strftime("%H:%M:%S", time.localtime())

print(TimeTest.showTime())

t = TimeTest(2, 10, 10)

nowTime =t.showTime()

print(nowTime)

如上,使用了靜態方法(函數),然而方法體中并沒使用(也不能使用)類或實例的屬性(或方法)。若要獲得當前時間的字符串時,并不一定需要實例化對象,此時對于靜態方法而言,所在類更像是一種名稱空間。

其實,我們也可以在類外面寫一個同樣的函數來做這些事,但是這樣做就打亂了邏輯關系,也會導致以后代碼維護困難。

以上就是我對Python的實例方法,類方法和靜態方法之間的區別和作用的簡要闡述。

總結

以上是生活随笔為你收集整理的深入理解python面向对象_转:Python3 面向对象,较为深入的两个理解的全部內容,希望文章能夠幫你解決所遇到的問題。

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