python元類深刻理解

1.python 中的類

在python中,類也是一個對象,只不過這個對象擁有生成實例的能力,咱們通常使用class XXX來定義一個類,在python解釋器執行到這個地方的時候會自動建立出這個對象,python也爲咱們提供了手動建立類的方法,type()。type()這個方法對咱們來講並不陌生,咱們所熟知的用法是:class = type(instance),當傳入一個參數時,type()返回這個參數的類。而今天咱們要用到的是type的另外一個功能。type("classname",(object,),{"name":"jiao"})。當給type傳入三個參數時,就是一個手動建立類的方式。python

class A():
    def __init__(self,name):
        self.name = name
        print("建立了一個實例")

a = type("a",(A,),{"name":"jiao"}) 
print(a)              #<class '__main__.a'>
print(a.name)          #jiao
print(a("jiang"))      #建立了一個實例  
                        #<__main__.a object at 0x00000280A973AA58>

 

type接收三個參數分別是:緩存

classname: 要建立的class 的名稱app

object:要建立類的父類所組成的元組函數

sttr_dict: 要建立類的屬性spa

type返回一個class,咱們接收並賦值到一個變量上,如今這個變量就指向咱們所建立的類,咱們能夠經過這個變量來使用類。code

 

2.python 中的type

在python 中,幾乎全部的東西都是對象,這包括整數、字符串、函數以及類。它們所有都是對象,並且它們都是從一個類建立而來——typeorm

3.__metaclass__屬性

python在建立類時,會按照以下的流程進行:對象

 

Foo中有__metaclass__這個屬性嗎?若是是,Python會在內存中經過__metaclass__建立一個名字爲Foo的類對象(我說的是類對象,請緊跟個人思路)。若是Python沒有找到__metaclass__,它會繼續在Bar(父類)中尋找__metaclass__屬性,並嘗試作和前面一樣的操做。若是Python在任何父類中都找不到__metaclass__,它就會在模塊層次中去尋找__metaclass__,並嘗試作一樣的操做。若是仍是找不到__metaclass__,Python就會用內置的type來建立這個類對象。blog

那麼在__metaclass__中放置什麼樣的代碼能夠建立類呢?type,或者任何使用到type或者子類化type的東東均可以。內存

 

4.自定義元類

class UpperAttrMetaClass(type):
    def __new__(cls,class_name,class_parents,class_attr, *args, **kwargs):
        print("__new__")
        class_attr['name'] = "jiao"
        return type.__new__(cls,class_name,class_parents,class_attr)

    def __init__(self,*args,**kwargs):
        print("__init__")
        super().__init__(*args, **kwargs)
        self.__cache = {}

    def __call__(self, *args, **kwargs):
        print("__call__")
        if args in self.__cache:
            return self.__cache[args]
        else:
            obj = super().__call__(*args)
            self.__cache[args] = obj
            return obj


class A(metaclass=UpperAttrMetaClass):
    def __init__(self,name):
        self.name = name
        print("a.__init__")

 

 

5.類的建立流程

1.元類的__new__(),返回建立好的類。當咱們想要改變建立方式的時候就要重寫這個方法。

2.元類的__init__(),初始化一些類的屬性

 

6.實例建立流程

1.元類的__call__(),建立一個實例時,首先調用這個方法,返回建立好的實例,因此咱們能夠經過改寫這個方法來改變實例建立過程,好比實現單例模式

2.類的__init__(),初始化實例屬性

 

7.元類的應用

1.單例模式

class Singleton(type):
    def __init__(cls,*args,**kwargs):
        cls.__instance = None
        super().__init__(*args,**kwargs)

    def __call__(cls, *args, **kwargs):
        if cls.__instance is None:
            cls.__instance = super().__call__(*args,**kwargs)
            return cls.__instance
        else:
            return cls.__instance

class Spam(metaclass=Singleton):
    def __init__(self):
        print("Creating Spam")

 

2.緩存模式
import weakref

class Cached(type):
    def __init__(cls,*args,**kwargs):
        super().__init__(*args,**kwargs)
        cls.__cache = weakref.WeakValueDictionary()

    def __call__(cls, *args, **kwargs):
        if args in cls.__cache:
            return cls.__cache[args]
        else:
            obj = super().__call__(*args)
            cls.__cache[args] = obj
            return obj


class Spams(metaclass=Cached):
    def __init__(self,name):
        print("Creating Spam({!r})".format(name))
        self.name = name

 

3.獲取屬性的定義順序

 

能過獲取到屬性的定義順序,咱們就能夠經過簡單的方法實現屬性到數據的映射,能夠更加簡單的將類中的屬性數據化。

from collections import OrderedDict

class Typed:
    _excepted_type = type(None)

    def __init__(self,name=None):
        self._name = name

    def __set__(self, instance, value):
        if not isinstance(value,self._excepted_type):
            raise TypeError("Excepted"+str(self._excepted_type))
        instance.__dict__[self._name] = value

class Integer(Typed):
    _excepted_type = int

class Float(Typed):
    _excepted_type = float

class String(Typed):
    _excepted_type = str

class OrderedMeta(type):

    def __new__(cls, clsname,bases,clsdict):
        d = dict(clsdict)
        order = []
        for name,value in clsdict.items():
            if isinstance(value,Typed):
                value._name = name
                order.append(name)
                d['_order'] = order
       return type.__new__(cls,clsname,bases,d)

    @classmethod
    def __prepare__(metacls, name, bases):
        return OrderedDict()

#注:__prepare__該方法會在類定義一開始的時候調用,調用時以類名和基類名稱做爲參數,它必須返回一個映射對象,供處理類定義體時調用


#eg.
class Structure(metaclass=OrderedMeta):

    def as_csv(self):
        return ','.join(str(getattr(self,name)) for name in self._order)

class Stock(metaclass=OrderedMeta):
    name = String()
    shares = Integer()
    price = Float()
    def __init__(self,name,shares,price):
        self.name = name
        self.shares = shares
        self.price = price

s = Stock("haha",23,23.3)
print(s.name)
s = Stock(34,23,34)
# print(s.as_csv())

 

 

8.小結

元類主要就是在類和實例建立的時候發揮做用,來實現一些功能。

相關文章
相關標籤/搜索