06-Python類與對象

什麼是類

百度百科:

類是對象的抽象,對象是對客觀事物的抽象。

用通俗的話來說:

類是類別的意思,是數據類型。
對象是類別下的具體事物。

也就是說:

類是數據類型,對象是變量。

比如:

自定義一種數據類型:水果。那水果就是類。
由此數據類型創建一個變量:一個蘋果。那這個蘋果就是對象。
類是抽象的,對象是具體的。

類和對象的關係

類是一種數據類型,相當於一個模板,用於創建對象。
每一個對象都有自己獨立的內存空間,用於存儲數據成員。

比如:

人都有名字,年齡,性別等信息。這是一個類,是抽象的。
小明叫張明,18歲,男。這是一個對象,是具體的。

專業術語

封裝了變量和函數的一種自定義的數據類型

  • 數據成員

類中的變量

  • 方法

類中的函數

  • 對象

由類創建的變量稱作爲對象

  • 實例化

創建一個對象。
因爲類是抽象的,而對象是具體的,是一個實例,所以通過類創建對象的過程稱爲實例化。

  • 繼承

根據父類創建子類,子類 繼承 父類中所有變量和方法

數據成員

示例:

class Student:      #定義類
    pass
s=Student()         #創建對象(類的實例化)
s.i=12345           #創建或修改類的數據成員
print(s.i)          #訪問類的數據成員

結果:

12345

解釋:

使用關鍵字class定義一個類
類名爲Student,通常類名首字母大寫。
對象名爲s
i 爲數據成員
訪問類中成員的語法:對象名.成員名

每個對象都有單獨的內存空間去存儲自己的普通數據成員(後面會講到靜態數據成員,就不一樣了)

方法

類中的函數稱爲方法
方法可以訪問此類所有的數據成員和方法。

示例:

class Student:
    def f(self):    #定義方法
        print("hello world")
s=Student()         #創建對象(類的實例化)
s.f()               #調用類的方法

結果:

hello world

解釋:

f 爲方法名
訪問類中成員的語法:對象名.成員名
類的方法與普通的函數區別:第一個參數爲self。
self實際上是對象的地址。所以通過self訪問的成員都屬於當前對象。

帶有參數的方法:

class Student:
    def f(self,s):    #定義方法
        	print(s)
s=Student()
s.f("hello world")    #調用類的方法 (輸出:hello world)

解釋:

字符串hello world將傳給s

self

示例:

class Student:
    def f(self):
        print(self.i)   #訪問類中的屬性
s=Student()   
s.i=12345
s.f()

結果:

12345

解釋:

類的方法與普通的函數區別:第一個參數爲self。
self實際上是對象的地址。
每一個對象的self地址都不一樣,它們都指向本身。
通過self可以訪問自身的數據成員和方法。

訪問數據成員和方法必須使用self,否則就是臨時變量,而不是數據成員

示例:

class Student:
    def f(self):
        i=200       #臨時變量
        	print(self.i)   #數據成員
        	print(i)
s=Student()
s.i=100           #創建類的普通數據成員
s.f()

結果:

100
200

解釋:

通過結果可以看出,兩個i是不同的變量
通過self訪問的是對象內部的內存空間,是當前對象的成員。

構造方法

構造方法:創建對象時自動調用此方法

class Student:
    def __init__(self):
        print("對象被創建")
s=Student()

結果:

對象被創建


數據成員

普通數據成員

普通數據成員存儲在對象自己的內存空間中。
所以每個對象的普通數據成員都是獨立的,因爲它們的內存空間不一樣。

示例:

class Student:
    def f(self):
        print(self.i)       #在類中訪問普通數據成員
s=Student()
s.i=100           #創建類的普通數據成員
s.i=200           #修改類的普通數據成員
print(s.i)        #訪問類的普通數據成員
s.f()

解釋:

當數據成員不存在時, s.i=100將創建此數據成員

當數據成員存在時,s.i=100將修改此數據成員

self是對象的地址,用於訪問對象自己的屬性,包括數據成員和方法

一般在構造函數中創建普通數據成員

在構造函數中創建普通數據成員:

class Student:
    def __init(self):
        self.a=1        #定義普通數據成員
        self.b=2        #定義普通數據成員

解釋:

當創建對象時會自動調用構造方法,所以數據成員也被自動創建。
若不使用構造方法,需要在手動的、依次給每一個對象創建這些數據成員。(很麻煩)

靜態數據成員

靜態數據成員存儲在類中,是所有對象共享的。
通過類名訪問靜態成員,而不是對象名。

