Python3快速入門(六)Python3面向對象

一、面向對象技術簡介

1、面向對象簡介

面向對象編程(Object Oriented Programing,OOP)是一種編程思想,OOP把對象當成程序的一個基本單元,一個對象包含數據和操作數據的方法。面向對象編程的三大特性如下:

A、封裝,可以隱藏實現細節,使代碼模塊化。

B、繼承,可以通過擴展已存在的類來實現代碼重用,避免重複編寫相同的代碼。

C、多態,封裝和繼承的目的都是爲了實現代碼重用, 而多態是爲了實現接口重用,使得類在繼承和派生的時候能夠保證任何一個類的實例都能正確調用約定好的屬性和方法。

面向對象編程通過封裝、繼承、多態實現了軟件工程的重用性、靈活性、擴展性三個目標。

2、面向對象術語

類(Class)是用來描述具有相同的屬性和方法的對象的集合,定義了集合中每個對象所共有的屬性和方法。

對象是類的實例,Python中對象包括兩個數據成員(類變量和實例變量)和方法。

方法是類中定義的函數。

類變量在類的所有實例化對象中是公用的。類變量定義在類中且在函數體外。

方法重寫:如果從父類繼承的方法不能滿足子類的需求,可以對其進行重寫(override)。

繼承是一個派生類(derived class)繼承基類(base class)的字段和方法。繼承允許把一個派生類的對象作爲一個基類對象對待。

實例化:創建一個類的實例,類的具體對象。

3、對象的屬性

在python當中一切皆對象,每個對象都有三個屬性:id、類型type和數值。id是對象的地址,id相同則必爲同一對象,不同對象的值可以相同。


在學習過程中有什麼不懂得可以加我的
python學習交流扣扣qun,784-758-214
羣裏有不錯的學習視頻教程、開發工具與電子書籍。
與你分享python企業當下人才需求及怎麼從零基礎學習好python,和學習什麼內容

# -*- coding:utf-8 -*-

x = 10

print(id(x))

print(type(x))  # <class 'int'>

print(x)

y = 10

print(id(y))

print(type(y))  # <class 'int'>

print(y)

print(x is y)  # True

二、類的定義

1、類的定義

類是一種抽象數據類型,是對現實世界的一類數據及其操作的封裝。

類定義語法格式如下:


class ClassName:

    <statement-1>

    .

    .

    .

    <statement-N>

類實例化後,可以使用其屬性,創建一個類後,可以通過類名訪問其類屬性。

Person類有以下3個屬性:

nationality:國籍

name:姓名

id:×××號碼


import uuid

class Person:

    nationality = "China"

    def __init__(self, name):

        self.name = name

        self.__id = str(uuid.uuid1())

    def hello(self):

        print("Hello, I am %s, I come from %s, My ID is %s" %(self.name, self.nationality, self.__id))

所有人的國籍基本都是相同的,且允許直接通過類或實例來訪問,允許隨意修改。

大部分人的姓名是不同的,且允許直接通過類的實例來訪問和隨意修改。

所有人的×××號碼都是不一樣的,且不允許直接通過類或實例來訪問或隨意修改。

2、類的實例化


import Person

bauer = Person.Person("Bauer")

bauer.hello()

3、類成員的可見性

Python中默認所有的成員都是公有成員,但私有成員是以兩個下劃線開頭的名字表示私有成員,私有成員不允許直接訪問,只能通過內部方法進行訪問,私有成員也不允許被繼承。

Python中通過在類變量、實例變量、類方法、實例方法前加__前綴,可以將其對外進行隱藏,變爲類的私有變量或函數。由於Python中內置變量或函數使用__前後綴,因此,不推薦私有的變量或函數加__前後綴,只加__前綴。

Python作爲動態語言,允許類或實例動態增加屬性,與類內部的私有的屬性並不相同。

Python類維護了一個用於保存類的數據的字典,字典內部Python將私有成員改名爲_ClassName + __variable_name,因此在類外通過訪問私有變量新的名稱可以訪問相應的私有變量。


import uuid

class Person(object):

    nationality = "China"

    def __init__(self, name):

        self.name = name

        self.__id = str(uuid.uuid1())

    def hello(self):

        print("Hello, I am %s, I come from %s, My ID is %s" %(self.name, self.nationality, self.__id))

if __name__ == "__main__":

    bauer = Person("Bauer")

    print(bauer.__dict__)

    print(bauer._Person__id)

"""

output:

{'name': 'Bauer', '_Person__id': 'ed496846-94c7-11e9-80c4-5ce0c5e8bcf0'}

ed496846-94c7-11e9-80c4-5ce0c5e8bcf0

"""

三、類的屬性

1、類屬性

直接定義在class下的屬性是公有屬性/類屬性,類屬性是類的所有實例對象共同所有的,因此默認情況下類屬性值只會保留一份,而不會爲類的每個實例都保存一份。

類屬性可以使用ClassName.VariableName訪問,在實例方法內部也可以使用self.__class__.VariableName進行訪問。


import uuid

class Person(object):

    nationality = "China"

    def __init__(self, name):

        self.name = name

        self.__id = str(uuid.uuid1())

    def hello(self):

        print("Hello, I am %s, I come from %s, My ID is %s" %(self.name, self.nationality, self.__id))

    def sayHello(self):

        print("Hello,I come from %s" % self.__class__.nationality)

if __name__ == "__main__":

    bauer = Person("Bauer")

    bauer.sayHello()

    jack = Person("Jack")

    print(Person.nationality, bauer.nationality, jack.nationality)

    bauer.nationality = "USA"

    print(Person.nationality, bauer.nationality, jack.nationality)

    Person.nationality = "Germany"

    print(Person.nationality, bauer.nationality, jack.nationality)

"""

output:

Hello,I come from China

China China China

China USA China

Germany USA Germany

"""

類屬性可以通過類直接訪問,也可以直接通過實例進行訪問; 如果通過類的某個實例對類屬性進行修改,本質上是爲該實例添加了一個與類屬性名稱相同的實例屬性,對真正的類屬性沒有影響,因此不會影響其它實例獲取類屬性的值; 通過類對類屬性進行修改,必然會改變類屬性的值,對類的所有實例是都有影響的。

2、實例屬性

實例屬性又稱成員屬性或成員變量,是類的每個實例對象單獨持有的屬性。實例屬性必須在類的init方法中進行聲明。


import uuid

class Person(object):

    nationality = "China"

    def __init__(self, name):

        self.name = name

        self.__id = str(uuid.uuid1())

    def hello(self):

        print("Hello, I am %s, I come from %s, My ID is %s" %(self.name, self.nationality, self.__id))

if __name__ == "__main__":

    bauer = Person("Bauer")

    jack = Person("Jack")

    print(bauer.name, jack.name)

    bauer.name = "Jack Bauer"

    jack.name = "Chen Jack"

    print(bauer.name, jack.name)

    #print(Person.name)  ## AttributeError: type object 'Person' has no attribute 'name'

"""

output:

Bauer Jack

Jack Bauer Chen Jack

"""

通過類訪問成員屬性會報錯:

print(Person.name)

實例屬性可以直接通過實例對象來訪問和更改,是每個實例對象獨有的,某個實例對象的實例屬性被更改不會影響其它實例對象的相同屬性的值。實例屬性的值不能通過類來訪問和修改。

Python作爲動態語言,可以在類外部動態增加實例對象的屬性。

3、私有屬性

私有屬性和實例屬性必須在__init__方法中進行聲明,但私有屬性的屬性名需要以雙下劃線__開頭,比如Person中的__id屬性。私有屬性是一種特殊的實例屬性,只允許在實例對象的內部(成員方法或私有方法中)訪問,而不允許在實例對象的外部通過實例對象或類來直接訪問,也不能被子類繼承。


import uuid

class Person(object):

    nationality = "China"

    def __init__(self, name):

        self.name = name

        self.__id = str(uuid.uuid1())

    def hello(self):

        print("Hello, I am %s, I come from %s, My ID is %s" %(self.name, self.nationality, self.__id))

if __name__ == "__main__":

    bauer = Person("Bauer")

    bauer.hello()

    # print(bauer.__id) # AttributeError: 'Person' object has no attribute '__id'

    # print(Person.__id) # AttributeError: type object 'Person' has no attribute '__id'

"""

output:

Hello, I am Bauer, I come from China, My ID is c0c02dcc-94aa-11e9-972c-5ce0c5e8bcf0

"""

私有屬性不能通過類直接訪問,也不能通過實例對象直接訪問,但私有屬性可以通過成員方法進行訪問。

私有屬性可以通過成員方法或是實例對象._類名__私有變量名的方式來訪問。


import uuid

class Person(object):

    nationality = "China"

    def __init__(self, name):

        self.name = name

        self.__id = str(uuid.uuid1())

    def hello(self):

        print("Hello, I am %s, I come from %s, My ID is %s" %(self.name, self.nationality, self.__id))

    def get_id(self):

        return self.__id

if __name__ == "__main__":

    bauer = Person("Bauer")

    bauer.hello()

    print(bauer._Person__id)

    print(bauer.get_id())

"""

output:

Hello, I am Bauer, I come from China, My ID is c0c02dcc-94aa-11e9-972c-5ce0c5e8bcf0

354547ae-94ab-11e9-a52c-5ce0c5e8bcf0

354547ae-94ab-11e9-a52c-5ce0c5e8bcf0

"""

四、類的特殊屬性

Python的類中有一些內置的、特殊的屬性,其名稱以雙下劃線__開頭且以雙下劃線__結尾。特殊屬性不是私有屬性,可以在類的外部通過實例對象去直接訪問,且都有着各自特殊的意義。

__doc__表示類的描述信息。

__module__表示當前操作的對象對應的類的定義所在的模塊名。

__class__表示當前操作的對象對應的類名。

__dict__是一個字典,保存類的所有的成員(包括屬性和方法)或實例對象中的所有成員屬性。


import uuid

class Person(object):

    nationality = "China"

    def __init__(self, name):

        self.name = name

        self.__id = str(uuid.uuid1())

    def __hello(self):  # 私有方法

        print("Hello, I am %s, I come from %s, My ID is %s" %(self.name, self.nationality, self.__id))

    def say_hello(self):  # 成員方法/實例方法

        self.__hello()

    @classmethod

    def get_nationality(cls):  # 類方法

        return cls.nationality

    @staticmethod

    def add(a, b):  # 靜態方法

        return a + b

    @property

    def id(self):

        return self.__id

if __name__ == "__main__":

    bauer = Person("Bauer")

    print("Person Object")

    print(bauer.__class__)

    print(bauer.__doc__)

    print(bauer.__dict__)

    print(bauer.__module__)

    print("Person Class")

    print(Person.__class__)

    print(Person.__doc__)

    print(Person.__dict__)

    print(Person.__module__)

"""

output:

Person Object

<class '__main__.Person'>

None

{'name': 'Bauer', '_Person__id': 'f545f99a-94b5-11e9-aa3f-5ce0c5e8bcf0'}

__main__

Person Class

<class 'type'>

None

{'__module__': '__main__', 'nationality': 'China', '__init__': <function Person.__init__ at 0x7f49941d5d90>, '_Person__hello': <function Person.__hello at 0x7f49941d5e18>, 'say_hello': <function Person.say_hello at 0x7f49941d5ea0>, 'get_nationality': <classmethod object at 0x7f499b470be0>, 'add': <staticmethod object at 0x7f499b470c18>, 'id': <property object at 0x7f499b464908>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}

__main__

"""

實例對象.__dict__類.__dict__的值是不同的,實例對象.__dict__的值中只包含成員屬性和私有屬性,類.__dict__的值中包含類的類屬性和所有方法;

__module____class__的值可用於反射來實例化一個類的對象。

五、類的方法

1、成員方法

成員方法通過類的實例對象去訪問,第一個參數必須是當前實例對象,通常寫爲self;但也可以通過類名來調用成員方法,此時需要手動的傳遞一個類的實例對象給成員方法的self參數。


import uuid

class Person(object):

    nationality = "China"

    def __init__(self, name):

        self.name = name

        self.__id = str(uuid.uuid1())

    def hello(self):

        print("Hello, I am %s, I come from %s, My ID is %s" %(self.name, self.nationality, self.__id))

if __name__ == "__main__":

    bauer = Person("Bauer")

    bauer.hello()

    Person.hello(bauer)

"""

output:

Hello, I am Bauer, I come from China, My ID is 0e4d0606-94af-11e9-a958-5ce0c5e8bcf0

Hello, I am Bauer, I come from China, My ID is 0e4d0606-94af-11e9-a958-5ce0c5e8bcf0

"""

2、私有方法

私有方法是以雙下劃線開頭的成員方法。私有方法只能在實例方法內部訪問,且不能被子類繼承;私有方法的第一個參數也必須是當前實例對象本身,通常寫爲self。通常,前後加雙下劃線的命名方式用於Python內置的方法,不推薦自定義方法使用。如果開發者以前後加雙下劃線的方式命名成員方法,則相應成員方法是公有的。


import uuid

class Person(object):

    nationality = "China"

    def __init__(self, name):

        self.name = name

        self.__id = str(uuid.uuid1())

    def __hello(self):  # 私有方法

        print("Hello, I am %s, I come from %s, My ID is %s" %(self.name, self.nationality, self.__id))

    def say_hello(self):  # 成員方法/實例方法

        self.__hello()

if __name__ == "__main__":

    bauer = Person("Bauer")

    bauer.say_hello()

"""

output:

Hello, I am Bauer, I come from China, My ID is 0e4d0606-94af-11e9-a958-5ce0c5e8bcf0

"""

3、類方法

類方法是以@classmethod來裝飾的成員方法,類方法要求第一個參數必須是當前類。類方法可通過實例對象進行訪問,還可以直接通過類名去訪問,且第一個參數表示的是當前類,通常寫爲cls。類方法只能訪問類屬性,不能訪問實例屬性,因此第一個參數傳遞的是代表當前類的cls,而不是表示實例對象的self。


import uuid

class Person(object):

    nationality = "China"

    def __init__(self, name):

        self.name = name

        self.__id = str(uuid.uuid1())

    def __hello(self):  # 私有方法

        print("Hello, I am %s, I come from %s, My ID is %s" %(self.name, self.nationality, self.__id))

    def say_hello(self):  # 成員方法/實例方法

        self.__hello()

    @classmethod

    def get_nationality(cls):  # 類方法

        return cls.nationality

if __name__ == "__main__":

    bauer = Person("Bauer")

    print(bauer.get_nationality())

    print(Person.get_nationality())

"""

output:

China

China

"""

4、靜態方法

靜態方法是以@staticmethod來裝飾的成員方法,靜態方法通常通過類名進行訪問,也可以通過類的實例對象進行訪問。本質上,靜態方法已經與類沒有任何關聯,因爲靜態方法不要求必須傳遞實例對象或類參數。

靜態方法內部可以訪問類變量,可以直接使用ClassName.Varaible_Name方式對類變量進行訪問。

靜態方法對參數沒有要求,因此可以任意給靜態方法定義參數,如果給靜態方法定義表示當前類的參數,那麼就可以訪問類屬性;如果給靜態方法定義了表示當前類的實例對象的參數,那麼就可以訪問實例屬性;如果沒有給靜態方法定義當前類參數或當前實例參數,那麼就不能訪問類或實例對象的任何屬性。


import uuid

class Person(object):

    sum = 0

    nationality = "China"

    def __init__(self, name):

        self.name = name

        self.__id = str(uuid.uuid1())

        Person.sum += 1

    def __hello(self):  # 私有方法

        print("Hello, I am %s, I come from %s, My ID is %s" %(self.name, self.nationality, self.__id))

    def say_hello(self):  # 成員方法/實例方法

        self.__hello()

    @classmethod

    def get_nationality(cls):  # 類方法

        return cls.nationality

    @staticmethod

    def add(a, b):  # 靜態方法

        return a + b

    @staticmethod  #靜態方法,內部使用類變量

    def counter():

        return Person.sum

    @staticmethod

    def get_counter(cls):  #靜態方法,傳遞當前類

        return cls.sum

if __name__ == "__main__":

    bauer = Person("Bauer")

    print(bauer.add(1, 2))

    print(Person.add(1, 2))

    print(Person.counter())

    print(Person.get_counter(Person))

"""

output:

3

3

1

1

"""

5、屬性方法

屬性方法是以@property來裝飾的成員方法,是以訪問實例屬性的方式對實例屬性進行訪問的成員方法;屬性方法第一個參數必須是當前實例對象,且屬性方法必須要有返回值。


import uuid

class Person(object):

    nationality = "China"

    def __init__(self, name):

        self.name = name

        self.__id = str(uuid.uuid1())

    def __hello(self):  # 私有方法

        print("Hello, I am %s, I come from %s, My ID is %s" %(self.name, self.nationality, self.__id))

    def say_hello(self):  # 成員方法/實例方法

        self.__hello()

    @classmethod

    def get_nationality(cls):  # 類方法

        return cls.nationality

    @staticmethod

    def add(a, b):  # 靜態方法

        return a + b

    @property

    def id(self):

        return self.__id

if __name__ == "__main__":

    bauer = Person("Bauer")

    print(bauer.id)

    # print(bauer.id()) # TypeError: 'str' object is not callable

"""

output:

631baef4-94b3-11e9-babe-5ce0c5e8bcf0

"""

Python中屬性方法通常用於在屬性方法內部進行一系列的邏輯計算,最終將計算結果返回。

6、方法的綁定

類內部定義的方法,在沒有被任何裝飾器修飾的情況下,是爲了綁定到對象的,self關鍵字含有自動傳值的過程,不管寫不寫self。 默認情況下,在類內部定義的方法都是綁定到對象的方法。 綁定方法綁定到誰的身上,誰就作爲第一個參數進行傳入,綁定到類的方法給對象使用是沒有任何意義的。

綁定到對象的方法,調用的時候會將對象參數自動傳入;綁定到類的方法,調用的時候會將類作爲參數自動傳入。

靜態方法是非綁定方法,不與類或對象綁定,誰都可以調用,沒有自動傳值效果。非綁定方法不與類或對象綁定,類和對象都可以調用,但沒有自動傳值。


# -*- coding:utf-8 -*-

import time

import hashlib

import pickle

import os

"""

HOST = "127.1.1.1"

PORT = 3306

DB_PATH = r"/usr/lib/mysql/db"

"""

class MySQL:

    HOST = "127.1.1.1"

    PORT = 3306

    DB_PATH = r"/var/lib/mysql"

    @staticmethod

    def create_id():

        m = hashlib.md5(str(time.perf_counter()).encode("utf-8"))

        return m.hexdigest()

    def __init__(self,host,port):

        self.id = self.create_id()

        self.host = host

        self.port = port

    @classmethod

    def from_conf(cls):

        return cls.HOST, cls.PORT

    def save(self):

        file_path = r"%s%s%s"%(MySQL.DB_PATH,os.sep,self.id)

        #將對象以二進制的形式寫到磁盤

        pickle.dump(self,open(file_path,"wb"))

    def get(self):

        file_path = r"%s%s%s" % (MySQL.DB_PATH, os.sep, self.id)

        return pickle.load(open(file_path,"rb"))

if __name__ == '__main__':

    conn1 = MySQL("127.0.0.1","3306")

    print(conn1.id)

    conn1.save()

    result = conn1.get()

    print(result.id)

六、類的特殊方法

Python的類中有一些內置的、特殊的方法,其名稱是以雙下劃線__開頭且以雙下劃線__結尾。特殊方法不是私有方法,可以在類的外部通過實例對象去直接訪問,且都有着各自特殊的意義。

1、init構造方法

__init__方法是類構造函數,是類的特殊的方法,在創建類對象時自動調用,不能有返回值。定義如下:


def __init__(self):

    pass

__init__方法的第一個參數必須是創建的實例本身,通常推薦使用self。類的實例屬性、私有屬性必須在__init__方法進行聲明。


import uuid

class Person(object):

    nationality = "China"

    def __init__(self, name):

        self.name = name

        self.__id = str(uuid.uuid1())

        print(self.name, "__init__")

if __name__ == "__main__":

    bauer = Person("Bauer")

"""

output:

Bauer __init__

"""

2、del析構方法

__del__是類的析構方法,當對象在內存中被釋放,會自動觸發執行__del__方法,如實例對象的作用域退出時,或者執行 del 實例對象操作時。


import uuid

class Person(object):

    nationality = "China"

    def __init__(self, name):

        self.name = name

        self.__id = str(uuid.uuid1())

        print(self.name, "__init__")

    def __del__(self):

        print(self.name, "__del__")

if __name__ == "__main__":

    bauer = Person("Bauer")

    del bauer

"""

output:

Bauer __init__

Bauer __del__

"""

3、str

如果類中定義了__str__方法,那麼在打印對象時默認輸出__str__方法的返回值,否則會打印出實例對象的內存地址。


import uuid

class Person(object):

    nationality = "China"

    def __init__(self, name):

        self.name = name

        self.__id = str(uuid.uuid1())

        print(self.name, "__init__")

    def __del__(self):

        print(self.name, "__del__")

    def __str__(self):

        return "name: %s, nationality: %s, id: %s" % (self.name, self.nationality, self.__id)

if __name__ == "__main__":

    bauer = Person("Bauer")

    print(bauer)

"""

output:

Bauer __init__

name: Bauer, nationality: China, id: 0a9a80c2-94c0-11e9-891d-5ce0c5e8bcf0

Bauer __del__

"""

4、getitemsetitemdelitem

__setitem____getitem____delitem__用於索引操作,如對字典的操作,分別表示設置、獲取、刪除某個條目、數據。可以通過__setitem____getitem____delitem__方法來定義一個類對字典進行封裝,從而可以對字典中key的操作進行控制,尤其是刪除操作。

如果一個類實現了__setitem____getitem____delitem__方法,就可以執行一些字典操作。


class ChineseDict(object):

    def __init__(self, init=None):

        self.__dict = init if init is not None else {}

    def __setitem__(self, key, value):

        print('__setitem__', key)

        self.__dict[key] = value

    def __getitem__(self, item):

        print('__getitem__', item)

        return self.__dict.get(item, None)

    def __delitem__(self, key):

        print('__delitem__', key)

        if key is not None and key.startswith('wh'):

            print('You can not delete this item ')

            return None

        return self.__dict.pop(key, None)

if __name__ == "__main__":

    dic = ChineseDict(init={'name': 'Bauer', 'nationality': 'China', "age": 23})

    print(dic["name"], dic["nationality"], dic["age"])

    del dic["age"]

    print(dic["age"])

"""

output:

__getitem__ name

__getitem__ nationality

__getitem__ age

Bauer China 23

__delitem__ age

__getitem__ age

None

"""

5、new

__new__方法會在__init__方法前被執行,會創建並返回一個新的實例對象,然後傳遞給__init____new__不是一個成員方法,而是一個靜態方法。

在Python中,一切皆對象,在新式類中,爲了將類型(int,str,float等)和類統一,所有的類都是type類型的對象。在類中有一個屬性 __metaclass__可以指定當前類由哪個類進行實例化。而創建對象過程中,構造函數不是__init__方法,而是__new__方法,__new__方法會返回一個對象,即對象構造函數。

類實例化對象內部實現過程的代碼段:


class PersonType(type):

    def __init__(cls, what, bases=None, dic=None):

        super(PersonType, cls).__init__(what, bases, dic)

    def __call__(cls, *args, **kwargs):

        obj = cls.__new__(cls)

        cls.__init__(obj, *args, **kwargs)

        return obj

class Dog:

    __metaclass__ = PersonType

    def __init__(self,name,age):

        self.name=name

        self.age=age

    def __new__(cls, *args, **kwargs):

        return object.__new__(cls)

if __name__ == "__main__":

    obj = Dog("Dog", 3)

    print(obj.name, obj.age)

"""

output:

Dog 3

"""

6、call

類中定義__call__方法時,類對象實例可以作爲一個函數去調用,而函數的調用方式是函數名()。


import uuid

class Person(object):

    nationality = "China"

    def __init__(self, name):

        self.name = name

        self.__id = str(uuid.uuid1())

    def __call__(self, *args, **kwargs):

        print("name: ", self.name, "args: ", *args)

    def hello(self):

        print("Hello, I am %s, I come from %s, My ID is %s" %(self.name, self.nationality, self.__id))

if __name__ == "__main__":

    bauer = Person("Bauer")

    bauer("China", 26)

"""

output:

name:  Bauer args:  China 26

"""

七、類的繼承

1、派生類定義

Python中類的繼承按照父類中的方法是否已實現可分爲兩種:

實現繼承 :指直接繼承父類的屬性和已定義並實現的的方法;

接口繼承 :僅繼承父類類的屬性和方法名稱,子類必須自行實現方法的具體功能代碼。

如果是根據要繼承的父類的個數來分,有可以分爲:

單繼承: 只繼承1個父類。

多繼承: 繼承多個父類。


class Person(object):

    def __init__(self, name, age):

        self.name = name

        self.age = age

    def walk(self):

        print('%s is walking...' % self.name)

    def talk(self):

        print('%s is talking...' % self.name)

class Teacher(Person):

    def __init__(self, name, age, level, salary):

        super(Teacher, self).__init__(name, age)

        self.level = level

        self.salary = salary

    def teach(self):

        print('%s is teaching...' % self.name)

class Student(Person):

    def __init__(self, name, age, class_):

        Person.__init__(self, name, age)

        self.class_ = class_

    def study(self):

        print('%s is studying...' % self.name)

if __name__ == "__main__":

    t1 = Teacher('Bauer', 33, 'Senior', 20000)

    s1 = Student('Jack', 13, 'A class')

    t1.talk()

    t1.walk()

    t1.teach()

    s1.talk()

    s1.walk()

    s1.study()

"""

output:

Bauer is talking...

Bauer is walking...

Bauer is teaching...

Jack is talking...

Jack is walking...

Jack is studying...

"""

Teacher類 和Student類都繼承 Person類,因此Teacher和Student是Person的子類/派生類,而Person是Teacher和Student的父類/基類/超類;

Teacher和Student對Person的繼承屬於實現繼承,且是單繼承;

Teacher類繼承了Person的name和age屬性,及talk()和walk()方法,並擴展了自己的level和salary屬性,及teach()方法;

Student類繼承了Person的name和age屬性,及talk()和walk()方法,並擴展了自己的class屬性,及study()方法;

Teacher和Student對Person類屬性和方法繼承體現了 代碼的重用性, 而Teacher和Student擴展的屬性和方法體現了 靈活的擴展性;

子類 Teacher 和 Student 也可以在自己的類定義中重新定義父類中的talk()和walk()方法,改變其實現代碼,即方法重寫override。

2、派生類構造函數

派生類的構造函數需要顯式調用父類的構造函數,對父類的屬性成員進行初始化,調用父類的構造函數時需要顯式傳遞實例對象self。

子類需要在自己的__init__方法中的第一行位置調用父類的構造方法,上述代碼給出了兩種方法:

super(子類名, self).__init__(父類構造參數),如super.(Teacher, self).__init__(name, age),推薦方式。

父類名.__init__(self, 父類構造參數),如Person.__init__(self, name, age)

3、isinstance

isinstance可以判斷一個變量是否是某一種數據類型,也可以判斷對象是否是類的對象或者是類的子類對象。

issubclass用來判斷一個類是否是某個類的子類,返回的是一個bool類型數據。


import uuid

class Person(object):

    nationality = "China"

    def __init__(self, name):

        self.name = name

        self.__id = str(uuid.uuid1())

    def hello(self):

        print("Hello, I am %s, I come from %s, My ID is %s" %(self.name, self.nationality, self.__id))

class Teacher(Person):

    pass

if __name__ == "__main__":

    bauer = Teacher("Bauer")

    print(isinstance(bauer, Person))

    print(issubclass(Teacher, Person))

"""

output:

True

True

""" 

八、類的多繼承

1、多繼承簡介

Python支持多層父類繼承,子類會繼承父類所有的屬性和方法,包括父類的父類的所有屬性和方法。Python雖然支持多繼承,但Python對多繼承的支持的也是有限的。

多繼承時,使用super只會調用第一個父類的屬性方法,因此,要想調用特定父類的構造器只能顯式調用父類名.__init__

如果父類中有相同的方法名,而在子類使用時未顯式指定調用的具體賦類的方法,Python會根據繼承順序從左至右搜索查找父類中是否包含方法。


class A(object):

    def __init__(self):

        print("class A")

    def hello(self):

        print('hello, class A')

    def func2(self):

        print('class A: func2')

class B(A):

    def __init__(self):

        A.__init__(self)

        print("class B")

    def hello(self):

        print('hello, class B')

class C(A):

    def __init__(self):

        A.__init__(self)

        print("class C")

    def hello(self):

        print('hello, class C')

class D(B, C):

    def __init__(self):

        B.__init__(self)

        C.__init__(self)

        print("class D")

if __name__ == "__main__":

    d = D()

    d.hello()

    print(D.mro())

"""

output:

class A

class B

class A

class C

class D

hello, class B

[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

"""

如果子類從多個父類派生,而子類沒有自己的構造函數時,按順序繼承,哪個父類在最前面且有自己的構造函數,就繼承其構造函數。


class A(object):

    def __init__(self):

        print("class A")

    def hello(self):

        print('hello, class A')

    def func2(self):

        print('class A: func2')

class B(A):

    def __init__(self):

        A.__init__(self)

        print("class B")

    def hello(self):

        print('hello, class B')

class C(A):

    def __init__(self):

        A.__init__(self)

        print("class C")

    def hello(self):

        print('hello, class C')

class D(B, C):

    pass

if __name__ == "__main__":

    d = D()

    d.hello()

    print(D.mro())

"""

output:

class A

class B

hello, class B

[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

"""

如果子類從多個父類派生,而子類沒有自己的構造函數時,如果最前面第一個父類沒有構造函數,則依次查找後序繼承父類的構造函數。


class A(object):

    def __init__(self):

        print("class A")

    def hello(self):

        print('hello, class A')

    def func2(self):

        print('class A: func2')

class B(A):

    def hello(self):

        print('hello, class B')

class C(A):

    def __init__(self):

        A.__init__(self)

        print("class C")

    def hello(self):

        print('hello, class C')

class D(B, C):

    pass

if __name__ == "__main__":

    d = D()

    d.hello()

    print(D.mro())

"""

output:

class A

class C

hello, class B

[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

"""

2、多繼承查找順序

類的屬性__mro__或者方法mro()都能打印出類的繼承順序,super()在執行時查找MRO列表,到列表當前位置的類中去查找其下一個類。

爲了實現繼承,Python會在MRO列表上從左到右開始查找基類,直到找到第一個匹配屬性的類爲止。

在Python 3.x中無論是否顯式指定繼承object,所有的類都是新式類,在多繼承的情況下,經典類查找父類屬性或方法的順序是深度優先,新式類查找父類屬性的順序是廣度優先。

super是MRO中的一個類。MRO全稱Method Resolution Order,代表類的繼承順序。對於定義的每一個類,Python會計算出一個方法解析順序(MRO)列表,MRO列表是一個簡單的所有基類的線性順序列表。

MRO列表的構造是通過一個C3線性化算法來實現的,MRO會合並所有父類的MRO列表並遵循如下三條準則:

A、子類會先於父類被檢查。

B、多個父類會根據它們在列表中的順序被檢查。

C、如果對下一個類存在兩個合法的選擇,選擇第一個父類。

MRO可以保證多繼承情況每個類只出現一次,super().init相對於類名.init,在單繼承上用法基本無差,但在多繼承上,super方法能保證每個父類的方法只會執行一次,而使用類名的方法會導致方法被執行多次。

單繼承時,使用super方法,不能全部傳遞,只能傳父類方法所需的參數,否則會報錯;多繼承時,使用類名.init方法需要把每個父類全部寫一遍,而使用super方法只需一條語句便執行全部父類的方法,因此多繼承需要全部傳參。

九、類的多態性

多態通常是通過繼承接口的方式實現的,Python中沒有接口,但Python中可以通過在一個成員方法體中拋出一個NotImplementedError異常來強制繼承接口的子類在調用接口方法前必須先實現接口方法。


class Animal(object):  # Animal Interface

    def __init__(self, name):

        self.name = name

    def walk(self):

        raise NotImplemented('Subclass must implement the abstract method by self')

    def talk(self):

        raise NotImplemented('Subclass must implement the abstract method by self')

class Dog(Animal):

    def talk(self):

        print('%s is talking:wang wang...' % self.name)

    def walk(self):

        print('%s is a Dog,walk by 4 legs' % self.name)

class Duck(Animal):

    def talk(self):

        print('%s is talking: ga ga...' % self.name)

    def walk(self):

        print('%s is a Duck,walk by 2 legs' % self.name)

if __name__ == "__main__":

    dog = Dog('Trump')

    dog.talk()

    dog.walk()

    duck = Duck('Tang')

    duck.talk()

    duck.walk()

"""

output:

Trump is talking:wang wang...

Trump is a Dog,walk by 4 legs

Tang is talking: ga ga...

Tang is a Duck,walk by 2 legs

"""

接口的所有子類必須實現接口中定義的所有方法;接口的各個子類在實現接口中同一個方法時,具體的代碼實現各不相同,即多態。

十、反射機制

Python中反射機制是通過hasattr、getattr、setattr、delattr四個內置函數實現的,四個內置函數不僅可以用在類和對象中,也可以用在模塊等。

hasattr(key)返回的是一個bool值,判斷某個成員或者屬性在不在類或者對象中。

getattr(key,default=xxx)獲取類或者對象的成員或屬性,如果不存在,則會拋出AttributeError異常,如果定義了default那麼當沒有屬性的時候會返回默認值。

setattr(key,value)假如有key屬性,那麼更新key屬性,如果沒有就添加key屬性並賦值value。

delattr(key)刪除某個屬性。

實例代碼如下:


import uuid

class Person(object):

    nationality = "China"

    def __init__(self, name):

        self.name = name

        self.__id = str(uuid.uuid1())

    def hello(self):

        print("Hello, I am %s, I come from %s, My ID is %s" %(self.name, self.nationality, self.__id))

if __name__ == "__main__":

    bauer = Person("Bauer")

    setattr(bauer, "sex", "Man")

    print(getattr(bauer, "name"))

    print(getattr(bauer, "nationality"))

    print(getattr(bauer, "sex"))

    helloFunc = getattr(bauer, "hello")

    helloFunc()

    if hasattr(bauer, "job"):

        print(getattr(bauer, "job"))

    delattr(bauer, "sex")

    print(getattr(bauer, "name"))

    print(getattr(bauer, "nationality"))

    print(getattr(bauer, "sex"))  # AttributeError: 'Person' object has no attribute 'sex'

十一、單例模式

在面向對象編程中,單例模式是一個類只有一個對象,所有的操作都通過單例對象來完成,實現代碼如下:


class Instance:

    __instance = None

    @classmethod

    def get_instance(cls):

        if cls.__instance:

            return cls.__instance

        else:

            cls.__instance = Instance

            return cls.__instance

obj1 = Instance.get_instance()

print(id(obj1))

obj2 = Instance.get_instance()

print(id(obj2))

# output:

# 35307304

# 35307304

十二、異常處理

1、異常處理簡介

Python中使用try except finally組合來實現異常撲捉,except中的Exception是所有異常的父類,異常處理的示例如下:


try:

    int("12a")  #可能出現異常的代碼

except IndexError as e:  # 捕捉索引異常的子異常

    print("IndexError:",e)

except ValueError as e:  # 捕捉value錯誤的子異常

    print("ValueError:",e)

except Exception as e:  # 使用Exception捕獲,Exception能夠捕獲所有的異常

    print("Exception:",e)

else:  # 如果沒有異常發生,執行else中的代碼塊

    print("true")

finally:  # 不管是否發生異常,在最後都會執行finally中的代碼,假如try裏面的代碼正常執行,先執行else中的代碼,再執行finally中的代碼

    print("finally")

2、自定義異常處理

Exception是所有異常的父類,可以自定義Exception的子類,實現自定義異常處理。


class TypeErrorException(Exception):

    def __init__(self, message):

        self.message = message

    def __str__(self): # 打印異常的時候會調用對象裏面的__str__方法返回一個字符串

        return self.message

if __name__ == "__main__":

    try:

        raise TypeErrorException("Type error")

    except TypeErrorException as e:

        print("TypeErrorException:",e)

    except Exception as e:

        print("Exception:",e)

    else:

        print("true")

    finally:

        print("finally")

3、斷言

斷言assert一般用在判斷執行環境上,只要斷言的條件不滿足,就拋出異常,後續代碼不會被執行。


print("Assert test")

ok = True

result = False

assert ok == result

print("Assert test")

# output:

"""

Assert test

Traceback (most recent call last):

  File "test.py", line 6, in <module>

    assert ok == result

AssertionError

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