Python3快速入門(mén)(六)——Python3面向?qū)ο?

一、面向?qū)ο蠹夹g(shù)簡(jiǎn)介

1、面向?qū)ο蠛?jiǎn)介

面向?qū)ο缶幊蹋∣bject Oriented Programing,OOP)是一種編程思想,OOP把對(duì)象當(dāng)成程序的一個(gè)基本單元,一個(gè)對(duì)象包含數(shù)據(jù)和操作數(shù)據(jù)的方法。面向?qū)ο缶幊痰娜筇匦匀缦拢?
A、封裝,可以隱藏實(shí)現(xiàn)細(xì)節(jié),使代碼模塊化。
B、繼承,可以通過(guò)擴(kuò)展已存在的類(lèi)來(lái)實(shí)現(xiàn)代碼重用,避免重復(fù)編寫(xiě)相同的代碼。
C、多態(tài),封裝和繼承的目的都是為了實(shí)現(xiàn)代碼重用, 而多態(tài)是為了實(shí)現(xiàn)接口重用,使得類(lèi)在繼承和派生的時(shí)候能夠保證任何一個(gè)類(lèi)的實(shí)例都能正確調(diào)用約定好的屬性和方法。
面向?qū)ο缶幊掏ㄟ^(guò)封裝、繼承、多態(tài)實(shí)現(xiàn)了軟件工程的重用性、靈活性、擴(kuò)展性三個(gè)目標(biāo)。

2、面向?qū)ο笮g(shù)語(yǔ)

類(lèi)(Class)是用來(lái)描述具有相同的屬性和方法的對(duì)象的集合,定義了集合中每個(gè)對(duì)象所共有的屬性和方法。
對(duì)象是類(lèi)的實(shí)例,Python中對(duì)象包括兩個(gè)數(shù)據(jù)成員(類(lèi)變量和實(shí)例變量)和方法。
方法是類(lèi)中定義的函數(shù)。
類(lèi)變量在類(lèi)的所有實(shí)例化對(duì)象中是公用的。類(lèi)變量定義在類(lèi)中且在函數(shù)體外。
方法重寫(xiě):如果從父類(lèi)繼承的方法不能滿(mǎn)足子類(lèi)的需求,可以對(duì)其進(jìn)行重寫(xiě)(override)。
繼承是一個(gè)派生類(lèi)(derived class)繼承基類(lèi)(base class)的字段和方法。繼承允許把一個(gè)派生類(lèi)的對(duì)象作為一個(gè)基類(lèi)對(duì)象對(duì)待。
實(shí)例化:創(chuàng)建一個(gè)類(lèi)的實(shí)例,類(lèi)的具體對(duì)象。

3、對(duì)象的屬性

在python當(dāng)中一切皆對(duì)象,每個(gè)對(duì)象都有三個(gè)屬性:id、類(lèi)型type和數(shù)值。id是對(duì)象的地址,id相同則必為同一對(duì)象,不同對(duì)象的值可以相同。

            
              # -*- coding:utf-8 -*-
x = 10
print(id(x))
print(type(x))  # 
              
                
print(x)

y = 10
print(id(y))
print(type(y))  # 
                
                  
print(y)

print(x is y)  # True
                
              
            
          

二、類(lèi)的定義

1、類(lèi)的定義

類(lèi)是一種抽象數(shù)據(jù)類(lèi)型,是對(duì)現(xiàn)實(shí)世界的一類(lèi)數(shù)據(jù)及其操作的封裝。
類(lèi)定義語(yǔ)法格式如下:

            
              class ClassName:
    
              
                
    .
    .
    .
    
                
              
            
          

類(lèi)實(shí)例化后,可以使用其屬性,創(chuàng)建一個(gè)類(lèi)后,可以通過(guò)類(lèi)名訪問(wèn)其類(lèi)屬性。
Person類(lèi)有以下3個(gè)屬性:
nationality:國(guó)籍
name:姓名
id:×××號(hào)碼

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