示例:

class Student:
    name="張三"
print(Student.name)     #訪問靜態數據成員
Student.name="李四"     #修改靜態數據成員

解釋:

靜態數據成員在類中直接定義
通過類名訪問靜態數據成員,因爲是共享的
通過對象名訪問普通數據成員,因爲每個對象之間是獨立的

注意:也可以通過對象名訪問靜態數據成員,但不建議這樣

class Student:
    name="張三"
s1=Student()
s2=Student()

print("----創建之前:----")
print("Student.name :",Student.name)
print("s1.name :",s1.name)

print("----創建之後:----")
s1.name="李四"
print("Student.name :",Student.name)
print("s1.name :",s1.name)

結果:

----創建之前:----
Student.name : 張三
s1.name : 張三
----創建之後:----
Student.name : 張三
s1.name : 李四

解釋:

可以看到,當沒有創建普通數據成員時:s1.name表現爲靜態數據成員

當創建普通數據成員後:
s1.name表現爲普通數據成員

得出結論:不建議用對象名去修改靜態數據成員

私有數據成員

私有數據成員只能被當前類所訪問,外部和子類中都無法訪問。
這樣做的是爲了數據和代碼安全。

示例:

class Student:
    __name="張三"    #創建類的靜態私有數據成員
    def __init__(self):
        self.__age=18   #創建私有數據成員
s=Student()

解釋:

若在類的外部訪問類的私有成員,則會報錯:AttributeError。
私有數據成員名:使用雙下劃線開頭。
靜態私有數據成員:私有的,所有對象共享的通過類名訪問

外部和子類中都無法訪問私有成員,那如何訪問它們呢?藉助類中非私有方法作爲橋樑

class Student:
    def __init__(self):
        self.__name="張三"
    def getName(self):          #通過此方法訪問靜態成員__name
        return self.__name
    def setName(self,name):     #通過此方法修改靜態成員__name
        self.__name=name
s=Student()
s.setName("李四")
print(s.getName())      #輸出:李四

解釋:

無法直接訪問私有數據成員
類的內部可以訪問私有數據成員。如非私有方法。
得出結論:通過非私有方法間接訪問私有數據成員。

特殊的數據成員

這些特殊的數據成員已經存在,無需自己創建
如__dict__將會返回類中所有的數據成員,以字典返回

class Student:
    def __init__(self):
        self.a=1
        self.b=2
s=Student()
print(s.__dict__)       #輸出{'a': 1, 'b': 2}


方法

普通方法

class Student:
    def show(self,s):
        print("我是"+s)
s=Student()
s.show("學生")       #輸出:我是學生

私有方法

在類的外部和子類中無法訪問私有方法
私有方法僅供內部使用
私有方法名用雙下劃線開頭

class Student:
    def __show(self):  #外部無法直接訪問
        	print("Private")
s=Student()
s.__show()      #外部無法訪問私有方法,報錯!!!!

靜態方法

靜態方法是共享的,屬於類,而不是某個對象
靜態方法只能訪問靜態數據成員

class Student:
    @classmethod
    def show(cls,s):
        print(cls,s)  #輸出類名
Student.show("張三")       #輸出:<class '__main__.Student'> 張三

解釋:

方法前的@classmethod表示:此方法爲靜態方法

與普通方法類似,有一個額外的參數cls,cls是class的縮寫,代表類的地址。

因爲靜態方法是共享的,所以通過類名調用靜態方法。

構造方法

創建對象時Python自動調用構造方法

通常在構造方法中做對象的初始化工作,如創建數據成員。

構造方法名固定爲__init__。

class Student:
    def __init__(self):
        self.name=""
        self.age=0
s=Student()

析構方法

回收對象時Python自動調用析構方法
通常在析構方法中做對象的收尾工作,如關閉打開的文件。

class Student:
    def __del__(self):
        print("對象已被刪除")
s=Student()
對象已被刪除

解釋:

析構方法名固定爲__del__。

程序運行完畢時會回收所有變量和內存空間,所以會刪除對象s。

除此之外,代碼塊運行完畢也會回收臨時變量,如函數中的臨時變量。

__call__方法

__call__方法:對象名可以當作方法名使用,調用__call__方法的函數體和形參

class Student:
    def __call__(self):
        print("調用__call__")
s=Student()
s()     #輸出:調用__call__

解釋:

可以看到__call__方法的調用方式不是s.__call__(),而是通過對象名調用s()

強制轉換

通過定義強制轉換的方法,可以將對象轉換成任意數據類型。

class Student:
    def __int__(self):
        return 18
s=Student()
a=int(s)        #a的值爲18

解釋:

若要轉換爲int,需要定義__int__方法
強制轉換的語法爲int(obj)

若要轉換爲其他類型,如float,需要定義__float__方法。

應用-得到學生的成績
class Student:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __int__(self):
        return self.age
s=Student("張三",20)
a=int(s)        #得到學生s的年齡
應用-得到學生的信息
class Student:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __str__(self):
        return "我是一名學生,姓名:%s,年齡:%d"%(self.name,self.age)
s=Student("張三",20)
print(s)    #直接輸出,就會調用str(obj)

結果:

我是一名學生,姓名:張三,年齡:20

解釋:

類似的還有float等數據類型,也都可以做強制轉換
若要轉換爲其他類型,如float,需要定義__float__方法。

運算符重載

對象之間可以進行加減乘除等操作,如:

s1=Student()
s2=Student()
s3=s1-s2

那s3的值到底是多少?到底是s1和s2身高差?還是平均分差?還是其他屬性的差值?
這需要我們自己來定義,進行運算符的重載。

示例-重載符號+:

class Student:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __add__(self,other):        #運算符重載
        	return self.age + other.age
s1=Student("張三",20)
s2=Student("李四",18)
print(s1+s2)    #輸出38 (20+18=38)

解釋:

重載符號+的方法名固定爲:add
參數固定爲self和other,分別表示左操作數和右操作數。
通過return得到結果。

常用的符號與方法名

符號 方法名 描述
+ __add__
- __sub__
* __mul__
/ __truediv__
// __floordiv__ 整除
% __mod__ 取模
** __pow__
下標運算符重載

通過下標運算符,可通過obj[n]這種形式訪問對象中的數據

示例-通過下標查詢:

class Student:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __getitem__(self, item):  # 使用obj[n]時調用
        	if type(item) == slice:     #切片
          	     print(item.start)  #開始下標
         	     print(item.stop)   #結束下標
          	     print(item.step)   #步長
     	return item
s1=Student("張三",20)
print(s1[1:8:7])

結果:

1
8
7
slice(1, 8, 7)

示例-通過下標修改:

class Student:
    def __init__(self):
        self.score={"語文":98,"數學":96}
    def __setitem__(self, key, value):
        self.score[key]=value
s1=Student()
s1["語文"]=100
print(s1.score)

結果:

{'語文': 100, '數學': 96}

示例-通過下標刪除:

class Student:
    def __init__(self):
        self.score={"語文":98,"數學":96}
    def __delitem__(self, key):
        del self.score[key]
s1=Student()
del s1["語文"]
print(s1.score)

結果:

{'數學': 96}


屬性

通過定義屬性,可以把函數變成數據成員

示例:

class Student:
    @property
    def name(self):           #調用方式print(obj.name)
        return "張三"
    @name.setter
    def name(self, value):    #調用方式obj.name="Fei"
        print(value)
    @name.deleter
    def name(self):           #調用方式del obj.name
        print("del name")
s=Student()
print(s.name)       #輸出:張三
s.name="李四"       #輸出:李四
del s.name          #輸出:del name

解釋:

使用@property將函數變成數據成員
name.setter:name()的set函數
name.deleter:name()的del函數
屬性就像數據成員一樣使用,實際上是函數



繼承

當我們想爲某個類添加新的方法時,無需重新定義所有方法,使用繼承即可。
例如:普通學生可以得到成績信息,藝術生還能得到藝術成績。

好處:

代碼重用,方便維護。
通過繼承,可以得到所有父類的方法,不必再複製一份代碼。

基類&派生類

已存在的類稱爲基類(父類),新建的類稱爲派生類(子類)。

注意點:

子類不能直接訪問父類的私有成員

示例:

class A:
    def show(self):
        print("我是函數show()")
class B(A):           #繼承了A
    pass
b=B()
b.show()        #因爲B繼承了A,所以b擁有函數show()

解釋:

C繼承了A和B,所以擁有A和B的所有方法
子類不能直接調用父類的私有成員

多重繼承

子類可以再被繼承。
如:A被B繼承,B又被C繼承。

class A:
    pass
class B(A):             #繼承了A
    pass
class C(B):             #繼承了B
    pass
c=C()

函數重寫

繼承父類的函數之後,可以設置同名函數,這是新函數會把舊函數覆蓋。

class A:
    def show(self):
        print("我是A")
class B(A):   
    def show(self):         #重寫了show函數
        print("我是B")
b=B()
b.show()    #輸出:我是B

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章