Created
December 6, 2020 04:58
-
-
Save kadaliao/d9687b6a995c44a7b70e0fdce32857a3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# 用元类验证子类" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 6, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"<class '__main__.Meta'> Test () {'__module__': '__main__', '__qualname__': 'Test', 'hello': 'world', '__init__': <function Test.__init__ at 0x13f29d268>}\n" | |
] | |
} | |
], | |
"source": [ | |
"class Meta(type):\n", | |
" def __new__(metaclass, name, bases, class_dict):\n", | |
" print(metaclass, name, bases, class_dict)\n", | |
" return type.__new__(metaclass, name, bases, class_dict)\n", | |
"\n", | |
"class Test(metaclass=Meta):\n", | |
" hello = 'world'\n", | |
" def __init__(self, a, b):\n", | |
" self.a = a\n", | |
" self.b = b\n", | |
" print('Initiating Test Instance...')" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 11, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"class ValidatePolygon(type):\n", | |
" def __new__(meta, name, bases, class_dict):\n", | |
" print(meta, name, bases, class_dict)\n", | |
" # 有父类的时候才验证,免得自己也被验证了\n", | |
" if bases:\n", | |
" if class_dict['sides'] < 3:\n", | |
" raise ValueError('Polygons need at least 3 sides')\n", | |
" return type.__new__(meta, name, bases, class_dict)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 12, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"<class '__main__.ValidatePolygon'> Polygon () {'__module__': '__main__', '__qualname__': 'Polygon', 'sides': None, 'interior_angles': <classmethod object at 0x1400c8630>}\n", | |
"<class '__main__.ValidatePolygon'> Triangle (<class '__main__.Polygon'>,) {'__module__': '__main__', '__qualname__': 'Triangle', 'sides': 3}\n", | |
"<class '__main__.ValidatePolygon'> TwoSidesPolygon (<class '__main__.Polygon'>,) {'__module__': '__main__', '__qualname__': 'TwoSidesPolygon', 'sides': 3}\n" | |
] | |
} | |
], | |
"source": [ | |
"class Polygon(metaclass=ValidatePolygon):\n", | |
" sides = None\n", | |
" \n", | |
" @classmethod\n", | |
" def interior_angles(cls):\n", | |
" return (cls.sides - 2) * 180\n", | |
"\n", | |
"class Triangle(Polygon):\n", | |
" sides = 3\n", | |
" pass\n", | |
"\n", | |
"class TwoSidesPolygon(Polygon):\n", | |
" sides = 3" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# 用元类注册子类" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 7, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"import json\n", | |
"\n", | |
"class Serializable:\n", | |
" def __init__(self, *args, **kwargs):\n", | |
" self.args = args\n", | |
" self.kwargs = kwargs\n", | |
" def serialize(self):\n", | |
" return json.dumps({'args': self.args, 'kwargs': self.kwargs})" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 8, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"class Deseiralizable(Serializable):\n", | |
" @classmethod\n", | |
" def deserialize(cls, json_data):\n", | |
" params = json.loads(json_data)\n", | |
" return cls(*params['args'], **params['kwargs'])" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 9, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"class Point2D(Deseiralizable):\n", | |
" def __init__(self, x, y):\n", | |
" super().__init__(x, y)\n", | |
" self.x = x\n", | |
" self.y = y\n", | |
" def __repr__(self):\n", | |
" return 'Point2D(%d, %d)' % (self.x, self.y)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 10, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"{\"args\": [1, 2], \"kwargs\": {}}\n" | |
] | |
} | |
], | |
"source": [ | |
"p = Point2D(1, 2)\n", | |
"data = p.serialize()\n", | |
"print(data)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 11, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"Point2D(1, 2)\n" | |
] | |
} | |
], | |
"source": [ | |
"p2 = Point2D.deserialize(data)\n", | |
"# 不灵活,反序列化的时候需要事先知道类\n", | |
"print(p2)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 12, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"class BetterSerializable:\n", | |
" def __init__(self, *args, **kwargs):\n", | |
" self.args = args\n", | |
" self.kwargs = kwargs\n", | |
" def serialize(self):\n", | |
" data = {'target_class': self.__class__.__name__, 'args': self.args, 'kwargs': self.kwargs}\n", | |
" return json.dumps(data)\n", | |
"\n", | |
"class BetterPoint2D(BetterSerializable):\n", | |
" def __init__(self, x, y):\n", | |
" super().__init__(x, y)\n", | |
" self.x = x\n", | |
" self.y = y\n", | |
" def __repr__(self):\n", | |
" return f'<BetterPoint2D {self.x}, {self.y}>'" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 13, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"{\"target_class\": \"BetterPoint2D\", \"args\": [2, 3], \"kwargs\": {}}\n" | |
] | |
} | |
], | |
"source": [ | |
"bp = BetterPoint2D(2, 3)\n", | |
"print(bp.serialize())" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 14, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"registry = {}\n", | |
"def registry_calss(cls):\n", | |
" registry[cls.__name__] = cls\n", | |
" \n", | |
"def deserialize(json_data):\n", | |
" params = json.loads(json_data)\n", | |
" target_class = params['target_class']\n", | |
" if not target_class:\n", | |
" raise ValueError('Invalid data, target_class required')\n", | |
" if not target_class in registry:\n", | |
" raise ValueError('target_class is not registered')\n", | |
" return registry[target_class](*params['args'], **params['kwargs'])" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 15, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"registry_calss(BetterPoint2D)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 16, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"<BetterPoint2D 3, 4>\n" | |
] | |
} | |
], | |
"source": [ | |
"bp = BetterPoint2D(3, 4)\n", | |
"data = bp.serialize()\n", | |
"bp2 = deserialize(data)\n", | |
"print(bp2)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 25, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"{'BetterPoint2D': <class '__main__.BetterPoint2D'>, 'BetterPoint3D': <class '__main__.BetterPoint3D'>}\n", | |
"{\"target_class\": \"BetterPoint3D\", \"args\": [1, 2, 3], \"kwargs\": {}}\n", | |
"<BetterPoint3D 1, 2, 3>\n" | |
] | |
} | |
], | |
"source": [ | |
"# 如果忘记注册这个类就会报错,用元类来自动注册\n", | |
"class Meta(type):\n", | |
" def __new__(meta, name, bases, class_dict):\n", | |
" cls = type.__new__(meta, name, bases, class_dict)\n", | |
" registry[name] = cls\n", | |
" return cls\n", | |
"\n", | |
"class BetterPoint3D(BetterSerializable, metaclass=Meta):\n", | |
" def __init__(self, x, y, z):\n", | |
" super().__init__(x, y, z)\n", | |
" self.x = x\n", | |
" self.y = y\n", | |
" self.z = z\n", | |
" def __repr__(self):\n", | |
" return f'<BetterPoint3D {self.x}, {self.y}, {self.z}>'\n", | |
" \n", | |
"print(registry)\n", | |
"\n", | |
"bp3 = BetterPoint3D(1, 2, 3)\n", | |
"data = bp3.serialize()\n", | |
"print(data)\n", | |
"bp3 = deserialize(data)\n", | |
"print(bp3)" | |
] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Python 3", | |
"language": "python", | |
"name": "python3" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython3", | |
"version": "3.6.6+" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 2 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment