天天看點

Python之面向對象(封裝,繼承,多态)封裝:繼承:多态:

Python之面向對象(封裝,繼承,多态)封裝:繼承:多态:

目錄

封裝:

特點:

私有化:

Python中的dir()函數:

舉例說明:

@property裝飾器:

格式:

繼承:

has a:

is a:

特點:

對于super關鍵詞的用法介紹:

使用格式:

多繼承(了解):

多态:

封裝:

特點:

1.可了解為私有化屬性

2.在類中定義公有set方法和get方法,通過調用這兩個方法來操作私有化屬性

3.被私有化的屬性不能被繼承

在Python中,封裝的概念可了解為私有化。那麼我們為什麼要提出私有化呢,其實将屬性私有化可以提升它的安全級别,通路僅限于類中,不能随意被外界修改。下面簡單介紹一下私有化的概念。

私有化:

它的好處:

1.隐藏屬性不能随意被外界修改

2.當需要修改私有化時,可通過函數進行修改

        def setXXX(self,xxx):   #通過set對私有化屬性進行重新指派

                if xxx符合條件:

                        self.__xxx = xxx

                else:

                        不指派

3.當需要擷取私有化時,也通過函數擷取

        def getXXX(self):

                return self.__xxx

Python中的dir()函數:

dir()函數不帶參數時,以清單形式傳回目前範圍内的變量,方法和定義的類型;

帶參數時,以清單形式傳回參數的屬性,方法。

同 __dir__() 的用法相同

舉例說明:

class Student:
    __age = 18  # 私有化的類屬性

    def __init__(self, name, age):
        self.__name = name
        self.__age = age
        self.__score = 60  # 外部無法通路,無法修改

    # 定義公有set和get方法

    # set是為了指派
    def setAge(self, age):  # 可以改變私有化屬性的值
        if age > 0 and age <= 100:
            self.__age = age
        else:
            print('年齡不符合規定值!')

    # get是為了取值
    def getAge(self):
        return self.__age

    def __str__(self):
        return '姓名:{},年齡:{},分數:{}'.format(self.__name, self.__age, self.__score)


one = Student('JSY', 20)
print(one)

one.age = 21
one.name = 99
print(one)

one.setAge(28)
print(one)

# dir(對象名/類名) 或者  (對象名/類名).__dir__()
print(dir(one))
print(dir(Student))
print(one._Student__age)  # 其實它就是__age,隻是系統自動改名了,由此可見,私有化也是僞私有的
print(Student._Student__age)  # 通過該方法通路到了類的私有化屬性
print(one.__dir__())

print(__name__)  # 表示目前程式運作在哪一個子產品中
           

輸出:

姓名:JSY,年齡:20,分數:60
姓名:JSY,年齡:20,分數:60
姓名:JSY,年齡:28,分數:60
['_Student__age', '_Student__name', '_Student__score', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'getAge', 'name', 'setAge']
['_Student__age', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'getAge', 'setAge']
28
18
['_Student__name', '_Student__age', '_Student__score', 'age', 'name', '__module__', '__init__', 'setAge', 'getAge', '__str__', '__dict__', '__weakref__', '__doc__', '__repr__', '__hash__', '__getattribute__', '__setattr__', '__delattr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__new__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__']
__main__
           

解讀:第一行輸出為建立對象,而分數的屬性已經被固定,無需傳入參數;

           第二行輸出,是想要直接修改類中的私有化屬性,修改失敗,故屬性無變化

           第三行輸出,采用(set和get)函數的方式修改私有化屬性,修改成功

           接下兩行輸出,為對象和類中的屬性及方法輸出

           接下兩行輸出,為調用系統改名後的私有化屬性,故私有化本身是僞私有的

           倒數第二行輸出,為使用__dir__()的方法擷取對象的屬性及方法

           最後一行輸出,__name__方法可傳回目前程式在哪一個子產品中運作

@property裝飾器:

開發中會使用這種方式進行私有化的處理,代替set和get,來擷取和修改屬性。

格式:

@property

def func1(self):                #先定義擷取私有屬性

        return self.__xxx      #傳回私有屬性值

@func1.setter                 

def func2(self,xxx):           #再定義修改私有屬性

        self.__xxx=xxx

