1. 程式人生 > >python中面向物件元類的自定義用法

python中面向物件元類的自定義用法

面向物件中的常用方法

1.instance 和 issubclass

instance :判斷兩個物件是不是一類

issubclass :判斷某個類是不是另一個類的子類

#兩個常用方法的使用
class Person:
    passclass Student(Person):
    pass
​
stu = Student()
print(isinstance(stu, Student))
print(issubclass(Student,Person))
True
True  ##輸出的結果是布林值。

 

2.反射

反射,其實就是反省。簡單來講就是物件要具備一種修正錯誤的能力。

#四種方法:
hasattr      #判斷是否存在屬性
getattr      #獲取某個屬性的值
setattr      #設定某個屬性的值
delattr      #刪除某個屬性
#這四種方法的共同點,都是通過字串來操作屬性,通過字串操作。
#示例:反射屬性
class Student:
    def __init__(self, name, sex, age):
        self.name = name
        self.age = age
        self.sex = sex
    def study(self):
        print
('student is studying') deng = Student('deng', 'male', 25) #當我們獲取一個物件,但是不清楚物件內部細節,就需要用反射。 def test(obj): if hasattr(obj, 'name'): print(getattr(obj, 'name', "no 'name' attribute")) ​ test(deng) ​ #示例:反射方法 #通過反射方法的方式為物件增加一個方法,但是注意,這樣增加的方法就是一個普通函式,不會自動傳值。 res = getattr(deng, 'study', None)
print(res) res() def run(obj): print('student is running') ​ setattr(deng,'run',None) res1 = getattr(deng, 'run',None) run(deng) ​ #deng #<bound method Student.study of <__main__.Student object at 0x00000272D8DB8C50>> #student is studying #student is running

 

3._ _str _ _內建方法

當我們需要自定義顯示內容時,就需要實現 _ _str _ _方法

#該方法必須返回一個字串,返回是什麼,打印出來就是什麼。
class Test:
    def __init__(self,name):
        self.name = name
    def __str__(self):
        print('str run...')
        return self.name
​
t = Test('ming')
print(t)

 

其實,在我們將一個物件轉換為字串時,本質就是在呼叫這個物件的_ str _ _方法。

 

4.  _ _del _ _內建方法

該方法在物件(程式,檔案,等等)被從記憶體中刪除時會自動執行該方法。

class Student:
    def __del__(self):
        print('物件被刪除。。。')
​
stu = Student()  #建立stu物件
#當該stu物件建立完成後該程式就執行結束,就會執行del方法。
#觸發__del__有兩種:
    1.程式自動執行結束
    2.手動刪除,會立即執行__del__
#示例:
class Student:
    def __del__(self):
        print('物件被刪除。。。')
​
stu = Student()
del stu     #在這就會列印,但這是程式並未結束
import time
time.sleep(5) 
什麼時候使用它
在python中 有自動記憶體管理機制 所以 python自己建立的資料 不需要我們做任何操作
但是有一種情況 我們使用python打開了一個不屬於python管理的資料
比如打開了一個檔案  這個檔案一定是作業系統在開啟 會佔用系統記憶體  而python直譯器無法作業系統記憶體的
所以 當你的python直譯器執行結束後  檔案依然處於開啟狀態  這時候就需要使用__del__來關閉系統資源
​
簡單地說 當程式執行結束時 需要做一些清理操作 就使用__del__
​
__del__也稱之為 解構函式
​
分析構造 並拆除這個物件

 

 

5.exec方法

該方法是解析執行python程式碼(字串型別) 並且將得到的名稱 儲存到制定的名稱空間  直譯器內部也是呼叫它來執行程式碼。直譯器內部也是將程式碼看做字串。

該方法有三個引數:

引數一    需要一個字串物件, 表示需要被執行的python語句

引數二    是一個字典,表示全域性名稱空間

引數三    是一個字典,表示區域性名稱空間

#示例:
globals_dic = {}
locals_dic = {}
exec('''
a = 1  
if a >1: 
    print('ming') 
else:
    print('deng')''', globals_dic, locals_dic)

 

#注意:

1.如果同時制定了全域性和區域性的,則會將字串中包含的名稱解析後存到區域性名稱空間。

