Python基礎:面向對象(10)

面向對象

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。。。

託更許久,目前博客的內容我只是記錄自己的學習筆記,但是也想感謝一下能看到這裏的你,整篇全是代碼,沒有過多的文字說明,需要一點基礎功底,雖顯多餘,學會了,誰還看呢,但是若能對你有所幫助,亦是最好

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