###代碼示例###

class Student:
    __age = 18  # 私有化的類屬性

    def __init__(self, name, age):
        self.__name = name
        self.__age = age
        self.__score = 60  # 外部無法通路,無法修改

    #先有getxxx
    @property
    def acquire_age(self):
        return self.__age
    #再有setxxx,因為get依賴set
    @acquire_age.setter
    def assignment_age(self, age):
        if age > 0 and age < 100:
            self.__age = age
        else:
            print('年齡不符合規定值 !')

    def __str__(self):
        return '姓名:{},年齡:{},分數:{}'.format(self.__name, self.__age, self.__score)


s = Student('Tom', 99)

s.assignment_age = 98  #給__age指派
print(s.acquire_age)     #擷取__age的值
           

輸出:

98
           

此時,可直接通過函數名assignment_age來給私有屬性__age賦一個新的值

           再通過函數acquire_age,傳回私有屬性__age的值

繼承:

在講繼承之前我們先看一個例子:

import random

# 聲明(Road)
class Road:
    def __init__(self, name, len):
        self.name = name
        self.len = len


# 聲明(Car)
class Car:
    def __init__(self, brand, speed):
        self.brand = brand
        self.speed = speed

    def get_time(self, road):  # 需注意:road與r指向同一位址空間
        ran_time = random.randint(1, 10)
        message = '{}品牌的車在{}上以{}速度行駛{}小時'.format(self.brand, road.name, self.speed, ran_time)
        print(message)

    def __str__(self):
        return '{}品牌,速度:{}'.format(self.brand, self.speed)


# 建立執行個體化對象
r = Road('魏州大道', 6666)

c = Car('凱迪拉克', 66)
print(c)

c.get_time(r)
           

輸出:

凱迪拉克品牌,速度:66
凱迪拉克品牌的車在魏州大道上以66速度行駛6小時
           

代碼注意:在Car類裡的get_time方法,其參數可以是一個對象類型,且該對象可以是其他類的,                    故在調用該方法的時候,可以傳入Road的對象,借此來調用Road裡的屬性或是方法。

                  該例其實是一個 has a 的包含關系

在這個例子中我們看到了類與類直接的聯系,而繼承則是面向對象中的重要的類之間的關系。python中的繼承可分為兩種  has a 和 is a,下面介紹這兩種情況。

has a:

has a 強調一種包含的關系,比如:如果A中有B,那麼,B就是A的組成部分;當一個類中使用了另外一種自定義的類型(類或對象等),此時我們認為出現了has a 的包含現象。

###舉例###

class Computer:
    def __init__(self, brand, type, color):
        self.brand = brand
        self.type = type
        self.color = color

    def online(self):
        print('正在玩LOL中!')

    def __str__(self):
        return self.brand + '---' + self.type + '---' + self.color


class Book:
    def __init__(self, book_name, author, number):
        self.book_name = book_name
        self.author = author
        self.number = number

    def __str__(self):
        return self.book_name + '---' + self.author + '---' + str(self.number)


class Student:
    def __init__(self, name, computer, book):
        self.name = name
        self.computer = computer
        self.books = []
        self.books.append(book)

    def borrow_book(self, input_book):
        # print('書籍資訊:')
        # print(book)  #相當與調用對象名book1
        for book in self.books:
            if book.book_name == input_book.book_name:
                print('已經借過此書!')
                break
            else:
                self.books.append(input_book)
                print('借書成功!')
                break
        print(self.books)

    def show_book(self):
        for book in self.books:
            print(book.book_name)

    def __str__(self):
        return self.name + '---' + str(self.computer) + '---' + str(self.books)  # 不能傳回一個對象


# 建立對象
computer = Computer('hp', 'pro++', 'red')  # computer 對象類型
book = Book('武動乾坤', '天蠶洋芋', 15)

stu = Student('JJ', computer, book)  # 此時出現了  包含關系  (stu對象包含了computer對象和book對象)
print(stu)

book1 = Book('鬥破蒼穹', '天蠶洋芋', 20)

stu.show_book()
stu.borrow_book(book1)
           

輸出:

