什麼是繼承:html
在python中,新建的類能夠繼承一個或多個父類,經過繼承建立的新類稱爲「子類」或「派生類」,被繼承的類稱爲「基類」、「父類」或「超類」。java
python中類的繼承分爲:單繼承和多繼承python
class ParentClass1: #定義父類 pass class ParentClass2: #定義父類 pass class SubClass1(ParentClass1): #單繼承,基類是ParentClass1,派生類是SubClass pass class SubClass2(ParentClass1,ParentClass2): #python支持多繼承,用逗號分隔開多個繼承的類 pass
查看繼承linux
>>> SubClass1.__bases__ #__base__只查看從左到右繼承的第一個子類,__bases__則是查看全部繼承的父類 (<class '__main__.ParentClass1'>,) >>> SubClass2.__bases__ (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)
經典類與新式類算法
1.只有在python2中才分新式類和經典類,python3中統一都是新式類 2.在python2中,沒有顯式的繼承object類的類,以及該類的子類,都是經典類 3.在python2中,顯式地聲明繼承object的類,以及該類的子類,都是新式類 3.在python3中,不管是否繼承object,都默認繼承object,即python3中全部類均爲新式類
提示:若是沒有指定基類,python的類會默認繼承object類,object是全部python類的基類,它提供了一些常見方法(如__str__)的實現sql
>>> ParentClass1.__bases__ (<class 'object'>,) >>> ParentClass2.__bases__ (<class 'object'>,)
python究竟是如何實現繼承的,對於你定義的每個類,python會計算出一個方法解析順序(MRO)列表,這個MRO列表就是一個簡單的全部基類的線性順序列表,例如django
>>> F.mro() #等同於F.__mro__ [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
爲了實現繼承,python會在MRO列表上從左到右開始查找基類,直到找到第一個匹配這個屬性的類爲止。而這個MRO列表的構造是經過一個C3線性化算法來實現的。咱們不去深究這個算法的數學原理,它實際上就是合併全部父類的MRO列表並遵循以下三條準則:安全
在Java和C#中子類只能繼承一個父類,而Python中子類能夠同時繼承多個父類,若是繼承了多個父類,那麼屬性的查找方式有兩種,分別是:深度優先和廣度優先app
示範代碼函數
class A(object): def test(self): print('from A') class B(A): def test(self): print('from B') class C(A): def test(self): print('from C') class D(B): def test(self): print('from D') class E(C): def test(self): print('from E') class F(D,E): # def test(self): # print('from F') pass f1=F() f1.test() print(F.__mro__) #只有新式纔有這個屬性能夠查看線性列表,經典類沒有這個屬性 #新式類繼承順序:F->D->B->E->C->A #經典類繼承順序:F->D->B->A->E->C #python3中統一都是新式類 #pyhon2中才分新式類與經典類
在子類派生出的新方法中,每每須要重用父類的方法,咱們有兩種方式實現
方式一:指名道姓,即父類名.父類方法() 注意:這種調用須要在參數中加self
class Vehicle: #定義交通工具類 Country='China' def __init__(self,name,speed,load,power): self.name=name self.speed=speed self.load=load self.power=power def run(self): print('開動啦...') class Subway(Vehicle): #地鐵 def __init__(self,name,speed,load,power,line): Vehicle.__init__(self,name,speed,load,power) self.line=line def run(self): print('地鐵%s號線歡迎您' %self.line) Vehicle.run(self) line13=Subway('中國地鐵','180m/s','1000人/箱','電',13) line13.run()
方式二:super() 注意:這種調用不用在參數中加self
class Vehicle: #定義交通工具類 Country='China' def __init__(self,name,speed,load,power): self.name=name self.speed=speed self.load=load self.power=power def run(self): print('開動啦...') class Subway(Vehicle): #地鐵 def __init__(self,name,speed,load,power,line): #super(Subway,self) 就至關於實例自己 在python3中super()等同於super(Subway,self) super().__init__(name,speed,load,power) self.line=line def run(self): print('地鐵%s號線歡迎您' %self.line) super(Subway,self).run() class Mobike(Vehicle):#摩拜單車 pass line13=Subway('中國地鐵','180m/s','1000人/箱','電',13) line13.run()
這兩種方式的區別是:方式一是跟繼承沒有關係的,而方式二的super()是依賴於繼承的,而且即便沒有直接繼承關係,super仍然會按照mro繼續日後查找
#A沒有繼承B,可是A內super會基於C.mro()繼續日後找 class A: def test(self): super().test() class B: def test(self): print('from B') class C(A,B): pass c=C() c.test() #打印結果:from B print(C.mro()) #[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
組合就是把一個對象傳給另外一個類做爲類的參數,或者把對象添加到另外一個對象中
>>> class Equip: #武器裝備類 ... def fire(self): ... print('release Fire skill') ... >>> class Riven: #英雄Riven的類,一個英雄須要有裝備,於是須要組合Equip類 ... camp='Noxus' ... def __init__(self,nickname): ... self.nickname=nickname ... self.equip=Equip() #用Equip類產生一個裝備,賦值給實例的equip屬性 ... >>> r1=Riven('銳雯雯') >>> r1.equip.fire() #可使用組合的類產生的對象所持有的方法 release Fire skill class People: def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex class Course: def __init__(self,name,period,price): self.name=name self.period=period self.price=price def tell_info(self): print('<%s %s %s>' %(self.name,self.period,self.price)) class Teacher(People): def __init__(self,name,age,sex,job_title): People.__init__(self,name,age,sex) self.job_title=job_title self.course=[] self.students=[] class Student(People): def __init__(self,name,age,sex): People.__init__(self,name,age,sex) self.course=[] egon=Teacher('egon',18,'male','沙河霸道金牌講師') s1=Student('牛榴彈',18,'female') python=Course('python','3mons',3000.0) linux=Course('python','3mons',3000.0) #爲老師egon和學生s1添加課程 egon.course.append(python) egon.course.append(linux) s1.course.append(python) #爲老師egon添加學生s1 egon.students.append(s1) #使用 for obj in egon.course: obj.tell_info()
首先看java爲何有接口,由於java的類不能多繼承,接口用來實現多繼承,且java接口中方法都爲抽象方法。
java中抽象類和接口的區別:抽象類由abstract關鍵字來修飾,接口由interface關鍵字來修飾。抽象類中除了有抽象方法外,也能夠有數據成員和非抽象方法;而接口中全部的方法必須都是抽象的,接口中也能夠定義數據成員,但必須是常量。
接口
在python中根本就沒有一個叫作interface的關鍵字,若是非要去模仿接口的概念
能夠藉助第三方模塊:http://pypi.python.org/pypi/zope.interface
在python中實現抽象類
#一切皆文件 import abc #利用abc模塊實現抽象類 class All_file(metaclass=abc.ABCMeta): all_type='file' @abc.abstractmethod #定義抽象方法,無需實現功能 def read(self): '子類必須定義讀功能' pass @abc.abstractmethod #定義抽象方法,無需實現功能 def write(self): '子類必須定義寫功能' pass # class Txt(All_file): # pass # # t1=Txt() #報錯,子類沒有定義抽象方法 class Txt(All_file): #子類繼承抽象類,可是必須定義read和write方法 def read(self): print('文本數據的讀取方法') def write(self): print('文本數據的讀取方法') class Sata(All_file): #子類繼承抽象類,可是必須定義read和write方法 def read(self): print('硬盤數據的讀取方法') def write(self): print('硬盤數據的讀取方法') class Process(All_file): #子類繼承抽象類,可是必須定義read和write方法 def read(self): print('進程數據的讀取方法') def write(self): print('進程數據的讀取方法') wenbenwenjian=Txt() yingpanwenjian=Sata() jinchengwenjian=Process() #這樣你們都是被歸一化了,也就是一切皆文件的思想 wenbenwenjian.read() yingpanwenjian.write() jinchengwenjian.read() print(wenbenwenjian.all_type) print(yingpanwenjian.all_type) print(jinchengwenjian.all_type)
在python中用雙下劃線開頭的方式將屬性隱藏起來(設置成私有的)
【非特殊調用狀況下,子類和實例化的對象不能繼承,重寫,使用該方法或屬性。特殊調用均可以,緣由在下面代碼裏】
#其實這僅僅這是一種變形操做,在解釋器解析class時自動變形 #類中全部雙下劃線開頭的名稱如__x都會自動變造成:_類名__x的形式: #因爲只有在解析class定義時纔會變形,因此只要是本類外的類或對象都沒法訪問原形式 class A: __N=0 #類的數據屬性就應該是共享的,可是語法上是能夠把類的數據屬性設置成私有的如__N,會變形爲_A__N def __init__(self): self.__X=10 #變形爲self._A__X def __foo(self): #變形爲_A__foo print('from A') def bar(self): self.__foo() #只有在類內部才能夠經過__foo的形式訪問到. #A._A__N是能夠訪問到的,即這種操做並非嚴格意義上的限制外部訪問,僅僅只是一種語法意義上的變形
這種自動變形的特色:
這種變形須要注意的問題是:
一、這種機制也並無真正意義上限制咱們從外部直接訪問屬性,知道了類名和屬性名就能夠拼出名字:_類名__屬性,而後就能夠訪問了,如a._A__N
二、變形的過程只在類的定義是發生一次,在定義後的賦值操做,不會變形
三、在繼承中,父類若是不想讓子類覆蓋本身的方法,能夠將方法定義爲私有的
#正常狀況 >>> class A: ... def fa(self): ... print('from A') ... def test(self): ... self.fa() ... >>> class B(A): ... def fa(self): ... print('from B') ... >>> b=B() >>> b.test() from B #把fa定義成私有的,即__fa >>> class A: ... def __fa(self): #在定義時就變形爲_A__fa ... print('from A') ... def test(self): ... self.__fa() #只會與本身所在的類爲準,即調用_A__fa ... >>> class B(A): ... def __fa(self): ... print('from B') ... >>> b=B() >>> b.test() from A
class A: def __test(self): print("A.test") def func(self): print("A.func") self.__test() class B(A): def __test(self): print("B.test") b = B() b.func() ############### A.func A.test ############### class A: def test(self): print("A.test") def func(self): print("A.func") self.test() class B(A): def test(self): print("B.test") b = B() b.func() ################## A.func B.test
第一個輸出A.test的緣由是類A中func中self.__test()在解釋時已經變造成self._A__test(),因此調用的是父類中的__test方法,這也符合隱藏方法只能在當前類調用的原則
1:封裝數據,將數據隱藏起來這不是目的。隱藏起來而後對外提供操做該數據的接口才是目的
class Teacher: def __init__(self,name,age): self.__name=name self.__age=age def tell_info(self): print('姓名:%s,年齡:%s' %(self.__name,self.__age)) def set_info(self,name,age): if not isinstance(name,str): raise TypeError('姓名必須是字符串類型') if not isinstance(age,int): raise TypeError('年齡必須是整型') self.__name=name self.__age=age t=Teacher('egon',18) t.tell_info() t.set_info('egon',19) t.tell_info()
2:封裝方法:目的是隔離複雜度
#取款是功能,而這個功能有不少功能組成:插卡、密碼認證、輸入金額、打印帳單、取錢 #對使用者來講,只須要知道取款這個功能便可,其他功能咱們均可以隱藏起來,很明顯這麼作 #隔離了複雜度,同時也提高了安全性 class ATM: def __card(self): print('插卡') def __auth(self): print('用戶認證') def __input(self): print('輸入取款金額') def __print_bill(self): print('打印帳單') def __take_money(self): print('取款') def withdraw(self): self.__card() self.__auth() self.__input() self.__print_bill() self.__take_money() a=ATM() a.withdraw()
property
將一個類的函數定義成特性之後,對象再去使用的時候obj.name,根本沒法察覺本身的name是執行了一個函數而後計算出來的,這種特性的使用方式遵循了統一訪問的原則
ps:面向對象的封裝有三種方式: 【public】 這種其實就是不封裝,是對外公開的 【protected】 這種封裝方式對外不公開,但對朋友(friend)或者子類(形象的說法是「兒子」,但我不知道爲何你們 不說「女兒」,就像「parent」原本是「父母」的意思,但中文都是叫「父類」)公開 【private】 這種封裝對誰都不公開
python並無在語法上把它們三個內建到本身的class機制中,在C++裏通常會將全部的全部的數據都設置爲私有的,而後提供set和get方法(接口)去設置和獲取,在python中經過property方法能夠實現
class Foo: def __init__(self, val): self.__NAME = val # 將全部的數據屬性都隱藏起來 @property def name(self): return self.__NAME # obj.name訪問的是self.__NAME(這也是真實值的存放位置) @name.setter # 當對name賦值時該函數被調用 def name(self, value): if not isinstance(value, str): # 在設定值以前進行類型檢查 raise TypeError('%s must be str' % value) self.__NAME = value # 經過類型檢查後,將值value存放到真實的位置self.__NAME @name.deleter # 當name被刪除時,該函數被調用 def name(self): raise TypeError('Can not delete') f = Foo('egon') print(f.name) print(f.__dict__) # f.name=10 #賦值,調用setter,拋出異常'TypeError: 10 must be str' # del f.name # 刪除,調用deleterious,拋出異常'TypeError: Can not delete' ############# egon {'_Foo__NAME': 'egon'}
staticmethod
在類的方法定義語句上方寫上@staticmethod表明其爲靜態方法,靜態方法沒有默認的self參數,和普通函數同樣,至關於類的一個工具方法。固然也能夠給他傳self(對象名),可是這就失去了靜態方法的意義。
classmethod
在類的方法定義語句上方寫上@classmethod把類中的函數定義成類方法,默認攜帶cls參數,cls表明調用的類
類方法,實例方法,靜態方法的對比
class Foo(object): """類三種方法語法形式""" def instance_method(self): print("是類{}的實例方法,只能被實例對象調用".format(Foo)) @staticmethod def static_method(): print("是靜態方法") @classmethod def class_method(cls): print("是類方法") foo = Foo() foo.instance_method() foo.static_method() foo.class_method() print('----------------') Foo.static_method() Foo.class_method()
實例方法只能被實例對象調用,靜態方法(由@staticmethod裝飾的方法)、類方法(由@classmethod裝飾的方法),能夠被類或類的實例對象調用。
實例方法,第一個參數必需要默認傳實例對象,通常習慣用self。
靜態方法,參數沒有要求。
類方法,第一個參數必需要默認傳類,通常習慣用cls。
類方法的意義與做用:
1.類方法用在模擬java定義多個構造函數的狀況。 因爲python類中只能有一個初始化方法,不能按照不一樣的狀況初始化類。參考django Model instance reference 請看下面的代碼。
class Book(object): def __init__(self, title): self.title = title @classmethod def create(cls, title): book = cls(title=title) return book book1 = Book("python") book2 = Book.create("python and django") print(book1.title) print(book2.title)
特別說明,靜態方法也能夠實現上面功能,當靜態方法每次都要寫上類的名字,不方便
2.使用類方法能夠知道是哪一個類在調用該類方法:
class A: @classmethod def f(cls): print(cls) class B(A): pass A.f() B.f() --------------------- <class '__main__.A'> <class '__main__.B'>
調用f的時候,cls參數會根據實際調用的類來傳入,從而你能夠知道究竟是誰在call
靜態方法的意義與做用:
靜態方法有點像函數工具庫的做用,而類成員方法則更接近相似Java面向對象概念中的靜態方法。爲何說是工具函數呢,看下面
class D: def __init__(self): pass def save(self): # 在pycharm中會提示,該方法應該是靜態方法 print('save')
pycharm中會提示,save方法應該是靜態方法,爲何呢?由於在該方法中沒有使用任何跟實例對象和該類有關的東西(變量及方法),就好像一個工具方法,一個類的正常封裝時也不該該把工具函數歸於類,因此,把它設爲靜態,這樣和類關係就不大了。
pycharm中會提示,save方法應該是靜態方法,爲何呢?由於在該方法中沒有使用任何跟實例對象和該類有關的東西(變量及方法),就好像一個工具方法,一個類的正常封裝時也不該該把工具函數歸於類,因此,把它設爲靜態,這樣和類關係就不大了。
添加了@staticmethod以後,就沒有波浪線了,可是這裏注意,雖然靜態方法和類的關係並不大了,可是類名.__dict__裏任然有該靜態方法
靜態方法和類方法使用實例:
類中靜態方法方法調用靜態方法的狀況。下面的代碼,靜態方法調用另外一個靜態方法,若是改用類方法調用靜態方法,可讓cls代替類,讓代碼看起來精簡一些,並且當類名修改時,不用在類定義中修改原來的類名,若是是靜態方法還要修改Foo.averag()的Foo。
class Foo(object): X = 1 Y = 2 @staticmethod def averag(*mixes): return sum(mixes) / len(mixes) @staticmethod def static_method(): return Foo.averag(Foo.X, Foo.Y) @classmethod def class_method(cls): return cls.averag(cls.X, cls.Y) foo = Foo() print(foo.static_method()) print(foo.class_method())
繼承類中的區別:從下面代碼能夠看出,若是子類繼承父類的方法,子類覆蓋了父類的靜態方法,子類的實例繼承了父類的static_method靜態方法,調用該方法,仍是調用的父類的方法和類屬性。子類的實例繼承了父類的class_method類方法,調用該方法,調用的是子類的方法和子類的類屬性。
class Foo(object): X = 1 Y = 2 @staticmethod def averag(*mixes): return sum(mixes) / len(mixes) @staticmethod def static_method(): return Foo.averag(Foo.X, Foo.Y) @classmethod def class_method(cls): return cls.averag(cls.X, cls.Y) class Son(Foo): X = 3 Y = 5 @staticmethod def averag(*mixes): return sum(mixes) / 3 p = Son() print(p.static_method()) print(p.class_method()) # 1.5 # 2.6666666666666665
注意,類方法和靜態方法使用時都是類名(或對象).方法名,不能直接a = 方法名()這樣調用。
參考http://www.cnblogs.com/linhaifeng/articles/8029564.html
exec:三個參數
參數一:字符串形式的命令 參數二:全局做用域(字典形式),若是不指定,默認爲globals(),把運行中全局變量傳入globals 參數三:局部做用域(字典形式),若是不指定,默認爲locals(),把運行中局部變量傳入locals
exec的使用
#能夠把exec命令的執行當成是一個函數的執行,會將執行期間產生的名字存放於局部名稱空間中 g={ 'x':1, 'y':2 } l={} exec(''' global x,z x=100 z=200 m=300 ''',g,l) print(g) #{'x': 100, 'y': 2,'z':200,......} print(l) #{'m': 300}
首先要知道python中萬物皆是對象,類也是對象,元類是類的類,是類的模板
元類是用來控制如何建立類的,正如類是建立對象的模板同樣,而元類的主要目的是爲了控制類的建立行爲
元類的實例化的結果爲咱們用class定義的類,正如類的實例爲對象(f1對象是Foo類的一個實例,Foo類是 type 類的一個實例)
type是python的一個內建元類,用來直接控制生成類,python中任何class定義的類其實都是type類實例化的對象
class Foo: pass f1=Foo() #f1是經過Foo類實例化的對象 #type函數能夠查看類型,也能夠用來查看對象的類,兩者是同樣的 print(type(f1)) # 輸出:<class '__main__.Foo'> 表示,obj 對象由Foo類建立 print(type(Foo)) # 輸出:<class 'type'>
第一步先看類的建立流程:
首先,要知道,解釋器解釋到新變量時會分配名稱空間,類建立也不例外,類體定義的名字都會存放於類的名稱空間中(一個局部的名稱空間),而後對名稱空間進行填充。
第二步調用元類來建立類。
方式一:使用class關鍵字
class Chinese(object): country='China' def __init__(self,name,age): self.name=name self.age=age def talk(self): print('%s is talking' %self.name)
方式二:就是手動模擬class建立類的過程):將建立類的步驟拆分開,手動去建立
#準備工做: #建立類主要分爲三部分 1 類名 2 類的父類(元類) 3 類體 #類名 class_name='Chinese' #類的父類 class_bases=(object,) #類體 class_body=""" country='China' def __init__(self,name,age): self.name=name self.age=age def talk(self): print('%s is talking' %self.name) """
步驟一(先處理類體->名稱空間):類體定義的名字都會存放於類的名稱空間中(一個局部的名稱空間),咱們能夠事先定義一個空字典,而後用exec去執行類體的代碼(exec產生名稱空間的過程與真正的class過程相似,只是後者會將__開頭的屬性變形),生成類的局部名稱空間,即填充字典
class_dic={} exec(class_body,globals(),class_dic) print(class_dic) #{'country': 'China', 'talk': <function talk at 0x101a560c8>, '__init__': <function __init__ at 0x101a56668>}
步驟二:調用元類type(也能夠自定義)來產生類Chinense
Foo=type(class_name,class_bases,class_dic) #實例化type獲得對象Foo,即咱們用class定義的類Foo print(Foo, Foo.country) print(type(Foo)) print(isinstance(Foo,type)) ''' <class '__main__.Chinese'> China#表示由Chinese類建立,這也是type的第一個參數的做用 <class 'type'> True '''
咱們看到,type 接收三個參數:
補充:若Foo類有繼承,即class Foo(Bar):.... 則等同於type('Foo',(Bar,),{})
知識儲備:
類默認繼承object,新式類不寫也默認繼承。
__call__方法在對象被調用時被使用,類也是對象,因此會調用父類(元類)的__call__
類實例化對象實際上經歷三件事:一、產生空對象obj 二、初始化 三、返回obj
代碼以下,講解在代碼下方
class Mymeta(type): #繼承默認元類的一堆屬性 def __init__(self,class_name,class_bases,class_dic): if not class_name.istitle(): raise TypeError('類名首字母必須大寫') super(Mymeta,self).__init__(class_name,class_bases,class_dic) def __call__(self, *args, **kwargs): #self=People print(self,args,kwargs) #<class '__main__.People'> ('egon', 18) {} #一、調用self,即People下的函數__new__,在該函數內完成:一、產生空對象obj 二、初始化 三、返回obj obj=self.__new__(self,*args,**kwargs) #二、必定記得返回obj,由於實例化People(...)取得就是__call__的返回值 return obj class People(object,metaclass=Mymeta): country='China' def __init__(self,name,age): self.name=name self.age=age def talk(self): print('%s is talking' %self.name) def __new__(cls, *args, **kwargs): obj=object.__new__(cls) cls.__init__(obj,*args,**kwargs) return obj obj=People('egon',18) print(obj.__dict__) #{'name': 'egon', 'age': 18} #####調試順序,按行號來##### #1-2-18-19-21-25-2-3-6-33-10-13-29-30-21-22-23-31-16-34
難以理解的幾個點:
1.爲何從25跳到2,由於此時People還未被調用,還沒輪到__call__方法出馬,此時參考第二種type建立類的方式,看看__init__的參數是否是和type參數同樣?這裏就是在普通類被實例化以前先調用初始化函數,初始化完成立馬到33進行普通類(普通類也是對象)的調用,而後進入__call__方法。
2.進入call方法以後的13-29及之後:在33行普通類被調用後進入8行的call方法後,須要完成三個指標,一、產生空對象obj 二、初始化 三、返回obj。在元類的__call__方法裏調用了普通類的__new__方法進行三步走,普通類使用object.__new__(cls)來建立空對象,而後對空對象進行初始化(30-21),最後返回對象,大功告成。
#應用:定製元類實現單例模式 class Mymeta(type): def __init__(self,name,bases,dic): #定義類Mysql時就觸發 self.__instance=None super().__init__(name,bases,dic) def __call__(self, *args, **kwargs): #Mysql(...)時觸發 if not self.__instance: self.__instance=object.__new__(self) #產生對象 self.__init__(self.__instance,*args,**kwargs) #初始化對象 #上述兩步能夠合成下面一步 # self.__instance=super().__call__(*args,**kwargs) return self.__instance class Mysql(metaclass=Mymeta): def __init__(self,host='127.0.0.1',port='3306'): self.host=host self.port=port obj1=Mysql() obj2=Mysql() print(obj1 is obj2)