所有人的國(guó)籍基本都是相同的,且允許直接通過(guò)類(lèi)或?qū)嵗齺?lái)訪問(wèn),允許隨意修改。
大部分人的姓名是不同的,且允許直接通過(guò)類(lèi)的實(shí)例來(lái)訪問(wèn)和隨意修改。
所有人的×××號(hào)碼都是不一樣的,且不允許直接通過(guò)類(lèi)或?qū)嵗齺?lái)訪問(wèn)或隨意修改。

2、類(lèi)的實(shí)例化

            
              import Person

bauer = Person.Person("Bauer")
bauer.hello()
            
          

3、類(lèi)成員的可見(jiàn)性

Python中默認(rèn)所有的成員都是公有成員,但私有成員是以?xún)蓚€(gè)下劃線開(kāi)頭的名字表示私有成員,私有成員不允許直接訪問(wèn),只能通過(guò)內(nèi)部方法進(jìn)行訪問(wèn),私有成員也不允許被繼承。
Python中通過(guò)在類(lèi)變量、實(shí)例變量、類(lèi)方法、實(shí)例方法前加 __ 前綴,可以將其對(duì)外進(jìn)行隱藏,變?yōu)轭?lèi)的私有變量或函數(shù)。由于Python中內(nèi)置變量或函數(shù)使用 __ 前后綴,因此,不推薦私有的變量或函數(shù)加 __ 前后綴,只加 __ 前綴。
Python作為動(dòng)態(tài)語(yǔ)言,允許類(lèi)或?qū)嵗齽?dòng)態(tài)增加屬性,與類(lèi)內(nèi)部的私有的屬性并不相同。
Python類(lèi)維護(hù)了一個(gè)用于保存類(lèi)的數(shù)據(jù)的字典,字典內(nèi)部Python將私有成員改名為 _ClassName + __variable_name ,因此在類(lèi)外通過(guò)訪問(wèn)私有變量新的名稱(chēng)可以訪問(wèn)相應(yīng)的私有變量。

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

三、類(lèi)的屬性

1、類(lèi)屬性

直接定義在class下的屬性是公有屬性/類(lèi)屬性,類(lèi)屬性是類(lèi)的所有實(shí)例對(duì)象共同所有的,因此默認(rèn)情況下類(lèi)屬性值只會(huì)保留一份,而不會(huì)為類(lèi)的每個(gè)實(shí)例都保存一份。
類(lèi)屬性可以使用ClassName.VariableName訪問(wèn),在實(shí)例方法內(nèi)部也可以使用 self.__class__.VariableName 進(jìn)行訪問(wèn)。

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

類(lèi)屬性可以通過(guò)類(lèi)直接訪問(wèn),也可以直接通過(guò)實(shí)例進(jìn)行訪問(wèn); 如果通過(guò)類(lèi)的某個(gè)實(shí)例對(duì)類(lèi)屬性進(jìn)行修改,本質(zhì)上是為該實(shí)例添加了一個(gè)與類(lèi)屬性名稱(chēng)相同的實(shí)例屬性,對(duì)真正的類(lèi)屬性沒(méi)有影響,因此不會(huì)影響其它實(shí)例獲取類(lèi)屬性的值; 通過(guò)類(lèi)對(duì)類(lèi)屬性進(jìn)行修改,必然會(huì)改變類(lèi)屬性的值,對(duì)類(lèi)的所有實(shí)例是都有影響的。

2、實(shí)例屬性

實(shí)例屬性又稱(chēng)成員屬性或成員變量,是類(lèi)的每個(gè)實(shí)例對(duì)象單獨(dú)持有的屬性。實(shí)例屬性必須在類(lèi)的 init 方法中進(jìn)行聲明。

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

通過(guò)類(lèi)訪問(wèn)成員屬性會(huì)報(bào)錯(cuò):
print(Person.name)
實(shí)例屬性可以直接通過(guò)實(shí)例對(duì)象來(lái)訪問(wèn)和更改,是每個(gè)實(shí)例對(duì)象獨(dú)有的,某個(gè)實(shí)例對(duì)象的實(shí)例屬性被更改不會(huì)影響其它實(shí)例對(duì)象的相同屬性的值。實(shí)例屬性的值不能通過(guò)類(lèi)來(lái)訪問(wèn)和修改。
Python作為動(dòng)態(tài)語(yǔ)言,可以在類(lèi)外部動(dòng)態(tài)增加實(shí)例對(duì)象的屬性。

3、私有屬性

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

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

私有屬性不能通過(guò)類(lèi)直接訪問(wèn),也不能通過(guò)實(shí)例對(duì)象直接訪問(wèn),但私有屬性可以通過(guò)成員方法進(jìn)行訪問(wèn)。
私有屬性可以通過(guò)成員方法或是 實(shí)例對(duì)象._類(lèi)名__私有變量名 的方式來(lái)訪問(wèn)。

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

四、類(lèi)的特殊屬性

Python的類(lèi)中有一些內(nèi)置的、特殊的屬性,其名稱(chēng)以雙下劃線 __ 開(kāi)頭且以雙下劃線 __ 結(jié)尾。特殊屬性不是私有屬性,可以在類(lèi)的外部通過(guò)實(shí)例對(duì)象去直接訪問(wèn),且都有著各自特殊的意義。
__doc__ 表示類(lèi)的描述信息。
__module__ 表示當(dāng)前操作的對(duì)象對(duì)應(yīng)的類(lèi)的定義所在的模塊名。
__class__ 表示當(dāng)前操作的對(duì)象對(duì)應(yīng)的類(lèi)名。
__dict__ 是一個(gè)字典,保存類(lèi)的所有的成員(包括屬性和方法)或?qū)嵗龑?duì)象中的所有成員屬性。

            
              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):  # 成員方法/實(shí)例方法
        self.__hello()

    @classmethod
    def get_nationality(cls):  # 類(lèi)方法
        return cls.nationality

    @staticmethod
    def add(a, b):  # 靜態(tài)方法
        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

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

                
                  
None
{'__module__': '__main__', 'nationality': 'China', '__init__': 
                  
                    , '_Person__hello': 
                    
                      , 'say_hello': 
                      
                        , 'get_nationality': 
                        
                          , 'add': 
                          
                            , 'id': 
                            
                              , '__dict__': 
                              
                                , '__weakref__': 
                                
                                  , '__doc__': None}