JJ---hp---pro++---red---[<__main__.Book object at 0x0000021AA3F7C610>]
武動乾坤
借書成功!
[<__main__.Book object at 0x0000021AA3F7C610>, <__main__.Book object at 0x0000021AA3F7C520>]
武動乾坤
鬥破蒼穹
           

從代碼:stu = Student('JJ', computer, book) 可以看出,定義一個Student對象的時候,傳入了                Computer的對象和Book的對象,故在Student類中可以對它們的(非私有化)屬性進行調                用。

is a:

is a 可以說是真正意義上的繼承關系:如果A是B的一種,那麼B就是A的基類(base class / 父類);比如:等邊三角形是三角形,則三角形是等邊三角形的基類。

特點:

1.私有化屬性不能被繼承

2.如果子類中沒有定義__init__,自動調用父類的__init__方法

3.如果子類繼承父類需要定義自己的__init__方法(來初始化新的屬性等操作),需要在__init__裡先     調用一下父類的__init__,再去初始化新的屬性或其他操作。

4.調用父類的方法:super關鍵詞

5.如果父類有eat(),子類也定義一個eat()方法,按照就近原則:先找目前類,再去找父類

   如果子類出現父類同名的方法,可稱之為 override--重寫(覆寫)。

   此情況一般出現在:父類提供的方法不能滿足子類的需求

6.子類中可調用父類的同名方法:

   使用super().方法名(參數)    #可調用父類的方法,再寫新增的方法(内容)

對于super關鍵詞的用法介紹:

Python中的super()方法設計目的是用來解決多重繼承時父類的查找問題,是以在單重繼承中用不用super都沒關系;但是,使用super()是一個好的習慣。一般我們在子類中需要調用父類的方法時才會這麼用。

1.super(cls,obj)      #即傳入類名+對象名

obj對象必須是cls類的對象(cls的子類的對象當然也是cls類的對象),記作 type(obj) <= cls;具有判斷功能

2.super(cls1,cls2)   #即傳入兩個類名

cls2必須是cls1的子類或是本身,記作cls2<=cls1 (右的範圍小于左);具有判斷功能

3.super().__init()      #等同于  super(A,self).__init__()

使用格式:

class Student(Person):     #可了解為所有學生都是人,Person是父類

        pass

###舉例說明###

# 可以定義一個“總類”
class Person:
    def __init__(self, name,age):
        self.name = name
        self.age = age

    def eat(self):
        print(self.name + '正在吃飯!','年齡:'+str(self.age))


# 在定義這種相似的類時,可能會出現許多重複的屬性或方法,此時需要使用到*繼承*
class Student(Person):  # 此為繼承的格式
    def __init__(self, name,age):
        print('--->Student的init')
        # 如果要定義自己的__init__,還需調用父類的__init__;可使用super來調用父類的方法
        # super(Student, self).__init__()  # super另一種使用方式
        super().__init__(name,age)  # super() == 父類Person ;調用父類的__init__方法
        super(Student, self).__init__()



class Employee(Person):
    def __init__(self,name,age,work):  #新增的屬性work,需要在本類中進行處理
        print('--->Employee的init')
        super().__init__(name,age) #調用父類的__init__先将self.name 和 self.age 擷取
        self.work = work     #獨有屬性在本類的__init__裡進行初始化
    def __str__(self):
        return '姓名:'+self.name+'年齡:'+str(self.age)+'職業:'+self.work
    
    def eat(self):
        super(Employee, self).eat()  #在子類中調用父類的同名方法,再向下寫新增的内容
        print(self.name+'正在吃飯! ')


class Doctor(Person):
    def __init__(self,name,age,department):
        super(Doctor, self).__init__(name,age)  #super(類名,對象).__init__();底層進行判斷:判斷該對象是否是以這個類建構出來的
        self.department = department
    def __str__(self):
        return '姓名:'+self.name+'年齡:'+str(self.age)+'部門:'+self.department

s = Student('Jack',16)
s.eat()
print(s.name)

e = Employee('Tom',29,'程式員')
print(e)
e.eat()    #此時由于Employee本類中就有eat方法,無需去調用父類(Person)裡的eat方法,故此為就近原則

d = Doctor('華佗',88,'老中醫')
print(d)
d.eat()
           

輸出:

