面向對象
https://www.runoob.com/python3/python3-class.html
概念定義不再贅述,下面是自己練習的一些代碼
類的簡介
# 創建一個類
# 語法:class類名(父類):
# 代碼塊
class MyClass():
pass
print(MyClass()) # 打印對象名稱和內存地址
mc = MyClass() # 用McClass這個類創建了一個對象mc,mc即爲McClass這個類的實例化
print(mc, type(mc))
<__main__.MyClass object at 0x000002019756C390>
<__main__.MyClass object at 0x000002019756C390> <class '__main__.MyClass'>
對象的創建流程
# 類?
# 類本質上也是一個對象,是一個可以用來創建對象的對象
print(id(MyClass()), type(MyClass()))
# 類是一個type類型的對象
# 現在通過MyClass創建的類都是空類
# 可以像對象中添加變量,對象中的變量稱之爲屬性
# 語法:對象.屬性名 = 屬性值
mc.name = '小明'
print(mc.name)
2177074493760 <class '__main__.MyClass'>
小明
對象的定義
類和對象即對現實生活內容或程序內容的抽象
所有事物由兩部分構成
1.數據(屬性)
2.行爲(方法)
重新定義一個人
# 定義一個人的類
class Person():
# 在類的代碼中,可以定義變量和函數
# 在類中的變量,將會成爲所有實例的公共屬性
# 所有實例都可以訪問這些變量
name = '小明'
# 在類中定義的函數,稱之爲方法,可以通過類的實例訪問
# 如果是函數,有幾個形參傳幾個實參
# 如果是方法,默認傳遞一個參數,所以類中的方法至少要定義一個形參 self
def speak(self):
print('hello')
p1 = Person()
p1.speak()
hello
屬性和方法
屬性和方法的查找流程
當調用一個屬性和方法的時候,解析器會在當前的對象中尋找是否有該屬性值或方法
如果有則返回當前對象的屬性值或方法內容,如果沒有,則取當前對象的類對象中尋找,如果有則返回對象中的屬性值,如果沒有則報錯
類對象和實例對象都可以保存屬性和方法
如果這個屬性(方法)是所有類共享的,則應該保存到類對象當中
如果這個屬性(方法)是某個實例獨有的,則應該保存在實例對象中
self
# 對self的理解
class Hero():
def talk(self):
print(self)
h = Hero()
h.talk()
print(h)
# 從這個例子可以看出,實力化的對象和在方法中的self其實本質上是一樣的,self作爲一個類的規範寫法,本身就可以理解成爲自己,自身的意思,在方法中即表達實例化的自身在類中的應用
<__main__.Hero object at 0x000002206DAB7940>
<__main__.Hero object at 0x000002206DAB7940>
手動添加實例對象——細思極恐
class Hero():
name = ''
def talk(self):
print('我是%s'%self.name)
h1 = Hero()
h2 = Hero()
h1.name = '鋼鐵俠'
h2.name = '美國隊長'
h1.talk()
h2.talk()
# 對於Person這個類的name屬性是必須的,且每個實例對象的name屬性不一樣
# 手動添加name屬性,易遺忘或者出錯
我是鋼鐵俠
我是美國隊長
特殊方法
對象的創建流程
1.創建一個變量
2.在內存中創建一個新的對象
3.__init__方法執行
特殊方法以下劃線開頭,以下劃線結尾
特殊方法不需要手動調用,它會自動調用一次
學習特殊方法要理解
1.特殊方法什麼時候被調用
2.特殊方法的作用是什麼
class Hero():
print('我是類的代碼塊')
def __init__(self, name):
# 通過self向新建的對象初始化屬性
self.name = name
print('init執行了')
def talk(self):
print('我是{}'.format(self.name))
he = Hero('GQ')
he.talk()
我是類的代碼塊 # 類代碼塊先執行
init執行了 # __init__第二執行
我是GQ
類的基本結構
# 由上述代碼可以得出類的基本結構
class 類名([父類]):
公共屬性
# 對象的初始化方法
def __init__(self, ...)
...
# 其他方法
def method(self, ...)
...
練習
# 首先定義一個車的類
class Car():
# 車的基本屬性:名稱,顏色
def __init__(self, name, color):
self.name = name
self.color = color
# 車的基本功能
def run(self):
print('{}開始跑了'.format(self.name))
def laba(self):
print('{}嘀嘀嘀'.format(self.name))
c = Car('大奔', '黃色')
c.laba()
封裝
封裝:面向對象三大特性之一。
指隱藏對象中不希望被外部訪問到的屬性或方法
將對象的屬性名稱修改爲一個外部不知道的的名字
- 下邊代碼提供了一個getter和setter方法可以訪問和修改屬性
class Dog():
def __init__(self, name, age):
self.hidden_name = name
self.hidden_age = age
def speak(self):
print('你們好,我是{}'.format(self.hidden_name))
def get_name(self):
# 用get_name()獲取對象的屬性值
return self.hidden_name
def set_name(self, name):
self.hidden_name = name
def get_age(self):
return self.hidden_age
def set_age(self, age):
if age > 0: # 這裏追加一個判斷,驗證數據的正確性,合法性
self.hidden_age = age
dog = Dog('二哈',5) # 這裏實例化一個類名稱取爲"二哈"
dog.hidden_name = '凱撒' # 但是我們自己修改了屬性值
print(dog.hidden_name)
凱撒
dog.set_name('鬆獅')
print(dog.get_name())
鬆獅
dog.speak()
你們好,我是鬆獅
dog.set_age(-10)
print(dog.get_age())
5
-
使用封裝確實增長了類定義的複雜度,但是確保了數據的安全性
-
1.隱藏了屬性名,使調用者無法隨意修改對象中的屬性
-
2.增加了getter和setter方法,可以很好的控制屬性是否是隻讀的
- 如果希望屬性是隻讀的,則可以直接去掉setter方法
- 如果希望屬性是不能被外界訪問的,則可以直接去掉getter方法
-
3.使用setter方法設置屬性,可以增加數據的驗證,確保數據的值是正確的
-
4.可以再讀取屬性和設置屬性的時候做一些其他的操作
-
封裝(2)
私有屬性:給對象的屬性使用雙下劃線開頭,__屬性名
雙下劃線開頭的屬性是對象的隱藏屬性,隱藏屬性只能在類的內部訪問,無法通過外部訪問
隱藏屬性是python自動爲屬性改了一個名字
一般情況下,私有屬性不需要做修改等操作
class Person():
def __init__(self, name):
self.__name = name
def get_name(self):
return self.__name
def set_name(self, name):
self.__name = name
p = Person('大明')
# print(p.__name)
print(p._Person__name) # 設置成私有變量實際訪問名稱更改爲:_類名__屬性名
p.__name = '小明'
p._Person__name = '小明'
print(p.get_name())
@property
簡單來講,裝飾器的功能就是把一個方法變成屬性調用
添加property裝飾器以後,我們就可以像調用一個屬性一樣調用一個方法
class Person():
def __init__(self, name):
self.__name = name
@property
def get_name(self):
return self.__name
p = Person('大明')
print(p.get_name)
繼承
主要是爲了提高代碼的複用性,讓類與類之間產生關係,有了關係,纔有了多態
如果每次都要創建一個新的類,會顯得很麻煩,在一個類中有的通用功能,在其他類中也可以使用的時候,我們可以用繼承的方式,簡化代碼,接下來寫一個最簡單的繼承的例
# 下面定義一個動物類
class Animal:
def run(self):
print('動物跑。。。')
def sleep(self):
print('動物睡覺。。。')
class Dog(Animal): # 這裏直接繼承Animal
pass
d = Dog()
d.run() # 動物跑。。。
d.sleep() # 動物睡覺。。。
print(isinstance(d, Dog)) # True
print(isinstance(d, Animal)) # True
方法重寫
若子類中有和父類重名的方法,通過子類的實例去調用方法時,會調用子類的方法而不是父類的方法
當我們調用一個對象的時候,會優先尋找當前對象是否有該方法,如果有則直接調用,如果沒有,則去父類中去尋找,以此類推
class A(object):
def text(self):
print('A...')
class B(A):
def text(self):
print('B...')
class C(B):
def text(self):
print('C...')
c = C()
c.text()
C...
super()
父類中所有的方法都會被子類繼承,包括特殊方法
class Animal:
def __init__(self, name):
self._name = name
def run(self):
print('動物跑。。。')
def sleep(self):
print('動物睡覺。。。')
@property
def name(self):
return self._name
@name.setter
def name(self, name):
self._name
# 這裏我想直接調用父類的__init__來初始化父類中的屬性
# super()可以用來獲取當前類的父類
# 並且通過super()返回的對象,調用方法時不需要傳遞self
class Dog(Animal):
def __init__(self, name, age):
# 這裏我直接調用父類的__init__
# Animal.__init__(self,name) # 固定格式,只能獲取該名稱的父類
super().__init__(name) # 動態獲取父類,不管名稱是什麼,且不用傳遞self
self._age = age
def run(self):
print('狗跑。。。')
@property
def age(self):
return self._age
@age.setter
def age(self, age):
self._age = age
d = Dog('二哈', 10)
d._name = '獅子'
print(d.name)
獅子
print(d.age)
10
繼承 — (儘量避免使用多重繼承)
語法:類名.__bases__ 可以用來獲取當前類的所有父類
class A(object):
def text(self):
print('A...')
class B(A):
def text(self):
print('B...')
# 這裏需要按順序繼承父類,當然,如果B沒有繼承A,AB是平級關係的話,則不需要按順序寫
class C(B, A):
pass
print(C.__bases__)
(<class '__main__.B'>, <class '__main__.A'>)
c = C()
c.text()
B...
# 在父類子類中,若出現同名方法,會在子類本身中進行查找,若果沒有,則逐級向父類查找
多態
class A:
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
@name.setter
def name(self, name):
self._name = name
class B:
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
@name.setter
def name(self, name):
self._name = name
# 這裏我再定義一個普通的函數
def speak(parameter):
print('你真可愛 {}'.format(parameter.name)) # 參數具備有name屬性就可以被執行
a = A('二哈')
b = B('薩摩')
speak(a) # 這裏我把A這個類的實例化對象a當作參數傳入speak這個函數中,當然也可以替換成B的實例化對象
# 如果上邊的例子不清楚,下邊以len()舉例
lis = [1, 2, 3]
st = 'python'
print(lis) # 3
print(st) # 6
# lis和st不是同一個對象,但是len()函數可以計算出他們的長度,len()檢查不同對象類型的長度,其實就是多態的體現
# len()這個函數可以獲取長度,是通過__len__這個特殊方法
- 面向對象三大特徵
- 封裝:確保數據安全
- 繼承:對象可擴展
- 多態:程序靈活性
類中的屬性和方法
下邊用兩個例子演示,這裏分開,是爲了互相不影響值的變化
class A(object):
# 類屬性:直接在類中定義的屬性就是類屬性
# 類屬性可以通過類或者類的實例訪問到
count = 0
a = A()
a.count = 5
print('A', A.count) # 打印類屬性
print('a', a.count) # 打印實例的屬性,並不影響類屬性的值
A 0
a 5
class A(object):
# 類屬性只能通過類對象來修改,無法通過實例來修改
count = 0
a = A()
A.count = 10 # 修改類屬性,實例屬性也會隨之改變
print('A', A.count)
print('a', a.count)
A 10
a 10
上述只是爲了對比,雖然會顯得多此一舉(但是我還是想寫,怕看不懂了)
class A(object):
count = 0
def __init__(self):
# 實例屬性:通過實例對象添加的屬性
# 實例屬性只能通過實例屬性來訪問和修改,類對象無法訪問和修改
self.name = '薩摩'
# 實例方法:
# 在類中定義,以self爲第一個參數都是實例方法
# 實例方法調用時,python會將調用對象作爲self傳入
def text(self):
print('我是text')
# 類方法
# cls是類方法的第一個參數,也會被自動傳遞,cls也就是當前的類對象
# 類方法可以通過類去調用,也可以通過實例調用
@classmethod
def text2(cls):
print('我是text2。。。', cls)
print(cls.count)
# 靜態方法
# 靜態方法基本與當前類無關,它只是一個保存到當前類中的函數
# 靜態方法一般都是一些工具類的函數,例如存儲,計算系統時間等等
@staticmethod
def text3():
print('我是text3。。。')
a = A()
a.text()
我是text
A.text(a) # 這裏傳入的a也就是代碼中的self,也就是實例化的對象
我是text
# 類方法的調用
A.text2()
我是text2。。。 <class '__main__.A'>
0
a.text2()
我是text2。。。 <class '__main__.A'>
0
# 靜態方法的調用
A.text3()
我是text3。。。
a.text3()
我是text3。。。
託更許久,目前博客的內容我只是記錄自己的學習筆記,但是也想感謝一下能看到這裏的你,整篇全是代碼,沒有過多的文字說明,需要一點基礎功底,雖顯多餘,學會了,誰還看呢,但是若能對你有所幫助,亦是最好