# -*- encoding: utf-8 -*-
import json
'''
第34條: 用元類來註冊子類
關鍵:
1 元類
作用: 在程序中自動註冊類型,對於需要反向查詢的場合有用。
可以在簡單的標示符與對應的類之間建立映射關係。
應用場景:
1) 將python對象表示爲JSON格式的序列化數據,即
將指定對象轉換成JSON字符串。
2) 開發者繼承某個類的時候,程序自動調用registerCLass方法
,並將新的子類註冊好。
原因: 定義完子類的class語句體後,元類可以攔截這個新的子類,
就可以註冊新的類型
2 元類中註冊子類的用法
示例:
class Meta(type):
def __new__(meta, name, bases, class_dict):
cls = type.__new__(meta, name, bases, class_dict)
# 獲取新的類後,註冊該類
registerClass(cls)
return cls
class RegisteredSerializable(BetterSerializable):
__metaclass__ = Meta
class Vector3D(RegisteredSerializable):
def __init__(self, x, y, z):
super(Vector3D, self).__init__(
x, y, z
)
self.x = x
self.y = y
self.z = z
def useMetaclass2():
v3 = Vector3D(1, 2, 3)
print "Before: {value}".format(
value=v3
)
data = v3.serialize()
print "Serialized: {value}".format(
value=data
)
after = deserialize(data)
print "After: {value}, type: {mytype}".format(
value=after,
mytype=type(after)
)
分析:
1) 元類必須繼承萬物之祖 type
2)注意元類的type.__new__(meta, name, bases, class_dict)返回的就是我們的類這個類型
3 總結
1) 類的註冊是很有用的模式,
2) 設置元類中可以返回獲取的新的類的時候,進行註冊
(實際就是設置: <類名, 類> 的字典)
從基類中指定元類爲剛纔編寫的元類,那麼子類繼承基類的時候,
就可以自動註冊子類了
參考:
Effectiv Python 編寫高質量Python代碼的59個有效方法
'''
class Serializable(object):
def __init__(self, *args):
self.args = args
def serialize(self):
return json.dumps({"args": self.args})
class Point2D(Serializable):
def __init__(self, x, y):
super(Point2D, self).__init__(x, y)
self.x = x
self.y = y
def repr(self):
return "Point2D(%d, %d)" % (self.x, self.y)
class Deserializable(Serializable):
@classmethod
def deserialize(cls, jsonData):
params = json.loads(jsonData)
return cls(*params['args'])
class BetterPoint2D(Deserializable):
pass
class BetterSerializable(object):
def __init__(self, *args):
self.args = args
def serialize(self):
# 把序列化對象的類名寫道JSON數據裏面
return json.dumps(
{
'class': self.__class__.__name__,
'args': self.args
}
)
def __repr__(self):
return "args: {args}".format(args=self.args)
registry = {}
'''
建立: <類名,類>的映射,用於後續根據序列化的數據建立類的實例
'''
def registerClass(targetClass):
registry[targetClass.__name__] = targetClass
def deserialize(data):
params = json.loads(data)
name = params['class']
targetClass = registry[name]
return targetClass(*params['args'])
class EvenBetterPoint2D(BetterSerializable):
def __init__(self, x, y):
super(EvenBetterPoint2D, self).__init__(x, y)
self.x = x
self.y = y
def useMetaclass():
point = Point2D(5, 3)
print "Object: {point}".format(point=point)
print "Serialized: {point}".format(point=point.serialize())
point = BetterPoint2D(5, 3)
print "Before: {value}".format(value=point)
data = point.serialize()
print "Serialized: {value}".format(
value=data
)
after = BetterPoint2D.deserialize(data)
print "After: {value}".format(
value=after
)
print "##################### registry class"
registerClass(EvenBetterPoint2D)
point = EvenBetterPoint2D(5, 3)
print "Before: {point}".format(point=point)
data = point.serialize()
print "Serialized: {data}".format(data=data)
after = deserialize(data)
print "After: {after}, type: {value}".format(
after=after, value=type(after))
'''
注意: 元類必須繼承自萬物之祖type
'''
class Meta(type):
def __new__(meta, name, bases, class_dict):
cls = type.__new__(meta, name, bases, class_dict)
# 獲取新的類後,註冊該類
registerClass(cls)
return cls
class RegisteredSerializable(BetterSerializable):
__metaclass__ = Meta
class Vector3D(RegisteredSerializable):
def __init__(self, x, y, z):
super(Vector3D, self).__init__(
x, y, z
)
self.x = x
self.y = y
self.z = z
def useMetaclass2():
v3 = Vector3D(1, 2, 3)
print "Before: {value}".format(
value=v3
)
data = v3.serialize()
print "Serialized: {value}".format(
value=data
)
after = deserialize(data)
print "After: {value}, type: {mytype}".format(
value=after,
mytype=type(after)
)
def process():
useMetaclass2()
if __name__ == "__main__":
process()