__main__
"""
                                
                              
                            
                          
                        
                      
                    
                  
                
              
            
          

實(shí)例對(duì)象.__dict__ 類(lèi).__dict__ 的值是不同的, 實(shí)例對(duì)象.__dict__ 的值中只包含成員屬性和私有屬性, 類(lèi).__dict__ 的值中包含類(lèi)的類(lèi)屬性和所有方法;
__module__ __class__ 的值可用于反射來(lái)實(shí)例化一個(gè)類(lèi)的對(duì)象。

五、類(lèi)的方法

1、成員方法

成員方法通過(guò)類(lèi)的實(shí)例對(duì)象去訪問(wèn),第一個(gè)參數(shù)必須是當(dāng)前實(shí)例對(duì)象,通常寫(xiě)為self;但也可以通過(guò)類(lèi)名來(lái)調(diào)用成員方法,此時(shí)需要手動(dòng)的傳遞一個(gè)類(lèi)的實(shí)例對(duì)象給成員方法的self參數(shù)。

            
              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、私有方法

私有方法是以雙下劃線開(kāi)頭的成員方法。私有方法只能在實(shí)例方法內(nèi)部訪問(wèn),且不能被子類(lèi)繼承;私有方法的第一個(gè)參數(shù)也必須是當(dāng)前實(shí)例對(duì)象本身,通常寫(xiě)為self。通常,前后加雙下劃線的命名方式用于Python內(nèi)置的方法,不推薦自定義方法使用。如果開(kāi)發(fā)者以前后加雙下劃線的方式命名成員方法,則相應(yīng)成員方法是公有的。

            
              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):  # 成員方法/實(shí)例方法
        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、類(lèi)方法

?類(lèi)方法是以 @classmethod 來(lái)裝飾的成員方法,類(lèi)方法要求第一個(gè)參數(shù)必須是當(dāng)前類(lèi)。類(lèi)方法可通過(guò)實(shí)例對(duì)象進(jìn)行訪問(wèn),還可以直接通過(guò)類(lèi)名去訪問(wèn),且第一個(gè)參數(shù)表示的是當(dāng)前類(lèi),通常寫(xiě)為cls。類(lèi)方法只能訪問(wèn)類(lèi)屬性,不能訪問(wèn)實(shí)例屬性,因此第一個(gè)參數(shù)傳遞的是代表當(dāng)前類(lèi)的cls,而不是表示實(shí)例對(duì)象的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):  # 成員方法/實(shí)例方法
        self.__hello()

    @classmethod
    def get_nationality(cls):  # 類(lèi)方法
        return cls.nationality

if __name__ == "__main__":
    bauer = Person("Bauer")
    print(bauer.get_nationality())
    print(Person.get_nationality())

"""
output:
China
China
"""
            
          

4、靜態(tài)方法

靜態(tài)方法是以 @staticmethod 來(lái)裝飾的成員方法,靜態(tài)方法通常通過(guò)類(lèi)名進(jìn)行訪問(wèn),也可以通過(guò)類(lèi)的實(shí)例對(duì)象進(jìn)行訪問(wèn)。本質(zhì)上,靜態(tài)方法已經(jīng)與類(lèi)沒(méi)有任何關(guān)聯(lián),因?yàn)殪o態(tài)方法不要求必須傳遞實(shí)例對(duì)象或類(lèi)參數(shù)。
靜態(tài)方法內(nèi)部可以訪問(wèn)類(lèi)變量,可以直接使用 ClassName.Varaible_Name 方式對(duì)類(lèi)變量進(jìn)行訪問(wèn)。
靜態(tài)方法對(duì)參數(shù)沒(méi)有要求,因此可以任意給靜態(tài)方法定義參數(shù),如果給靜態(tài)方法定義表示當(dāng)前類(lèi)的參數(shù),那么就可以訪問(wèn)類(lèi)屬性;如果給靜態(tài)方法定義了表示當(dāng)前類(lèi)的實(shí)例對(duì)象的參數(shù),那么就可以訪問(wèn)實(shí)例屬性;如果沒(méi)有給靜態(tài)方法定義當(dāng)前類(lèi)參數(shù)或當(dāng)前實(shí)例參數(shù),那么就不能訪問(wèn)類(lèi)或?qū)嵗龑?duì)象的任何屬性。

            
              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):  # 成員方法/實(shí)例方法
        self.__hello()

    @classmethod
    def get_nationality(cls):  # 類(lèi)方法
        return cls.nationality

    @staticmethod
    def add(a, b):  # 靜態(tài)方法
        return a + b

    @staticmethod  #靜態(tài)方法,內(nèi)部使用類(lèi)變量
    def counter():
        return Person.sum

    @staticmethod
    def get_counter(cls):  #靜態(tài)方法,傳遞當(dāng)前類(lèi)
        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 來(lái)裝飾的成員方法,是以訪問(wèn)實(shí)例屬性的方式對(duì)實(shí)例屬性進(jìn)行訪問(wèn)的成員方法;屬性方法第一個(gè)參數(shù)必須是當(dāng)前實(shí)例對(duì)象,且屬性方法必須要有返回值。

            
              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):  # 成員方法/實(shí)例方法
        self.__hello()

    @classmethod
    def get_nationality(cls):  # 類(lèi)方法
        return cls.nationality

    @staticmethod
    def add(a, b):  # 靜態(tài)方法
        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中屬性方法通常用于在屬性方法內(nèi)部進(jìn)行一系列的邏輯計(jì)算,最終將計(jì)算結(jié)果返回。

6、方法的綁定

類(lèi)內(nèi)部定義的方法,在沒(méi)有被任何裝飾器修飾的情況下,是為了綁定到對(duì)象的,self關(guān)鍵字含有自動(dòng)傳值的過(guò)程,不管寫(xiě)不寫(xiě)self。 默認(rèn)情況下,在類(lèi)內(nèi)部定義的方法都是綁定到對(duì)象的方法。 綁定方法綁定到誰(shuí)的身上,誰(shuí)就作為第一個(gè)參數(shù)進(jìn)行傳入,綁定到類(lèi)的方法給對(duì)象使用是沒(méi)有任何意義的。
綁定到對(duì)象的方法,調(diào)用的時(shí)候會(huì)將對(duì)象參數(shù)自動(dòng)傳入;綁定到類(lèi)的方法,調(diào)用的時(shí)候會(huì)將類(lèi)作為參數(shù)自動(dòng)傳入。
靜態(tài)方法是非綁定方法,不與類(lèi)或?qū)ο蠼壎ǎl(shuí)都可以調(diào)用,沒(méi)有自動(dòng)傳值效果。非綁定方法不與類(lèi)或?qū)ο蠼壎ǎ?lèi)和對(duì)象都可以調(diào)用,但沒(méi)有自動(dòng)傳值。

            
              # -*- 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)
        #將對(duì)象以二進(jìn)制的形式寫(xiě)到磁盤(pán)
        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)
            
          

六、類(lèi)的特殊方法

Python的類(lèi)中有一些內(nèi)置的、特殊的方法,其名稱(chēng)是以雙下劃線 __ 開(kāi)頭且以雙下劃線 __ 結(jié)尾。特殊方法不是私有方法,可以在類(lèi)的外部通過(guò)實(shí)例對(duì)象去直接訪問(wèn),且都有著各自特殊的意義。

1、 init 構(gòu)造方法

__init__ 方法是類(lèi)構(gòu)造函數(shù),是類(lèi)的特殊的方法,在創(chuàng)建類(lèi)對(duì)象時(shí)自動(dòng)調(diào)用,不能有返回值。定義如下:

            
              def __init__(self):
    pass
            
          

__init__ 方法的第一個(gè)參數(shù)必須是創(chuàng)建的實(shí)例本身,通常推薦使用self。類(lèi)的實(shí)例屬性、私有屬性必須在 __init__ 方法進(jìn)行聲明。

            
              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 析構(gòu)方法

__del__ 是類(lèi)的析構(gòu)方法,當(dāng)對(duì)象在內(nèi)存中被釋放,會(huì)自動(dòng)觸發(fā)執(zhí)行 __del__ 方法,如實(shí)例對(duì)象的作用域退出時(shí),或者執(zhí)行?del 實(shí)例對(duì)象操作時(shí)。

            
              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

如果類(lèi)中定義了 __str__ 方法,那么在打印對(duì)象時(shí)默認(rèn)輸出 __str__ 方法的返回值,否則會(huì)打印出實(shí)例對(duì)象的內(nèi)存地址。

            
              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、 getitem setitem delitem

__setitem__ __getitem__ __delitem__? 用于索引操作,如對(duì)字典的操作,分別表示設(shè)置、獲取、刪除某個(gè)條目、數(shù)據(jù)。可以通過(guò) __setitem__ __getitem__ __delitem__ 方法來(lái)定義一個(gè)類(lèi)對(duì)字典進(jìn)行封裝,從而可以對(duì)字典中key的操作進(jìn)行控制,尤其是刪除操作。
如果一個(gè)類(lèi)實(shí)現(xiàn)了 __setitem__ __getitem__ __delitem__ 方法,就可以執(zhí)行一些字典操作。

            
              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__ 方法會(huì)在 __init__ 方法前被執(zhí)行,會(huì)創(chuàng)建并返回一個(gè)新的實(shí)例對(duì)象,然后傳遞給 __init__ __new__ 不是一個(gè)成員方法,而是一個(gè)靜態(tài)方法。
在Python中,一切皆對(duì)象,在新式類(lèi)中,為了將類(lèi)型(int,str,float等)和類(lèi)統(tǒng)一,所有的類(lèi)都是type類(lèi)型的對(duì)象。在類(lèi)中有一個(gè)屬性 __metaclass__ 可以指定當(dāng)前類(lèi)由哪個(gè)類(lèi)進(jìn)行實(shí)例化。而創(chuàng)建對(duì)象過(guò)程中,構(gòu)造函數(shù)不是 __init__ 方法,而是 __new__ 方法, __new__ 方法會(huì)返回一個(gè)對(duì)象,即對(duì)象構(gòu)造函數(shù)。
類(lèi)實(shí)例化對(duì)象內(nèi)部實(shí)現(xiàn)過(guò)程的代碼段:

            
              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

類(lèi)中定義 __call__ 方法時(shí),類(lèi)對(duì)象實(shí)例可以作為一個(gè)函數(shù)去調(diào)用,而函數(shù)的調(diào)用方式是函數(shù)名()。

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

七、類(lèi)的繼承

1、派生類(lèi)定義

Python中類(lèi)的繼承按照父類(lèi)中的方法是否已實(shí)現(xiàn)可分為兩種:
實(shí)現(xiàn)繼承?:指直接繼承父類(lèi)的屬性和已定義并實(shí)現(xiàn)的的方法;
接口繼承?:僅繼承父類(lèi)類(lèi)的屬性和方法名稱(chēng),子類(lèi)必須自行實(shí)現(xiàn)方法的具體功能代碼。
如果是根據(jù)要繼承的父類(lèi)的個(gè)數(shù)來(lái)分,有可以分為:
單繼承:?只繼承1個(gè)父類(lèi)。
多繼承:?繼承多個(gè)父類(lèi)。

            
              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類(lèi) 和Student類(lèi)都繼承 Person類(lèi),因此Teacher和Student是Person的子類(lèi)/派生類(lèi),而Person是Teacher和Student的父類(lèi)/基類(lèi)/超類(lèi);
Teacher和Student對(duì)Person的繼承屬于實(shí)現(xiàn)繼承,且是單繼承;
Teacher類(lèi)繼承了Person的name和age屬性,及talk()和walk()方法,并擴(kuò)展了自己的level和salary屬性,及teach()方法;
Student類(lèi)繼承了Person的name和age屬性,及talk()和walk()方法,并擴(kuò)展了自己的class屬性,及study()方法;
Teacher和Student對(duì)Person類(lèi)屬性和方法繼承體現(xiàn)了 代碼的重用性, 而Teacher和Student擴(kuò)展的屬性和方法體現(xiàn)了 靈活的擴(kuò)展性;
子類(lèi) Teacher 和 Student 也可以在自己的類(lèi)定義中重新定義父類(lèi)中的talk()和walk()方法,改變其實(shí)現(xiàn)代碼,即方法重寫(xiě)override。

2、派生類(lèi)構(gòu)造函數(shù)

派生類(lèi)的構(gòu)造函數(shù)需要顯式調(diào)用父類(lèi)的構(gòu)造函數(shù),對(duì)父類(lèi)的屬性成員進(jìn)行初始化,調(diào)用父類(lèi)的構(gòu)造函數(shù)時(shí)需要顯式傳遞實(shí)例對(duì)象self。
子類(lèi)需要在自己的 __init__ 方法中的第一行位置調(diào)用父類(lèi)的構(gòu)造方法,上述代碼給出了兩種方法:
super(子類(lèi)名, self).__init__(父類(lèi)構(gòu)造參數(shù)) ,如 super.(Teacher, self).__init__(name, age) ,推薦方式。
父類(lèi)名.__init__(self, 父類(lèi)構(gòu)造參數(shù)) ,如 Person.__init__(self, name, age)

3、isinstance

isinstance可以判斷一個(gè)變量是否是某一種數(shù)據(jù)類(lèi)型,也可以判斷對(duì)象是否是類(lèi)的對(duì)象或者是類(lèi)的子類(lèi)對(duì)象。
issubclass用來(lái)判斷一個(gè)類(lèi)是否是某個(gè)類(lèi)的子類(lèi),返回的是一個(gè)bool類(lèi)型數(shù)據(jù)。

            
              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
"""?
            
          

八、類(lèi)的多繼承

1、多繼承簡(jiǎn)介

Python支持多層父類(lèi)繼承,子類(lèi)會(huì)繼承父類(lèi)所有的屬性和方法,包括父類(lèi)的父類(lèi)的所有屬性和方法。Python雖然支持多繼承,但Python對(duì)多繼承的支持的也是有限的。
多繼承時(shí),使用super只會(huì)調(diào)用第一個(gè)父類(lèi)的屬性方法,因此,要想調(diào)用特定父類(lèi)的構(gòu)造器只能顯式調(diào)用 父類(lèi)名.__init__

如果父類(lèi)中有相同的方法名,而在子類(lèi)使用時(shí)未顯式指定調(diào)用的具體賦類(lèi)的方法,Python會(huì)根據(jù)繼承順序從左至右搜索查找父類(lèi)中是否包含方法。

            
              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
[
              
                , 
                
                  , 
                  
                    , 
                    
                      , 
                      
                        ]
"""
                      
                    
                  
                
              
            
          

如果子類(lèi)從多個(gè)父類(lèi)派生,而子類(lèi)沒(méi)有自己的構(gòu)造函數(shù)時(shí),按順序繼承,哪個(gè)父類(lèi)在最前面且有自己的構(gòu)造函數(shù),就繼承其構(gòu)造函數(shù)。

            
              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
[
              
                , 
                
                  , 
                  
                    , 
                    
                      , 
                      
                        ]
"""
                      
                    
                  
                
              
            
          

如果子類(lèi)從多個(gè)父類(lèi)派生,而子類(lèi)沒(méi)有自己的構(gòu)造函數(shù)時(shí),如果最前面第一個(gè)父類(lèi)沒(méi)有構(gòu)造函數(shù),則依次查找后序繼承父類(lèi)的構(gòu)造函數(shù)。

            
              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
[
              
                , 
                
                  , 
                  
                    , 
                    
                      , 
                      
                        ]
"""
                      
                    
                  
                
              
            
          

2、多繼承查找順序

類(lèi)的屬性 __mro__ 或者方法mro()都能打印出類(lèi)的繼承順序,super()在執(zhí)行時(shí)查找MRO列表,到列表當(dāng)前位置的類(lèi)中去查找其下一個(gè)類(lèi)。
為了實(shí)現(xiàn)繼承,Python會(huì)在MRO列表上從左到右開(kāi)始查找基類(lèi),直到找到第一個(gè)匹配屬性的類(lèi)為止。
在Python 3.x中無(wú)論是否顯式指定繼承object,所有的類(lèi)都是新式類(lèi),在多繼承的情況下,經(jīng)典類(lèi)查找父類(lèi)屬性或方法的順序是深度優(yōu)先,新式類(lèi)查找父類(lèi)屬性的順序是廣度優(yōu)先。
super是MRO中的一個(gè)類(lèi)。MRO全稱(chēng)Method Resolution Order,代表類(lèi)的繼承順序。對(duì)于定義的每一個(gè)類(lèi),Python會(huì)計(jì)算出一個(gè)方法解析順序(MRO)列表,MRO列表是一個(gè)簡(jiǎn)單的所有基類(lèi)的線性順序列表。
MRO列表的構(gòu)造是通過(guò)一個(gè)C3線性化算法來(lái)實(shí)現(xiàn)的,MRO會(huì)合并所有父類(lèi)的MRO列表并遵循如下三條準(zhǔn)則:
A、子類(lèi)會(huì)先于父類(lèi)被檢查。
B、多個(gè)父類(lèi)會(huì)根據(jù)它們?cè)诹斜碇械捻樞虮粰z查。
C、如果對(duì)下一個(gè)類(lèi)存在兩個(gè)合法的選擇,選擇第一個(gè)父類(lèi)。
MRO可以保證多繼承情況每個(gè)類(lèi)只出現(xiàn)一次,super().init相對(duì)于類(lèi)名.init,在單繼承上用法基本無(wú)差,但在多繼承上,super方法能保證每個(gè)父類(lèi)的方法只會(huì)執(zhí)行一次,而使用類(lèi)名的方法會(huì)導(dǎo)致方法被執(zhí)行多次。
單繼承時(shí),使用super方法,不能全部傳遞,只能傳父類(lèi)方法所需的參數(shù),否則會(huì)報(bào)錯(cuò);多繼承時(shí),使用類(lèi)名.init方法需要把每個(gè)父類(lèi)全部寫(xiě)一遍,而使用super方法只需一條語(yǔ)句便執(zhí)行全部父類(lèi)的方法,因此多繼承需要全部傳參。
九、類(lèi)的多態(tài)性
多態(tài)通常是通過(guò)繼承接口的方式實(shí)現(xiàn)的,Python中沒(méi)有接口,但Python中可以通過(guò)在一個(gè)成員方法體中拋出一個(gè)NotImplementedError異常來(lái)強(qiáng)制繼承接口的子類(lèi)在調(diào)用接口方法前必須先實(shí)現(xiàn)接口方法。

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

接口的所有子類(lèi)必須實(shí)現(xiàn)接口中定義的所有方法;接口的各個(gè)子類(lèi)在實(shí)現(xiàn)接口中同一個(gè)方法時(shí),具體的代碼實(shí)現(xiàn)各不相同,即多態(tài)。

十、反射機(jī)制

Python中反射機(jī)制是通過(guò)hasattr、getattr、setattr、delattr四個(gè)內(nèi)置函數(shù)實(shí)現(xiàn)的,四個(gè)內(nèi)置函數(shù)不僅可以用在類(lèi)和對(duì)象中,也可以用在模塊等。
hasattr(key)返回的是一個(gè)bool值,判斷某個(gè)成員或者屬性在不在類(lèi)或者對(duì)象中。
getattr(key,default=xxx)獲取類(lèi)或者對(duì)象的成員或?qū)傩裕绻淮嬖冢瑒t會(huì)拋出AttributeError異常,如果定義了default那么當(dāng)沒(méi)有屬性的時(shí)候會(huì)返回默認(rèn)值。
setattr(key,value)假如有key屬性,那么更新key屬性,如果沒(méi)有就添加key屬性并賦值value。
delattr(key)刪除某個(gè)屬性。
實(shí)例代碼如下:

            
              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'
            
          

十一、單例模式

在面向?qū)ο缶幊讨校瑔卫J绞且粋€(gè)類(lèi)只有一個(gè)對(duì)象,所有的操作都通過(guò)單例對(duì)象來(lái)完成,實(shí)現(xiàn)代碼如下:

            
              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、異常處理簡(jiǎn)介

Python中使用try except finally組合來(lái)實(shí)現(xiàn)異常撲捉,except中的Exception是所有異常的父類(lèi),異常處理的示例如下:

            
              try:
    int("12a")  #可能出現(xiàn)異常的代碼
except IndexError as e:  # 捕捉索引異常的子異常
    print("IndexError:",e)
except ValueError as e:  # 捕捉value錯(cuò)誤的子異常
    print("ValueError:",e)
except Exception as e:  # 使用Exception捕獲,Exception能夠捕獲所有的異常
    print("Exception:",e)
else:  # 如果沒(méi)有異常發(fā)生,執(zhí)行else中的代碼塊
    print("true")
finally:  # 不管是否發(fā)生異常,在最后都會(huì)執(zhí)行finally中的代碼,假如try里面的代碼正常執(zhí)行,先執(zhí)行else中的代碼,再執(zhí)行finally中的代碼
    print("finally")
            
          

2、自定義異常處理

Exception是所有異常的父類(lèi),可以自定義Exception的子類(lèi),實(shí)現(xiàn)自定義異常處理。

            
              class TypeErrorException(Exception):
    def __init__(self, message):
        self.message = message

    def __str__(self): # 打印異常的時(shí)候會(huì)調(diào)用對(duì)象里面的__str__方法返回一個(gè)字符串
        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一般用在判斷執(zhí)行環(huán)境上,只要斷言的條件不滿(mǎn)足,就拋出異常,后續(xù)代碼不會(huì)被執(zhí)行。

            
              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 
              
                
    assert ok == result
AssertionError
"""