--->Student的init
Jack正在吃飯! 年齡:16
Jack
--->Employee的init
姓名:Tom年齡:29職業:程式員
Tom正在吃飯! 年齡:29
Tom正在吃飯! 
姓名:華佗年齡:88部門:老中醫
華佗正在吃飯! 年齡:88
           

本例中,Person為父類,Student,Employee,Doctor,均繼承它,而這些子類都有一些本身才具有的性質,故需要重寫父類的__init__方法;此外,繼承中的檢索按照就近原則,先檢索本類,再到父類。

多繼承(了解):

Python允許多重繼承:一個子類可以有多個父類(java不可以)       #開發中很少使用

        class  子類(父類1,父類2...):

                pass

多重繼承的搜尋順序:在Python2中,對于經典類和新式類有所不同

經典類:  #Python2中是從左至右,深度優先

class Base:

        pass

class A(Base):

        pass

class B(Base):

        pass

新式類:   

class Base(object):   #差別在此,python2中是廣度優先

        pass

class A(Base):

        pass

class B(Base):

        pass

注意:在python3裡,無論經典類還是新式類都是廣度優先

           檢視搜尋順序的方法:1.類名.__mro__     #直接使用檢視

                                               2.import inspect   #導包後,使用getmro()方法檢視

                                                  print(inspect.getmro(類名))

###舉例說明###

#C繼承了A和B,而A和B繼承了Base
#Base-->A,B-->C
class Base:
    def test(self):
        print('--->Base')


class A(Base):
    def test1(self):
        print('--->A')

class B(Base):
    def test2(self):
        print('--->B')

class C(A,B):   #同時繼承多個父類
    def test(self):
        print('--->C')

import inspect
print(inspect.getmro(C))  #顯示的可了解為搜尋順序
print(C.__mro__)          #同上

c = C()
c.test1()  #可以使用多個父類提供的方法
c.test2()
# c.test3()
c.test()  #出現與父類同名方法時,搜尋順序為就近原則
           

輸出:

(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>)
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>)
--->A
--->B
--->C
           

注意:前兩行輸出為繼承類的搜尋順序

          最後的輸出是 調用的C類中的test方法,由于出現與父類同名的方法,搜尋順序為就近原則

多态:

實際上,python并無嚴格多态概念。多态可了解為:一種事物的多個形态,對于繼承可以認為是為了精簡代碼,而多态是對精簡後代碼的靈活使用,因而多态依賴于繼承。

##示例##

class Person:
    def __init__(self,name):
        self.name = name

    def feed_pet(self,pet):  #此時出現了多态;參數pet既可以接收cat,也可以接收dog,還可以接收tiger(不是Pet的子類)或其他對象
        if isinstance(pet,Pet): #判斷了pet是否是Pet或Pet子類的對象
            print('{}喜歡養寵物:{},昵稱是:{}'.format(self.name,pet.role,pet.nickname))
        else:
            print('這個不是寵物!可能有危險!!!')
class Pet:
    role = 'Pet'
    def __init__(self,nickanme,age):
        self.nickname = nickanme
        self.age = age

    def show(self):
        print('昵稱:{},年齡:{}'.format(self.nickname,self.age))

class Cat(Pet):
    role = '貓'
    def catch_mouse(self):
        print('抓老鼠...')

class Dog(Pet):
    role = '狗'
    def watch_house(self):
        print('看家...')

class Tiger:
    def eat(self):
        print('兇猛東北虎!')

cat = Cat('妙妙',3)
dog = Dog('旺财',2)
tiger = Tiger()
p = Pet('山竹',1)
person = Person('JJ')

person.feed_pet(cat)  #此時涉及到了多态
person.feed_pet(tiger)
person.feed_pet(p)
           

輸出:

JJ喜歡養寵物:貓,昵稱是:妙妙
這個不是寵物!可能有危險!!!
JJ喜歡養寵物:Pet,昵稱是:山竹
           

注意:

1.對同一個函數feed_pet(),傳入不同參數(對象),可以實作不同功能。

2.由于python中所有類均繼承于祖先類Object,故多态總能應用到很多場景(無論類與類之間是否有繼承關系)。

Python之面向對象(封裝,繼承,多态)封裝:繼承:多态: