首頁  >  文章  >  後端開發  >  Python類別和物件怎麼應用

Python類別和物件怎麼應用

WBOY
WBOY轉載
2023-05-13 20:01:141328瀏覽

物件= 屬性方法

我們前面其實已經接觸過封裝的概念,把亂七八糟的資料丟進清單裡面,這是一種封裝,是資料層面的封裝;把常用的程式碼片段打包成一個函數,這也是一種封裝,是語句層面的封裝;現在我們要學習的對象,也是一種封裝的思想, 對象的來源是模擬真是世界,將資料和程式碼都封裝在了一起。

打個比方,烏龜就是真實世界的一個對象,通常會從兩個部分來描述它。
(1)從靜態的特徵描述:例如,綠色的,有四條腿,有外殼等等,這是靜態一方面的描述。
(2)從動態的行為描述:例如,它會爬,如果追它,它還會跑,有時還會咬人,睡覺等等,這都是從行為方面進行描述的。

Python中的物件也是如此,一個物件的特徵稱為「屬性」一個物件的行為稱為「方法」。 :

如果將烏龜寫成程式碼,將會是下面這樣:

class Turtle: # Python中的类名约定以大写字母开头
    
    # 特征的描述称为属性,在代码层面看来其实就是变量
    color = 'green'
    legs = 4
    shell = True

    # 方法实际就是函数,通过调用这些函数来完成某些工作
    def climb(self):
        print('向前爬')
    def run(self):
        print('向前跑')
    def bite(self):
        print('咬人')
    def sleep(self):
        print('睡觉')

以上程式碼定義了物件的特徵(屬性)和行為(方法) ,但還不是一個完整的對象,將定義的這些稱為類別(Class)。需要使用類別來建立一個真正的對象,這個物件就叫作這個類別的一個實例(Instance),也叫實例物件(Instance Objects)
舉個例子,這就像工廠需要生產一系列玩具,需要先製作這個玩具的模具,然後再根據這個模具再進行大量生產。

那麼要怎麼創建真正的實例物件呢? 建立一個對象,也叫類別的實例化其實很簡單:

# 首先要有上面那一段类的定义
tt = Turtle()

注意:類別名稱後面跟著小括號,這跟呼叫函數是一樣的。所以在Python中,類別名稱約定用大寫字母開頭,函數用小寫字母開頭,這樣比較容易區分。另外,賦值操作並不是必要的,但如果沒有把創建好的實例物件賦值給一個變量,這個物件就沒辦法使用,因為沒有引用指向這個實例,最終會被Python的垃圾回收機制自動回收。

如果要 呼叫物件裡的方法,使用點操作符(.) 即可。

Python類別和物件怎麼應用


 

接下來我們來看一段程式碼,再深入理解一下類別類別物件實例物件三個概念:

Python類別和物件怎麼應用

從這個例子可以看出,對實例物件c的count屬性進行賦值後,就相當於覆寫了類別物件C的count屬性。如下圖所示,如果沒有賦值覆蓋,那麼引用的是類別物件的count屬性。

Python類別和物件怎麼應用

需要注意的是,類別中定義的屬性是靜態變數,類別的屬性是與類別物件綁定,並不會依賴任何它的實例物件。

另外,如果屬性的名字跟方法名稱相同,屬性會覆寫方法

Python類別和物件怎麼應用

為了避免名字上的衝突,應該遵守一些約定俗成的規矩:
(1)不要試圖在一個類別裡面定義出所有能想到的特性和方法,應該利用繼承和組合機制來擴展。
(2)用不同的詞性命名,如屬性名用名詞、方法名稱用動詞,並使用駝峰命名法等。

self是什麼

細心的讀者發現物件的方法都會有一個self參數,那麼這個self是什麼呢?如果你接觸過C ,那麼你應該很容易對號入座,Python的self其實就相當於C 的this指針

如果你之前沒有接觸過任何程式語言,那麼簡單說,如果把類比作圖紙,那麼由類別實例化後的物件才是真正可以住的房子。根據一張圖紙可以設計出成千上萬的房子,它們外觀都差不多,但是每個房子都有不同的主人。每個人要找到自己的房子,那self就相當於這裡的門牌號碼,有了self,你就可以輕鬆找到自己的房子。

Python的self參數就是同一個道理,由一個類別可以產生無數個對象,當一個物件方法被呼叫的時候,物件會將自身的引用作為第一個參數傳給該方法,那麼Python就知道需要操作哪個物件的方法了

舉個簡單的例子:

#

Python類別和物件怎麼應用

公有和私有