2.如果只傳了一個傳引數 則 將字串中包含名稱 解析後存到全域性中。

 

6.元類

  • 使用class可以發現,類其實是type型別的例項(物件)。

  • 一切皆物件

  • 元類是指 用於產生類的類  type就是元類

class Student:
    def study(self):
        print('studying')
print(type(Student))

 

總結:

1.類由type例項化產生

2.我們可以使用type產生一個類

3.一個類由類名,類的父類,類的名稱空間組成。

type類例項化可以得到類,類例項化可以得到物件。

 

7._ _ call _ _ 內建方法

_ _ call _ _呼叫的意思,在物件被呼叫的時候執行該方法(執行該物件所屬的類)

#示例:
class Person:
    def __call__(self, *args, **kwargs):
        print('call  running')
p = Person()   #建立物件不會執行call內建方法
p()   # call  running  物件被呼叫時會自動執行該方法。

 

自定義元類 的目的

1.可以通過call 來控制物件的建立過程
2.可用控制類的建立過程

自定義一個元類(元類也是一個類),但該類需繼承元類type。

#1.通過__call__來控制物件的建立過程
#1.建立一個元類(需要繼承type)
#2.覆蓋__call__方法,會將正在例項化物件的類轉化為傳入的引數
#3.在新的__call__方法中,需要按下面編寫,然後再加你需要的控制邏輯即可。
class MyMeta(type):
    #self表示建立物件的那個類,*args, **kwargs引數
    def __call__(self, *args, **kwargs):
        print('MyMeta中的call')
        #下面三步是固定的
        #1.建立物件
        obj = object.__new__(self)
        #2.呼叫初始化方法
        self.__init__(obj,*args,**kwargs)
        #3.得到一個完整的物件
        return obj
#先修改Person的元類為MyMeta
class Person(metaclass=MyMeta):
    def __init__(self,name ,age):
        self.name = name
        self.age = age
    def __call__(self, *args, **kwargs):
        print('call is running...')
deng = Person('deng', 26)
print(deng)
deng()
####
MyMeta中的call
<__main__.Person object at 0x00000251588D89B0>
call is running...
​
​
#2.通過元類控制類的建立過程:
#1.建立一個元類(需要繼承type)
#2.覆蓋__init__方法,該方法會新建類的物件,類名,父類名,名稱空間,可以利用這些資訊做處理
#3.對於需要控制的類,需要指定metaclass為上面元類
class MyMeta(type):
    def __init__(self, class_name, bases, namespace):
        print('=======')
        #控制類名必須大寫
        if not class_name.istitle():
            print('類名必須大寫開頭。。。')
            #該程式碼主動丟擲異常
            raise TypeError('類名,開頭必須大寫開頭。。。')
        if not self.__doc__:
            raise TypeError
        pass
class Student(metaclass=MyMeta):
    '''
    這是文件註釋,可以通過__doc__獲取
    '''
    #在類的__init__中可以控制該類物件的建立過程
    def __init__(self, name):
        print('+++++++++++++')
        print(self.__doc__)
        self.name = name
print(Student.__doc__)

 

8.單例模式

單例   一種設計模式

單個物件,一個類如果只有一個例項,那麼該類稱之為單例

為什麼需要單例?

class MyMeta(type):
    obj = None
    def __call__(self, *args, **kwargs):
        if not MyMeta.obj:
            obj = object.__new__(self)
            self.__init__(obj, *args, **kwargs)
            MyMeta.obj = obj
        return MyMeta.obj
class Printer(metaclass=MyMeta):
    '''
    這是一個單例類,請不要直接例項化,使用get方法獲取例項
    '''
    obj = None
    def __init__(self, name, type):
        self.name = name
        self.type = type
    def printing(self, text):
        print('正在列印%s' % text)
    @classmethod
    def get_printer(cls):
        if not cls.obj:
            obj = cls('ec001', 'sharp')
            cls.obj = obj
            print('建立新物件')
        return cls.obj
​
p = Printer.get_printer()
print(p.name)
print(p)
p1 = Printer('ming','002')
print(p1.name)
print(p1)

 

 

總結:

單例的優點:就是可以優化記憶體,減少重複物件的產生佔用記憶體。