首頁  >  文章  >  後端開發  >  詳細講解Python中的元類別及其用法

詳細講解Python中的元類別及其用法

巴扎黑
巴扎黑原創
2017-07-20 09:30:331505瀏覽

1、用元類別驗證子類別

每當我們定義新類別的時候,元類別就會運行雅正程式碼,以確保這個新類別符合規定的規格。
Python系統把子類別的class語句處理完畢,就會呼叫元類別的 __new__ 方法。元類別可以透過 __new__ 方法,取得子類別、孫子類別的名稱,父親及屬性。
這樣使得我們不需要將驗證程式碼放在本類別 __init__ 方法中,等到建置物件再驗證。

下例中,定義一個邊數小於3的子類,class語句一結束,元類的驗證程式碼就會拒絕這個class。

class ValidatePolygon(type):
    def __new__(meta, name, bases, class_dict):
        # Don't validate the abstract Polygon class
        if bases != (object,):
            if class_dict['sides'] < 3:
                raise ValueError('Polygons need 3+ sides')
        return type.__new__(meta, name, bases, class_dict)

class Polygon(object, metaclass=ValidatePolygon):
    sides = None  # Specified by subclasses

    @classmethod
    def interior_angles(cls):
        return (cls.sides - 2) * 180

class Triangle(Polygon):
    sides = 3

print(Triangle.interior_angles())

2、用元類別註冊子類別

每次從基底類別繼承子類別時,基底類別的元類別都可以自動執行註冊代碼。
這在需要反向查找 ‘reverse lookup’ 時很有用,使得在簡單標識符和對應的類別之間,建立映射關係。
依然利用的是class語句執行完,自動呼叫元類別的 __new__ 方法。

import json 

registry = {}

def register_class(target_class):
    registry[target_class.__name__] = target_class

def deserialize(data):
    params = json.loads(data)
    name = params['class']
    target_class = registry[name]
    return target_class(*params['args'])


class Meta(type):
    def __new__(meta, name, bases, class_dict):
        cls = type.__new__(meta, name, bases, class_dict)
        register_class(cls)
        return cls


class Serializable(object):
    def __init__(self, *args):
        self.args = args

    def serialize(self):
        return json.dumps({
            'class': self.__class__.__name__,
            'args': self.args,
        })

    def __repr__(self):
        return '%s(%s)' % (
            self.__class__.__name__,
            ', '.join(str(x) for x in self.args))


class RegisteredSerializable(Serializable, metaclass=Meta):
    pass


class Vector3D(RegisteredSerializable):
    def __init__(self, x, y, z):
        super().__init__(x, y, z)
        self.x, self.y, self.z = x, y, z


v3 = Vector3D(10, -7, 3)
print('Before:    ', v3)
data = v3.serialize()
print('Serialized:', data)
print('After:     ', deserialize(data))

print(registry)

3、用元類別註解類別的屬性

使用元類別像是在 class 語句上放置了掛鉤,class語句處理完畢,掛鉤就會立刻觸發。
下列中藉助元類別設定了 Filed.nameFiled.name

class Field(object):
    def __init__(self):
        # These will be assigned by the metaclass.
        self.name = None
        self.internal_name = None
        
    def __get__(self, instance, instance_type):
        if instance is None: return self
        return getattr(instance, self.internal_name, '')

    def __set__(self, instance, value):
        setattr(instance, self.internal_name, value)


class Meta(type):
    def __new__(meta, name, bases, class_dict):
        for key, value in class_dict.items():
            if isinstance(value, Field):
                value.name = key
                value.internal_name = '_' + key
        cls = type.__new__(meta, name, bases, class_dict)
        return cls


class DatabaseRow(object, metaclass=Meta):
    pass


class BetterCustomer(DatabaseRow):
    first_name = Field()
    last_name = Field()
    prefix = Field()
    suffix = Field()


foo = BetterCustomer()
print('Before:', repr(foo.first_name), foo.__dict__)
foo.first_name = 'Euler'
print('After: ', repr(foo.first_name), foo.__dict__)

元類別總結就到這裡,自己也沒有完全理解清楚。
希望對此有深刻理解的pythoner留言。

程式碼來自:

以上是詳細講解Python中的元類別及其用法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn