開發技術--面向對象

開發|面向對象

面向對象的學習,可讓咱們的代碼趨於模塊化,規範化,尤爲是引入設計模式以後,面向對象的優勢就更加明顯~~
但願你們在實際使用中多使用面向對象的思想,enjoy!python

前言

目前全部的文章思想格式都是:知識+情感。
知識:對於全部的知識點的描述。力求不含任何的自我感情色彩。
情感:用我本身的方式,解讀知識點。力求通俗易懂,完美透析知識。

正文

我給本篇文章的定位是回顧本身以前學過的知識點,必定會有我沒有涉及到的知識點。我會將我本身學習面向對象以後使用過的,忘記了的,我以爲本身須要掌握的內容,在正文中體現出來~~
花費了我很多時間,哈哈~~mysql

必備知識

1.面向對象直接這麼說,是很空的一個概念。尤爲是初學者,你說面向對象,根本都不知道是什麼,哪怕是學習了好久的人,你問什麼是面向對象,十之八九的答案都是,面向對象就是寫一個類。。。。terrible~, So, 先一步一步的來了解一下什麼是面向對象吧~linux

2.看面向對象以前,想函數怎麼能夠執行,除了一個一個的向下執行,還能夠嵌套使用吧!在函數中須要增長一個功能,我想須要更改的地方不一出,改完了,你敢肯定沒有影響其餘的功能嗎???答案確定是,回去檢查一下吧!咋們保險一點~sql

3.由此面向對象就引出來了,能夠簡單的理解爲面相對象就是解決只使用函數的擴展性問題。(其實,這裏有一個問題就是,我寫過全是函數的代碼,不知你是否寫過,又真的有相同體會?)數據庫

4.總結一下,面向對象解決了擴展性問題。由此,咋們一塊兒上了面向對象的車,一路開向面向對象的本質~設計模式

面向對象初識

接下來將開始,面向對象的第一次實質審查~~數組

什麼面向對象

一開始面向對象,你們會說Python裏面一切皆對象,是否還記得linux裏面一切皆文件~
補充一下,關於一切皆對象,對象怎麼使用???
一、均可以被引用,x=obj
二、均可以看成函數的參數傳入
三、均可以看成函數的返回值
四、均可以看成容器類的元素,例如: l=[func,time,obj,1]app

注意: Python3統一類與類型(數據類型)的概念,類型就是類
上面這麼說有一點空,看一個代碼,但願你們能夠有所理解~(看到的數據類型都是類)框架

In [10]: print(list)
<class 'list'>

In [11]: print(str)
<class 'str'>

In [12]: print(dict)
<class 'dict'>

對象 與 類的關係?

1.現實生活中,須要先有了一個一個的對象,才能夠將對象分爲哪一類,哪一類,可是,在本身寫程序的時候,須要本身先寫到一個類,才能夠產生一個的對象,產生對象的過程,稱爲實例化類模塊化

2.因此,必定是先有類,才能夠實例化成爲對象

建立類

在建立類以前,須要掌握一點基礎知識;
1.類中具備本身的數據,稱爲數據屬性,或者叫數據
2.類中具備本身的函數,稱爲函數屬性,或者叫方法
3.看下面建立的類,school屬於數據,sleep屬於方法

class Student:
    school = 'Peking University'

    def sleep(self):
        print('Student is sleeping.')

使用類

1.使用類,也就如何實例化出對象,根據上例子,student1=Student(),就能夠實例化對象。

2.實例化類,並執行函數

In [16]: class Student:
    ...:     school = 'Peking University'
    ...:
    ...:     def sleep(self):
    ...:         print('Student is sleeping.')
    ...:

In [17]: student1 = Student()

In [18]: student1.sleep()
Student is sleeping.

注意:代碼加載的時候,就會執行類,產生名稱空間存儲響應的數據,若是類中的數據屬性有another操做,就會被執行。看下面的another操做是print操做。

In [15]: class Student:
    ...:     school = 'Peking University'
    ...:     print(school)
    ...:
    ...:     def sleep(self):
    ...:         print('Student is sleeping.')
    ...:
Peking University

類的屬性與方法

在這裏將詳細的講一下,一個類中的屬性與方法和實例化對象的關係!

1.類的屬性是全部對象共同指向的,共同指向同一內存地址
驗證:

In [20]: class Student:
    ...:     school = 'Peking University'
    ...:
    ...:     def sleep(self):
    ...:         print('Student is sleeping.')
    ...:

In [21]: student1 = Student()

In [22]: student2 = Student()

In [23]: id(student1.school)
Out[23]: 1745287897136

In [24]: id(student2.school)
Out[24]: 1745287897136

2.類的方法是綁定給每個對象的,對象調用的時候自動傳參,bound method 。
驗證:

In [29]: student1.sleep
Out[29]: <bound method Student.sleep of <__main__.Student object at 0x000001965B6F95F8>>

In [30]: student2.sleep
Out[30]: <bound method Student.sleep of <__main__.Student object at 0x000001965B9F2CF8>>

面向對象進階

類的命名空間 dict

1.類的命名空間查看,以下:

In [33]: Student.__dict__
Out[33]:
mappingproxy({'__module__': '__main__',
              'school': 'Peking University',
              'sleep': <function __main__.Student.sleep(self)>,
              '__dict__': <attribute '__dict__' of 'Student' objects>,
              '__weakref__': <attribute '__weakref__' of 'Student' objects>,
              '__doc__': None})

2.能夠執行下面的代碼
類.__ dict __['name]
類.name

In [40]: Student.__dict__['school']
Out[40]: 'Peking University'

In [41]: Student.school
Out[41]: 'Peking University'

3.若是想要執行類中的函數屬性,須要加上一個對象,來看下面,直接執行會報錯,缺乏一個對象的參數。解決,傳入一個對象便可。

In [42]: Student.sleep()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-42-6e9af3d2ac0b> in <module>
----> 1 Student.sleep()

TypeError: sleep() missing 1 required positional argument: 'self'

In [43]: Student.sleep(student1)
Student is sleeping.

init 方法

1.init做爲初始化,能夠爲每個對象定製屬於本身的屬性,只須要在實例化的時候傳入便可。

2.方式一,實例化的時候,直接傳參獲得對象。

In [44]: class Student:
    ...:
    ...:     school = 'Peking University'
    ...:
    ...:     def __init__(self, name, age):
    ...:         self.name = name
    ...:         self.age = age
    ...:
    ...:     def sleep(self):
    ...:         print('%s is sleeping.' % self.name)
    ...:

In [45]: student1 = Student('kate', 18)

In [46]: student1.__dict__
Out[46]: {'name': 'kate', 'age': 18}

3.方式二,在使用方式一的時候,執行了兩步,首先是實例化了對象,其次是傳入參數,看下面,

In [48]: class Student:
    ...:
    ...:     school = 'Peking University'
    ...:
    ...:     def sleep(self):
    ...:         print('Student is sleeping.')
    ...:

In [49]: student2 = Student()

In [50]: student2.name = 'kate'

In [51]: student2.age = 18

In [52]: student2.__dict__
Out[52]: {'name': 'kate', 'age': 18}

bases

bases的做用就是查看繼承的父類有哪些,看下面例子,這個時候出現了一個object的類,可是咱們在書寫類的時候,並無繼承那個類啊,爲何會多了一個object的類,有疑惑的請移步到新式類與經典類,默認Python3中都是繼承object類的,不寫也給你默認繼承。

In [54]: Student.__bases__
Out[54]: (object,)

繼承

1.繼承顧名思義就是繼承,繼承一個或者多個父類,被繼承的類叫作基類(超類),繼承者叫作子類(派生類)。其中,繼承表示的是:什麼是什麼的關係。

2.繼承以後,實例化的對象的屬性查找,須要特別留意。對象的屬性,在不繼承的時候,先從本身屬性查找,再從類中找,找不到就報錯。若是有父類,會去父類中找,找不到報錯。

3.總結: 繼承的查找順序----->先本身找,再去類中找,再去父類中找(父類中找也會出現問題,哪個先找呢,看mro表就知道了~~)

4.實際看一下繼承的書寫,(學生繼承人這個類)

In [55]: class People:
    ...:
    ...:     def __init__(self, name, age):
    ...:         self.name = name
    ...:         self.age = age
    ...:
    ...:     def sleep(self):
    ...:         print('%s is sleeping.' % self.name)
    ...:
    ...:
    ...: class Student(People):
    ...:
    ...:     school = 'Peking University'
    ...:

In [56]: student = Student('kate', 18)

In [57]: student.__dict__
Out[57]: {'name': 'kate', 'age': 18}

派生

1.在繼承中,派生能夠獲得不一樣於父類的屬性與方法,有兩種方式,下面咱們一塊兒分別看一下~

2.指名道姓重用,(不依賴於繼承)

In [65]:
    ...: class People:
    ...:
    ...:     def __init__(self, name, age):
    ...:         self.name = name
    ...:         self.age = age
    ...:
    ...:     def sleep(self):
    ...:         print('%s is sleeping.' % self.name)
    ...:
    ...:
    ...: class Student(People):
    ...:     school = 'Peking University'
    ...:
    ...:     def sleep(self):
    ...:         People.sleep(self)
    ...:         print('Self sleeping....')
    ...:

In [66]: stu = Student('kate', 18)

In [67]: stu.sleep()
kate is sleeping.
Self sleeping....

3.使用super(),依賴於繼承,使用mro表計算實現繼承順序,在mro也存在一個指針,只要是遇到super,就繼續沿着mro表,繼續向後找。

In [68]: class People:
    ...:
    ...:     def __init__(self, name, age):
    ...:         self.name = name
    ...:         self.age = age
    ...:
    ...:     def sleep(self):
    ...:         print('%s is sleeping.' % self.name)
    ...:
    ...:
    ...: class Student(People):
    ...:     school = 'Peking University'
    ...:
    ...:     def sleep(self):
    ...:         super(Student, self).sleep()
    ...:         print('Self sleeping....')
    ...:

In [69]: stu = Student('kate', 18)

In [70]: stu.sleep()
kate is sleeping.
Self sleeping....

4,注意:通常的派生都是使用super()。而且更多的應用場景是在init中繼承父類的內容,也有本身的內容。必定不要忘記子啊繼承查找的時候,遇到super的試試,會繼續向後查找的,就不會回到前面在查找了~~

mro

mro列表,是查找繼承的順序列表,表示了屬性的查找順序。
例子:

In [71]: class A:
    ...:     name = 'A'
    ...:
    ...:
    ...: class B(A):
    ...:     name = "B"
    ...:
    ...:
    ...: class C(A):
    ...:     name = "C"
    ...:
    ...:
    ...: class D(B, C):
    ...:     name = 'D'
    ...:

In [74]: D.mro()
Out[74]: [__main__.D, __main__.B, __main__.C, __main__.A, object]

新式類

1.新式類,並非它有多麼的新,哈~
新式類的討論,須要分python2與Python3來講了,接下來,沒咱們一塊兒來看看吧!

2.Python2的新式類,必須繼承object類,繼承object的類以及他的子類稱爲新式類。

3.Python3,默認全部 的類都是默認繼承object類,因此都是新式類,也就是說Python3中沒有經典類。

4.新式類在mro尋繼承的時候,遵循廣度優先。具體能夠查看本身的mro列表。

經典類

1.經典類是專門來對於Python2來講的,因此經典類說的是在Python2中, 沒有繼承object的類。

2.經典類在繼承查詢的時候,遵循的是深度優先。

組合

1.組合,不須要深刻的瞭解,當一個類不能經過繼承實現關係的時候,有須要本身有本身有什麼關係的時候,就可使用派生。此時就,派生出本身的屬性與方法。

In [58]: class People:
    ...:
    ...:     def __init__(self, name, age):
    ...:         self.name = name
    ...:         self.age = age
    ...:
    ...:     def sleep(self):
    ...:         print('%s is sleeping.' % self.name)
    ...:
    ...:
    ...: class Student(People):
    ...:
    ...:     school = 'Peking University'
    ...:
    ...:
    ...: class Course:
    ...:
    ...:     def __init__(self, name, price):
    ...:         self.name = name
    ...:         self.price = price
    ...:

In [59]: student = Student('kate', 18)

In [60]: math = Course('math', 666)

In [61]: student.course = math

In [62]: student.__dict__
Out[62]: {'name': 'kate', 'age': 18, 'course': <__main__.Course at 0x1965b5c1198>}

In [63]: student.course.name
Out[63]: 'math'

In [64]: student.course.price
Out[64]: 666

2.此時表示給對象綁定了一個對象做爲他的屬性,能夠實現代碼的重用。

多態

Python屬於多態的語言,多態更多的體如今方法的調用上,保證多個類能夠實現調用同一種方法,實現相同的任務。
回想,len(),這個內置函數,在執行的時候,能夠對字符串,對列表,對元祖獲取數據的長度,可是不一樣的數據類型都是不同的類,可是實現了一樣的函數方法。感受本身解釋不清楚了,看代碼吧!

In [1]: a = 'hello'

In [2]: b = (1, 2, 3)

In [3]: c = [4, 5, 6]

In [4]: len(a)
Out[4]: 5

In [5]: len(b)
Out[5]: 3

In [6]: len(c)
Out[6]: 3

In [8]: a.__len__
Out[8]: <method-wrapper '__len__' of str object at 0x000002B0701D12D0>

In [9]: b.__len__
Out[9]: <method-wrapper '__len__' of tuple object at 0x000002B070404EE8>

In [10]: c.__len__
Out[10]: <method-wrapper '__len__' of list object at 0x000002B0703C0588>

鴨子類型

鴨子類型,具體的概念我就不解釋了!我想解釋的是,鴨子模式體現的是多態性,正如上文我所述的len()內置方法。它屬於一個比較理論的知識內容,Python語言自己就具體多態性,因此本身在學習的時候,按需進行學習!

封裝

1.封裝做爲面向對象的特性之一,就我目前的理解,封裝主要是對數據的隱藏,或者是變成本身私有的,因爲Python語言的特性,會在內部自動對槓槓開頭的變量進行相應的處理,因此正好使用這一特性實現數據隱藏。

2.具體的實現,隱藏,使前面加 __,此時的 __name 變爲 **_類名__name在類的加載階段已經變化了,加上前綴的類名。**

3.看下命名空間,應該能夠理解的吧~

In [11]: class People:
    ...:
    ...:     def __init__(self, name, age):
    ...:         self.__name = name
    ...:         self.__age = age
    ...:
    ...:     def sleep(self):
    ...:         print('%s is sleeping.' % self.__name)
    ...:

In [12]: human = People('kate', 18)

In [13]: human.__dict__
Out[13]: {'_People__name': 'kate', '_People__age': 18}

4.封裝能夠將封裝起來的數據,本身進行定義,不須要調用者操做與查看,不只僅是封裝數據,還隔離複雜度

面向對象高階

在面向對象高階,咱們開始一步一步的去尋找類的產生源頭~~追根溯源。

抽象類

1.抽象類的實現,使用的是abc模塊,子哎程序的設計模式中可能會使用到,我在設計模式的文章中寫過~,主要使用的是Python的abc模塊

2.抽象類的特性:** 只能被繼承,不能被實例化。只要是繼承抽象類的類,必須重寫相應的類方法,名字都必須同樣,否則報錯~,這在設計模式的時候,就很好的規範了底層的調用接口。**

3.例子,實現抽象類

In [17]: import abc
    ...:
    ...:
    ...: class People(metaclass=abc.ABCMeta):
    ...:
    ...:     @abc.abstractmethod
    ...:     def sleep(self):
    ...:         pass
    ...:
    ...:
    ...: class Student(People):
    ...:     school = 'Peking University'
    ...:
    ...:     def __init__(self, name, age):
    ...:         self.name = name
    ...:         self.age = age
    ...:
    ...:     def sleep(self):
    ...:         print('Self sleeping....')
    ...:

In [18]: stu = Student('kate', 18)

In [19]: stu.__dict__
Out[19]: {'name': 'kate', 'age': 18}

property

1.property 將訪問函數屬性變爲訪問數據屬性的方法。本來須要加上括號才能夠調用,如今直接就能夠進行調用了。統一調用形式不用加括號進行調。用了

2.能夠聯動設置的參數,修改:name.setter, 刪除:name.deleter

3.代碼實現,瞭解一下,這個property仍是很經常使用的,

In [23]: class People:
    ...:
    ...:     def __init__(self, name):
    ...:         self.__name = name
    ...:
    ...:     @property
    ...:     def name(self):
    ...:         return self.__name
    ...:
    ...:     @name.setter
    ...:     def name(self, new_name):
    ...:         self.__name = new_name
    ...:
    ...:
    ...:     @name.deleter
    ...:     def name(self):
    ...:         print('Error .... no permission to delete..')
    ...:

IIn [24]: human = People('kat')

In [26]: human.name
Out[26]: 'kat'

In [27]: human.name = 'kate'

In [28]: human.name
Out[28]: 'kate'

In [29]: del human.name
Error .... no permission to delete..

classmethod

classmethod屬於綁定到類的方法,類可使用,而且將類做爲第一個參數傳遞進去。此時,能夠回憶一下開始面向對象的時候,關於數據屬性是全部的對象共同擁有的,全部的函數屬性是綁定給每個對象。注意:bond method
看如下關於classmethod的例子:

In [30]: class People:
    ...:
    ...:     def __init__(self, name):
    ...:         self.__name = name
    ...:
    ...:     @classmethod
    ...:     def eat(self):
    ...:         print('eat....')
    ...:
    ...:     def sleep(self):
    ...:         print('sleep...')
    ...:

In [31]: peo = People('kate')

In [32]: peo.sleep
Out[32]: <bound method People.sleep of <__main__.People object at 0x000002B071C3E898>>

In [33]: peo.eat
Out[33]: <bound method People.eat of <class '__main__.People'>>

In [34]: People.sleep
Out[34]: <function __main__.People.sleep(self)>

In [35]: People.eat
Out[35]: <bound method People.eat of <class '__main__.People'>>

staticmethod

staticmethod表示非綁定方法,對象和類均可以使用,通常沒有什麼具體的意思,就是一個普通的函數,我在使用的過程當中也沒有遇到,使用方法相似於classmethod。能夠百度瞭解一下~~

反射

反射,這個使用的次數是不少的,尤爲是在不少開源的框架(Django 的框架中配置文件中的應用類,內部都是使用getattr)中都使用,這個方法很巧妙,能夠字符串與函數關聯起來。便可以經過字符串映射到對象的屬性。

hasattr

hasatrr單獨使用是沒有啥用的,只是作一個判斷,看下對象中是否是存在一個字符串對應的屬性。

getattr

grtattr 是真正幹活的那個,能夠進行字符串與函數屬性的映射,操做起來至關順溜~~,必定要多使用,尤爲是在開放封閉原則的配置文件,配置參數的時候局可使用。

setattr

setattr通常使用的不多,我在上次學習到如今基本上沒有怎麼使用。

delattr

基本不使用的命令了,沒使用過,要是不看到他了,我都忘記了這個了......

反射示例
In [39]: class Ftp:
    ...:
    ...:     def start(self):
    ...:         while 1:
    ...:             inp = input('>>>').strip()
    ...:             cmds = inp.split()
    ...:             if hasattr(self, cmds[0]):
    ...:                 func = getattr(self, cmds[0])
    ...:                 func(cmds)
    ...:
    ...:     def put(self, cmds):
    ...:         print(cmds)
    ...:
    ...:     def get(self, cmds):
    ...:         print(cmds)
    ...:

In [40]: f =Ftp()

In [41]: f.start()
>>>get d.md
['get', 'd.md']
>>>put s.doc
['put', 's.doc']

item系列

item系列,主要是將取出類中的數據屬性,變成字典的方式取出,相應的包含getitem, setitem, delitem,實現屬性的增刪改查。

In [1]: class Student:
   ...:     def __init__(self, name):
   ...:         self.name = name
   ...:
   ...:     def __getitem__(self, item):
   ...:         print('getitem...')
   ...:         return self.__dict__.get(item)
   ...:
   ...:     def __setitem__(self, key, value):
   ...:         print('setitem.... %s' % value)
   ...:         self.__dict__[key] = value
   ...:
   ...:     def __delitem__(self, key):
   ...:         print('delitem....')
   ...:         del self.__dict__[key]
   ...:

In [2]: stu = Student('kate')

In [3]: stu.name
Out[3]: 'kate'

In [4]: stu['name']
getitem...
Out[4]: 'kate'

In [5]: stu['age']
getitem...

In [6]: stu['age'] = 18
setitem.... 18

In [7]: stu['age']
getitem...
Out[7]: 18

In [8]: del stu['age']
delitem....

In [9]: stu.__dict__
Out[9]: {'name': 'kate'}

str

類中定義__str__以後,會在打印print的時候,自動觸發對應的方法,得到該方法的返回值進行輸出,因此str方法必須有返回值,且必須是字符串類型。目前在數據庫表中使用過,So,須要學會使用~

In [10]: class Student:
    ...:
    ...:     def __init__(self, name):
    ...:         self.name = name
    ...:
    ...:     def __str__(self):
    ...:         print('__str__ method')
    ...:         return self.name
    ...:

In [11]: stu = Student('kate')

In [12]: print(stu)
__str__ method
kate

iter

iter方法比較經常使用,在python中實現了__iter__方法的對象是可迭代的。實際上要想讓一個迭代器工做,至少要實現__iter__方法和next方法。

In [20]: class Test():
    ...:     def __init__(self,data):
    ...:         self.data = data
    ...:
    ...:     def __iter__(self):
    ...:         return self
    ...:     def __next__(self):
    ...:         if self.data > 5:
    ...:             raise StopIteration
    ...:         else:
    ...:             self.data+=1
    ...:             return self.data
    ...:

In [21]: t = Test(3)

In [22]: t
Out[22]: <__main__.Test at 0x1d9198679e8>

In [23]: for i in t:
    ...:     print(i)
    ...:
4
5
6

del

1.瞭解del以前,但願你們能夠回一下本身在文件操做的時候,是否是須要打開文件以後必需要關閉文件啊!不想本身的關閉的時候,能夠指定使用with語句進行關閉,可是這裏就出現了一個問題,程序運行結束打開的文件是否是關閉了??

2.已經引出del的做用了,del是實現回收操做系統資源,打開文件是操做系統打開文件,並非本身的英語程序打開,想一下軟件是運行在操做系統上的,操做系統創建在硬件的基礎之上,得到f文件句柄是是應用程序的變量,記住,文件句柄f是應用程序的一個變量,指向的是操做系統的打開文件的資源,調用read函數的時候,是去操做系統的內存取數據,完了,暈了暈了,可自行百度一下~~

3.就算del不實現,在程序結束的時候,程序也會自動的時間回收系統資源。在這裏瞬間讓我想到了numpy中建立一個空數組的時候,會出現非0的空數組,裏面包含的資源就是遺留的內存數據~~

doc

doc方法是建立類的時候,書寫註釋以後就能夠自動觸發該方法執行,而且在這裏面能夠看到對應的註釋內容。

call

調用對象的時候,在實例化的對象的後面加上括號,自動觸發call執行。還記的怎麼觸發init方法執行不,是否是實例化對象的時候觸發執行~

In [24]: class School:
    ...:
    ...:     def __init__(self):
    ...:         print('init....')
    ...:
    ...:     def __call__(self, *args, **kwargs):
    ...:         print('call....')
    ...:
    ...:

In [25]: s = School()
init....

In [26]: s()
call....

單例模式

1.單例模式,須要好好地掌握,在程序的設計模式中存在,在實際的使用中會使用,我推薦兩種實現單例模式的方法。

2.第一種實現單例模式,在類模塊中直接實例化成爲一個對象,在其餘模塊引入調用的時候,直接調用已經實例化的對象便可,此時就實現了調用的單例模式

3.在類的內部進行是否已經有實例化的判斷,若是已經實例化,就須要將以前的實例化對象返回便可。

4.單例模式的優勢,將參數相同的屬性在內存中指向同一個地方,優化的策略。

5.簡單實現單例模式,類內部判斷方法

In [34]: class Mysql:
    ...:     __instance = None
    ...:
    ...:     def __init__(self):
    ...:         self.host = '127.0.0.1'
    ...:         self.port = 3306
    ...:
    ...:     @classmethod
    ...:     def singleton(cls):
    ...:         if not cls.__instance:
    ...:             mysql = cls()
    ...:             cls.__instance = mysql
    ...:         return cls.__instance
    ...:

In [35]: m = Mysql.singleton()

In [36]: n = Mysql.singleton()

In [37]: m
Out[37]: <__main__.Mysql at 0x1d919a8fda0>

In [38]: n
Out[38]: <__main__.Mysql at 0x1d919a8fda0>

元類

終於來到面向對象的高階中的頂級了~~~讓咱們一塊兒,看看類的祖宗~

1.須要瞭解的東西,元類,顧名思義就是說產生類的類都是元類,其實就是type~

2.此時咱們就能夠獲得定義類的兩種方式: 使用class 或者 使用元類

3.使用元類定義類的三要素:** 類名 繼承類 命名空間
類名: 固然是本身起什麼就是什麼了
繼承類: 沒有多繼承,那
object 仍是須要繼承的
命名空間: 這個就須要本身結合以前學過一個
內置函數 exec() ** ,這個函數的執行會產生相應的局部命名空間與全局命名空間。

4.嘗試使用元類定義一個類~,看下面的例子:
注意: 使用exec的時候,若是不將exec內部書寫的字符串數據定格書寫會報錯的,語法不能解析~~~

In [41]: # 定義類的三要素:類名,類的基類們,類的名稱空間
    ...: class_name = 'Chinese'
    ...: class_bases = (object,)
    ...:
    ...: class_body = """
    ...: country='China'
    ...:
    ...: def __init__(self, name):
    ...:     self.name=name
    ...:
    ...: def area(self):
    ...:  print('%s is area' % self.name)
    ...: """
    ...:
    ...: class_dic = {}
    ...: exec(class_body, globals(), class_dic)  # Execute the given source in the context o
    ...: f globals and locals.
    ...:
    ...: Chinese = type(class_name, class_bases, class_dic)  # 元類type定義類,type(name, ba
    ...: ses, dict) -> a new type
    ...:

In [42]: obj1 = Chinese('kate')

In [43]: obj1.name
Out[43]: 'kate'

In [44]: obj1
Out[44]: <__main__.Chinese at 0x1d919881be0>

元類控制類的行爲

使用元類控制類的行爲,包括控制類的建立行爲,與實例化行爲。

1.控制建立行爲能夠實現, 建立的類必須首字母大寫, 必須寫註釋。。。。,而且能夠直接在Mymeta類中添加屬性,子類直接能夠進行調用。

In [49]: class Mymeta(type):
    ...:     def __init__(self, class_name, class_bases, class_dic):
    ...:         self.country = 'china'
    ...:
    ...:         if '__doc__' not in class_dic or not class_dic.get('__doc__').strip():
    ...:             raise TypeError('必須爲類指定文檔註釋')
    ...:
    ...:         if not class_name.istitle():
    ...:             raise TypeError('類名首字母必須大寫')
    ...:
    ...:         super(Mymeta, self).__init__(class_name, class_bases, class_dic)
    ...:
    ...:
    ...: class Student(object, metaclass=Mymeta):
    ...:     """
    ...:     Student class
    ...:     """
    ...:     def __init__(self, name, age):
    ...:         self.name = name
    ...:         self.age = age
    ...:
    ...:
    ...: stu = Student('kate', 18)
    ...:

In [50]: stu.__dict__
Out[50]: {'name': 'kate', 'age': 18}

2.控制類的實例化行爲, 使用的是 觸發__call__方法,這個時候就本身建立一個空對象,並調用init函數,返回對象,實現自動觸發init執行的內容。

In [49]: class Mymeta(type):
    ...:     def __init__(self, class_name, class_bases, class_dic):
    ...:         self.country = 'china'
    ...:
    ...:         if '__doc__' not in class_dic or not class_dic.get('__doc__').strip():
    ...:
    ...:         if not class_name.istitle():
    ...:             raise TypeError('類名首字母必須大寫')
    ...:
    ...:         super(Mymeta, self).__init__(class_name, class_bases, class_dic)
    ...:
    ...:     def __call__(self, *args, **kwargs):
    ...:         obj = object.__new__(self)  # 實例化Student,產生空對象obj
    ...:         self.__init__(obj, *args, **kwargs)  # 調用Student下的函數__init__,初始化o
    ...: bj
    ...:         return obj  # 返回初始化好了的obj
    ...:
    ...:
    ...: class Student(object, metaclass=Mymeta):
    ...:     """
    ...:     Student class
    ...:     """
    ...:
    ...:     def __init__(self, name, age):
    ...:         self.name = name
    ...:         self.age = age
    ...:
    ...:
    ...: stu = Student('kate', 18)

In [52]: stu.__dict__
Out[52]: {'name': 'kate', 'age': 18}

結束語

結束了面向對象的內容,長出一口氣,又陷入了新的迷茫~
面向對象的內容,遠不止我所述的這些,還有不少,尤爲是面向對象的思想,Python的一切皆對象,底層的不少槓槓方法,我我的以爲,學習重在思考與實踐與總結。
但願你們閱讀愉快~

相關文章
相關標籤/搜索