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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > python >内容正文

python

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

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

一,

1. 類的聲明和創(chuàng)建

對于 Python 函數(shù)來說,聲明與定義類沒什么區(qū)別,因?yàn)樗麄兪峭瑫r進(jìn)行的,定義(類體)緊跟在聲明(含 class 關(guān)鍵字的頭行[header line])和可選(但總是推薦使用)的文檔字符串后面。同時,所有的方法也必須同時被定義。

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

2. 有關(guān)類的屬性

(1)查看類的屬性

要知道一個類有哪些屬性,有兩種方法。最簡單的是使用 dir()內(nèi)建函數(shù)(也可以查看實(shí)例屬性)。另外是通過訪問類的字典屬性__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)是相應(yīng)的屬性對象的數(shù)據(jù)值。

(2)類的特殊屬性

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

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

C.__bases__???? 類C的所有父類構(gòu)成的元組

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

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

C.__class__???? 實(shí)例C對應(yīng)的類(僅新式類中)

1

2

3

4

5

6

3. 對象

(1)Understanding __new__ and __init__

Understanding __new__ and __init__

(2)__del__()方法

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

Python 中的解構(gòu)器是在實(shí)例釋放前提供特殊處理功能的方法,它們通常沒有被實(shí)現(xiàn),因?yàn)閷?shí)例很少被顯式釋放。

要注意,解構(gòu)器只能被調(diào)用一次,一旦引用計數(shù)為 0,則對象就被清除了。

總結(jié):

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

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

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

__del__()未捕獲的異常會被忽略掉 (因?yàn)橐恍┰赺_del__()用到的變量或許已經(jīng)被刪除了)。

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

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

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

(3)跟蹤對象

Python 沒有提供任何內(nèi)部機(jī)制來跟蹤一個類有多少個實(shí)例被創(chuàng)建了,或者記錄這些實(shí)例是些什么東西。如果需要這些功能,你可以顯式加入一些代碼到類定義或者_(dá)_init__()和__del__()中去。最好的方式是使用一個靜態(tài)成員來記錄實(shí)例的個數(shù)。 靠保存它們的引用來跟蹤實(shí)例對象是很危險的,因?yàn)槟惚仨毢侠砉芾磉@些引用,不然,你的引用可能沒辦法釋放(因?yàn)檫€有其它的引用)!

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)類、實(shí)例的其它內(nèi)建函數(shù)

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

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

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

4. 靜態(tài)方法和類方法

有兩種方式聲明靜態(tài)方法和類方法:

使用staticmethod()和 classmethod()內(nèi)建函數(shù)

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

靜態(tài)方法和類方法的區(qū)別:

靜態(tài)方法沒有cls參數(shù),所以它既不能訪問實(shí)例變量,也不能訪問類變量。

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()函數(shù)在子類中調(diào)用.

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__()方法后,如果你想調(diào)用父類的此方法,你必須顯示調(diào)用!Python默認(rèn)不會幫我們做這件事。

(3)多重繼承

方法解釋順序(MRO)

經(jīng)典類采用的是深度優(yōu)先算法(python2.2之前),而新式類采用的是廣度優(yōu)先算法。因?yàn)樵谛率筋愔惺褂蒙疃葍?yōu)先,會出現(xiàn)菱形效應(yīng)。

假設(shè)我們有如下繼承結(jié)構(gòu)的類:

這里寫圖片描述

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

在新式類(右邊繼承結(jié)構(gòu))中采用舊的深度優(yōu)先算法,假設(shè)在D的實(shí)例d中調(diào)用foo()方法,對于此方法的搜索順序是D->B->A->C;采用廣度優(yōu)先算法,搜索順序?yàn)镈->B->C-A。因?yàn)镈繼承了B、C,多數(shù)情況下我們更希望首先搜索的是C而不是A,因?yàn)榧僭O(shè)A、C中都有foo()方法時,你可能覺得A中的foo()方法太過通用了。很典型的就是__init__()方法。

(4)從標(biāo)準(zhǔn)類派生

比如你想從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

我們派生了一個可以自動四舍五入到兩位小數(shù)的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中有很多特殊方法,它們是以__開頭和結(jié)尾的。使用它們可以實(shí)現(xiàn):

模擬標(biāo)準(zhǔn)類型

重載操作符

1

2

3

4

5

6

7. 私有化

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

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

8. 包裝和授權(quán)

(1)包裝(wrapping)

定義:

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

包裝類:

你可以包裝類,但是實(shí)際上沒有必要。因?yàn)槟阃耆梢酝ㄟ^派生實(shí)現(xiàn)相同的效果。

(2)授權(quán)

簡介:

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