一般面向对象的编程语言都会区分公有和私有的数据类型,像C++和Java它们使用public和private关键字用于声明数据是公有的还是私有的,但在Python中并没有类似的关键字来修饰。

默认上对象的属性和方法都是公开的,可以直接通过点操作符(.)进行访问

Python類別和物件怎麼應用

为了实现类似私有变量的特征,Python内部采用了一种叫name mangling(名字改编)的技术,在Python中定义私有变量只需要在变量名或函数名前加上“_ _”两个下划线,那么这个函数或变量就会成为私有的了

Python類別和物件怎麼應用

这样,在外部将变量名“隐藏”起来了,理论上如果要访问,就要从内部进行

Python類別和物件怎麼應用

但是认真想一下这个技术的名字name mangling(名字改编),那就不难发现其实Python只是把双下横线开头的变量进行了改名而已。实际上,在外部使用“_类名_ _变量名”即可访问双下横线开头的私有变量了

Python類別和物件怎麼應用

说明:Python目前的私有机制其实是伪私有的,Python的类是没有权限控制的,所有的变量都是可以被外部调用的

继承

举个例子来说明继承。例如现在有个游戏,需要对鱼类进行细分,有金鱼(Goldfish)、鲤鱼(Carp)、三文鱼(Salmon)以及鲨鱼(Shark)。那么我们能不能不要每次都从头到尾去重新定义一个新的鱼类呢?因为我们知道大多数鱼的属性和方法是相似的,如果有一种机制可以让这些相似的东西得以自动传递,那么就方便多了。这就是继承。

继承的语法很简单:

c l a s s 类 名 ( 被 继 承 的 类 ) : . . . class 类名(被继承的类): \\ \quad ... class类名(被继承的类):...

被继承的类称为基类、父类或超类;继承者称为子类,一个子类可以继承它的父类的任何属性和方法。

举个例子:

Python類別和物件怎麼應用

需要注意的是,如果子类中定义与父类同名的方法或属性,则会自动覆盖父类对应的方法或属性

Python類別和物件怎麼應用

接下来,尝试写一下开头提到的金鱼(Goldfish)、鲤鱼(Carp)、三文鱼(Salmon)以及鲨鱼(Shark)的例子。

import random as r
class Fish:
    def __init__(self):
        self.x = r.randint(0, 10)
        self.y = r.randint(0, 10)
    def move(self):
        # 这里主要演示类的继承机制,就不考虑检查场景边界和移动方向问题
        # 假设所有的鱼都是一路向西游
        self.x -= 1
        print("我的位置是:", self.x, self.y)
# 金鱼
class Goldfish(Fish):
    pass
# 鲤鱼
class Carp(Fish):
    pass
#三文鱼
class Salmon(Fish):
    pass
# 上面三种鱼都是食物,直接继承Fish类的全部属性和方法
# 下面定义鲨鱼类,除了继承Fish类的属性和方法,还要添加一个吃的方法
class Shark(Fish):
    def __init__(self):
        self.hungry = True
    def eat(self):
        if self.hungry:
            print("吃掉你!")
            self.hungry = False
        else:
            print("太饱了,吃不下了~")

首先运行这段代码,然后进行测试:

Python類別和物件怎麼應用

同样是继承于Fish类,为什么金鱼(goldfish)可以移动,而鲨鱼(shark)一移动就报错呢?
可以看到报错提示为:Shark对象没有x属性,这是因为在Shark类中,重写了_ _init_ _()方法,但新的_ _init_ _()方法里面没有初始化鲨鱼的x坐标和y坐标,因此调用move()方法就会出错。
那么解决这个问题,只要在鲨鱼类中重写_ _init_ _()方法的时候先调用基类Fish的_ _init_ _()方法

下面介绍两种可以实现的技术:

  • (1)调用未绑定的父类方法

  • (2)使用super函数

调用未绑定的父类方法

什么是调用未绑定的父类方法?举个例子:

Python類別和物件怎麼應用

修改之后,再运行下发现鲨鱼也可以成功移动了:

Python類別和物件怎麼應用

这里需要注意的是,这个self并不是父类Fish的实例对象,而是子类Shark的实例对象。所以这里说的未绑定是指并不需要绑定父类的实例对象,使用子类的实例对象代替即可。

使用super函數

super函數能夠幫助我們自動找到基底類別的方法,而且也為我們傳入了self參數,這樣就不需要做這些事情了:

Python類別和物件怎麼應用

運行後得到同樣的結果:

Python類別和物件怎麼應用

##多重繼承

除此之外,Python也支援

多重繼承,就是可以同時繼承多個父類別的屬性與方法

c l a s s 類名( 父類1 , 父類2 , 父類3 , . . . ) : . . . class 類名(父類1,父類2,父類3,...):\\ \quad ... class類別名稱(父類別1,父類別2,父類別3,...):...

# 舉個例子:

Python類別和物件怎麼應用

這就是基本的多重繼承語法,但多重繼承很容易導致程式碼混亂,所以當你不確定是否真的必須使用多重繼承的時候,請盡量避免使用它,因為有些時候會出現不可預見的BUG。

組合

前面學習了繼承的概念,又提到了多重繼承,但如果現在我們有了烏龜類、魚類,現在要求定義一個類,叫水池,水池裡要有烏龜和魚。用多重繼承顯得很奇怪,因為水池和烏龜、魚是不同物種,那要怎麼把它們組合成一個水池的類別呢?

其實在Python中很簡單,直接
把需要的類別放進去實例化就可以了,這就叫組合

Python類別和物件怎麼應用

先執行上段程式碼,然後測試:

Python類別和物件怎麼應用

#建構與析構

Python的物件有許多神奇的方法,如果你的物件實作了這些方法中的某一個,那麼這個方法就會在特殊情況下被Python所調用,而這一切都是

自動發生的。

_ _init_ _(self[, …])建構方法

通常把_ _init_ _()方法稱為建構方法,只要實例化一個對象,

這個方法就會在物件被創建時自動呼叫。實例化物件時是可以傳入參數的,這些參數會自動傳入_ _init_ _()方法中,可以透過重寫這個方法來自訂物件的初始化操作

舉例:

Python類別和物件怎麼應用

有些讀者可能會問,有些時候在類別定義時寫_ _init_ _()方法,有時候卻沒有,這是為什麼?看下面這個例子:

Python類別和物件怎麼應用

這裡要注意的是,

_ _init_ _()方法的回傳值一定是None,不能是其他

Python類別和物件怎麼應用

所以,

一般在需要進行初始化的時候才重寫_ _init_ _()方法。所以這個_ _init_ _()方法並不是實例化物件時第一個被呼叫的方法

_ _new_ _(cls[, …])方法

#_ _new_ _()方法才是

一個物件實例化的時候所呼叫的第一個方法。與其他方法不同的是,它的第一個參數不是self而是這個類別(cls),而其他的參數會直接傳遞給_ _init_ _()方法的。

_ _new_ _()方法需要

回傳一個實例物件,通常是cls這個類別實例化的對象,當然你也可以回傳其他物件。

_ _new_ _()方法平常很少去重寫它,一般讓Python用預設的方案執行即可。但是

有一種情況要重寫這個方法,就是當繼承一個不可變的型別的時候,它的特性就顯得特別重要了。

Python類別和物件怎麼應用

_ _del_ _(self)析構方法

如果說_ _init_ _()和_ _new_ _()方法是物件的建構器的話,那麼Python也提供了一個析構器,叫作_ _del_ _()方法。

當物件將要被銷毀的時候,這個方法就會被呼叫。但是要注意的是,並非 del x 就相當於自動呼叫 x._ _del_ _(),_ _del_ _()方法是當垃圾回收機制回收這個物件的時候呼叫的。 舉個例子:

Python類別和物件怎麼應用

什麼是綁定

前面提到過綁定的概念,那到底什麼是綁定呢? Python中嚴格要求了方法需要有實例才能被調用,這種限制其實就是Python所謂的綁定概念。

有人可能會這麼嘗試,而且發現也可以呼叫:

Python類別和物件怎麼應用

但是,這樣做會有一個問題,就是根據類別實例化後的物件根本無法呼叫裡面的函數

Python類別和物件怎麼應用

其實是由於Python的綁定機制,這裡自動把bb物件當作第一個參數傳入,所以才會出現TypeError。

再看一個例子:

Python類別和物件怎麼應用

#_ _dict_ _屬性是由一個字典組成,字典中只有實例物件的屬性,不顯示類別屬性和特殊屬性,鍵表示的是屬性名稱值表示屬性對應的資料值

Python類別和物件怎麼應用

現在實例物件dd有了兩個新屬性,而且這兩個屬性是僅屬於實例物件的:

Python類別和物件怎麼應用

為什麼會這樣?其實這完全歸功於self參數:當實例物件dd去呼叫setXY方法的時候,它傳入的第一個參數就是dd,那麼self.x = 4, self.y = 5也就相當於dd.x = 4, dd.y = 5,所以在實例對象,甚至類別對像中都看不到x和y,是因為這兩個屬性是只屬於實例對象dd的

如果把類別實例刪掉,實例物件dd還能否呼叫printXY方法?答案是可以的:

Python類別和物件怎麼應用

以上是Python類別和物件怎麼應用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除