面向對象編程(Object Oriented Programing,OOP)是一種編程思想,OOP把對象當成程序的一個基本單元,一個對象包含數據和操做數據的方法。面向對象編程的三大特性以下:
A、封裝,能夠隱藏實現細節,使代碼模塊化。
B、繼承,能夠經過擴展已存在的類來實現代碼重用,避免重複編寫相同的代碼。
C、多態,封裝和繼承的目的都是爲了實現代碼重用, 而多態是爲了實現接口重用,使得類在繼承和派生的時候可以保證任何一個類的實例都能正確調用約定好的屬性和方法。
面向對象編程經過封裝、繼承、多態實現了軟件工程的重用性、靈活性、擴展性三個目標。python
類(Class)是用來描述具備相同的屬性和方法的對象的集合,定義了集合中每一個對象所共有的屬性和方法。
對象是類的實例,Python中對象包括兩個數據成員(類變量和實例變量)和方法。
方法是類中定義的函數。
類變量在類的全部實例化對象中是公用的。類變量定義在類中且在函數體外。
方法重寫:若是從父類繼承的方法不能知足子類的需求,能夠對其進行重寫(override)。
繼承是一個派生類(derived class)繼承基類(base class)的字段和方法。繼承容許把一個派生類的對象做爲一個基類對象對待。
實例化:建立一個類的實例,類的具體對象。mysql
在python當中一切皆對象,每一個對象都有三個屬性:id、類型type和數值。id是對象的地址,id相同則必爲同一對象,不一樣對象的值能夠相同。算法
# -*- 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
類是一種抽象數據類型,是對現實世界的一類數據及其操做的封裝。
類定義語法格式以下:sql
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))
全部人的國籍基本都是相同的,且容許直接經過類或實例來訪問,容許隨意修改。
大部分人的姓名是不一樣的,且容許直接經過類的實例來訪問和隨意修改。
全部人的×××號碼都是不同的,且不容許直接經過類或實例來訪問或隨意修改。ide
import Person bauer = Person.Person("Bauer") bauer.hello()
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 """
直接定義在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 """
類屬性能夠經過類直接訪問,也能夠直接經過實例進行訪問; 若是經過類的某個實例對類屬性進行修改,本質上是爲該實例添加了一個與類屬性名稱相同的實例屬性,對真正的類屬性沒有影響,所以不會影響其它實例獲取類屬性的值; 經過類對類屬性進行修改,必然會改變類屬性的值,對類的全部實例是都有影響的。ui
實例屬性又稱成員屬性或成員變量,是類的每一個實例對象單獨持有的屬性。實例屬性必須在類的init方法中進行聲明。this
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做爲動態語言,能夠在類外部動態增長實例對象的屬性。
私有屬性和實例屬性必須在__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__
的值可用於反射來實例化一個類的對象。
成員方法經過類的實例對象去訪問,第一個參數必須是當前實例對象,一般寫爲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 """
私有方法是以雙下劃線開頭的成員方法。私有方法只能在實例方法內部訪問,且不能被子類繼承;私有方法的第一個參數也必須是當前實例對象自己,一般寫爲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 """
類方法是以@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 """
靜態方法是以@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 """
屬性方法是以@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中屬性方法一般用於在屬性方法內部進行一系列的邏輯計算,最終將計算結果返回。
類內部定義的方法,在沒有被任何裝飾器修飾的狀況下,是爲了綁定到對象的,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的類中有一些內置的、特殊的方法,其名稱是以雙下劃線__
開頭且以雙下劃線__
結尾。特殊方法不是私有方法,能夠在類的外部經過實例對象去直接訪問,且都有着各自特殊的意義。
__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__ """
__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__ """
若是類中定義了__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__ """
__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 """
__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 """
類中定義__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 """
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。
派生類的構造函數須要顯式調用父類的構造函數,對父類的屬性成員進行初始化,調用父類的構造函數時須要顯式傳遞實例對象self。
子類須要在本身的__init__
方法中的第一行位置調用父類的構造方法,上述代碼給出了兩種方法:super(子類名, self).__init__(父類構造參數)
,如super.(Teacher, self).__init__(name, age)
,推薦方式。父類名.__init__(self, 父類構造參數)
,如Person.__init__(self, name, age)
。
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 """
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'>] """
類的屬性__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方法只需一條語句便執行所有父類的方法,所以多繼承須要所有傳參。
9、類的多態性
多態一般是經過繼承接口的方式實現的,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
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")
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")
斷言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 """