實(shí)現(xiàn)授權(quán):

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

我們來實(shí)現(xiàn)一個包裝文件對象的例子:

>>> 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()函數(shù)返回的文件對象。改寫了write()方法。當(dāng)使用包裝后的類實(shí)例化的對象調(diào)用write()方法時,使用的是修改的方法;當(dāng)調(diào)用close()方法時,因?yàn)槲覀儾]有改動此方法,則授權(quán)給原始文件對象,調(diào)用它的close()方法。

9. 新式類的高級特性

(1)工廠函數(shù)

在python中,所有的內(nèi)建轉(zhuǎn)換函數(shù)都是工廠函數(shù)。當(dāng)這些函數(shù)被調(diào)用時,實(shí)際上是對相應(yīng)的類型實(shí)例化。

類型測試:

使用

isinstance(obj, int)

1

也可以使用

isinstance(obj, (int, bool))

1

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

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

type(obj) is int

1

(2)__slots__類屬性

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

__slots__是一個類變量,由一序列型對象組成,由所有合法標(biāo)識構(gòu)成的實(shí)例屬性的集合來表示。它可以是一個列表,元組或可迭代對象。也可以是標(biāo)識實(shí)例能擁有的唯一的屬性的簡單字符串。任何試圖創(chuàng)建一個其名不在__slots__中的名字的實(shí)例屬性都將導(dǎo)致 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

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

(3)特殊方法__getattribute__()

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

當(dāng)有屬性被訪問時,不管這個屬性會不會被找到,__getattribute__()函數(shù)都會被調(diào)用。

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

如果你將要在__getattribute__()中訪問這個類或其祖先類的屬性,請務(wù)必小心。因?yàn)槠鋵?shí)你是在__getattribute__()中調(diào)用__getattribute__(),你將會進(jìn)入無窮遞歸。

(4)描述符

關(guān)于描述符,請移步這里:Python描述符

(5)元類:Metaclasses 和__metaclass__

在python中,類其實(shí)也是對象,它由元類創(chuàng)建。典型的應(yīng)用場景是:ORM。這里不詳細(xì)展開,你可以參見:

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

二,

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

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

實(shí)例方法

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

調(diào)用:只能由實(shí)例對象調(diào)用。

類方法

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

調(diào)用:實(shí)例對象和類對象都可以調(diào)用。

靜態(tài)方法

定義:使用裝飾器@staticmethod。參數(shù)隨意,沒有“self”和“cls”參數(shù),但是方法體中不能使用類或?qū)嵗娜魏螌傩院头椒?#xff1b;

調(diào)用:實(shí)例對象和類對象都可以調(diào)用。

實(shí)例方法

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

類方法

使用裝飾器@classmethod。

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

如下場景:

假設(shè)我有一個學(xué)生類和一個班級類,想要實(shí)現(xiàn)的功能為:

執(zhí)行班級人數(shù)增加的操作、獲得班級的總?cè)藬?shù);

學(xué)生類繼承自班級類,每實(shí)例化一個學(xué)生,班級人數(shù)都能增加;

最后,我想定義一些學(xué)生,獲得班級中的總?cè)藬?shù)。

思考:這個問題用類方法做比較合適,為什么?因?yàn)槲覍?shí)例化的是學(xué)生,但是如果我從學(xué)生這一個實(shí)例中獲得班級總?cè)藬?shù),在邏輯上顯然是不合理的。同時,如果想要獲得班級總?cè)藬?shù),如果生成一個班級的實(shí)例也是沒有必要的。

classClassTest(object):

__num =0

@classmethod

defaddNum(cls):

cls.__num += 1@classmethod

defgetNum(cls):

return cls.__num

#這里我用到魔術(shù)方法__new__,主要是為了在創(chuàng)建實(shí)例的時候調(diào)用累加方法。

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())

靜態(tài)方法

使用裝飾器@staticmethod。

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

譬如,我想定義一個關(guān)于時間操作的類,其中有一個獲取當(dāng)前時間的函數(shù)。

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)

如上,使用了靜態(tài)方法(函數(shù)),然而方法體中并沒使用(也不能使用)類或?qū)嵗膶傩?或方法)。若要獲得當(dāng)前時間的字符串時,并不一定需要實(shí)例化對象,此時對于靜態(tài)方法而言,所在類更像是一種名稱空間。

其實(shí),我們也可以在類外面寫一個同樣的函數(shù)來做這些事,但是這樣做就打亂了邏輯關(guān)系,也會導(dǎo)致以后代碼維護(hù)困難。

以上就是我對Python的實(shí)例方法,類方法和靜態(tài)方法之間的區(qū)別和作用的簡要闡述。

總結(jié)

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

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。