自动将对象从 class 转换为 subclass
convert object from class to subclass automatically
我必须解决以下问题。我有一个数据输入,其中定义了一个类型(下例中为动物)。基于这种类型,我需要不同的子类,因为我想根据类型具有不同的属性。这是一个例子:
class pet:
def __init__(self, dict):
self.name = dict['name']
self.type = dict['type']
class dog(pet):
def __init__(self, dict):
pet.__init__(self, dict)
self.weight = dict['weight']
class cat(pet):
def __init__(self, dict):
pet.__init__(self, dict)
self.color = dict['color']
if __name__ == '__main__':
pet1 = {'name': 'Harry', 'type': 'dog', 'weight': 100}
pet2 = {'name': 'Sally', 'type': 'cat', 'color': 'blue'}
mypet1 = pet(pet1)
mypet2 = pet(pet2)
我想根据类型参数自动将宠物对象分别转换为狗或猫。最后一点很关键,因为会有很多宠物,我无法手读类型并显式使用相应的子类。
有办法吗?
提前致谢
假设你在对象中有 str 类型(在你的例子中是类型):
def pet_factory(pet_obj):
return globals()[pet_obj['type']](pet_obj)
mypet1 = pet_factory(pet1)
不确定使用全局变量是否正确
你想要的有时被称为 虚拟构造函数 因为子 class 实例是由基础 class 构造函数创建的。这通常是通过使用某种 "factory" 函数来处理的。
但是,我不喜欢大多数工厂函数实现的一件事是,它们的实现方式通常需要每次都手动修改工厂函数subclass 添加到 class 层级中。更好的实现可以将其简化为简单地调用其他 "helper" 函数来注册每个子 class.
在 Python 中,可以通过重写基 class 的默认 __new__()
方法(有效地使其成为静态工厂函数)来实现这样的功能。然后,在该方法中,可以使用 class 对象的 __subclasses__()
方法来查找它们,而无需首先手动调用某些 "register" 辅助方法。因此,将 subclass 添加到虚拟构造的 class 层次结构中基本上是自动的。
以下是如何将这些概念应用到问题中的示例 class。另请注意,我还修改了您的代码,使其更严格地遵循 PEP 8 - Style Guide for Python Code 准则。
class Pet:
class UnknownType(Exception): pass # Custom Exception subclass.
def __init__(self, dictionary):
self.name = dictionary['name']
self.type = dictionary['type']
@classmethod
def _get_all_subclasses(cls):
""" Recursive generator of all subclasses of a class. """
for subclass in cls.__subclasses__():
yield subclass
for subclass in subclass._get_all_subclasses():
yield subclass
def __new__(cls, dictionary):
""" Create instance of appropriate subclass using string
value of 'type' in dictionary.
"""
kind = dictionary['type']
for subclass in cls._get_all_subclasses():
if subclass.kind == kind:
# Using "object" base class method avoids recursion here.
return object.__new__(subclass)
else: # no subclass with matching type found.
raise Pet.UnknownType(
'type "{}" is not recognized'.format(kind))
class Dog(Pet):
kind = 'Dog'
def __init__(self, dictionary):
super().__init__(dictionary)
self.weight = dictionary['weight']
class Cat(Pet):
kind = 'Cat'
def __init__(self, dictionary):
super().__init__(dictionary)
self.color = dictionary['color']
if __name__ == '__main__':
pet1 = {'name': 'Harry', 'type': 'Dog', 'weight': 100}
pet2 = {'name': 'Sally', 'type': 'Cat', 'color': 'blue'}
pet3 = {'name': 'Joe', 'type': 'Frog', 'eyecolor': 'brown'}
mypet1 = Pet(pet1)
mypet2 = Pet(pet2)
print(mypet1.__class__.__name__) # -> Dog
print(mypet2.__class__.__name__) # -> Cat
# Example showing use of custom Exception subclass.
try:
mypet3 = Pet(pet3)
except Pet.UnknownType as exc:
print('Error occurred:', exc)
# -> Error occurred: type "Frog" is not recognized
这基本上只是我对 .
回答中代码的改编
您可以为 pet
创建一个 class 方法,该方法遍历其子 class 以找到名称与给定 type
匹配的方法,然后实例化具有给定属性 dict:
的 subclass
class pet:
@classmethod
def get_pet(cls, attributes):
for c in cls.__subclasses__():
if c.__name__ == attributes['type']:
return c(attributes)
这样:
dog = pet.get_pet(pet1)
print(dog.__class__.__name__, dog.name, dog.type, dog.weight)
将输出:
dog Harry dog 100
首先,不要只是传递 dict
s;隐藏了实际需要的参数,并丑化了代码。为每个初始化程序识别的参数使用常规名称,将其余的捕获为 **kwargs
并将它们向上传递到初始化程序链。
其次,为了实现您的目标,在 Pet
上制作一个替代构造函数作为 classmethod
并使用它。 classmethod
可以 return 一个新对象,并且它们不限于对已创建的对象进行操作,例如 __init__
(__new__
可以替换 __init__
来达到类似的效果,但它更繁琐,而且通常不太明显):
class pet:
def __init__(self, name, type):
self.name = name
self.type = type
@classmethod
def fromtype(cls, type, **kwargs):
for c in cls.__subclasses__():
if c.__name__ == type:
break
else:
raise ValueError("Unknown type: {!r}".format(type))
return c(type=type, **kwargs)
class dog(pet):
def __init__(self, weight, **kwargs):
pet.__init__(self, **kwargs)
self.weight = weight
class cat(pet):
def __init__(self, color, **kwargs):
pet.__init__(self, **kwargs)
self.color = color
用法仅略有变化,来自:
mypet1 = pet(pet1)
mypet2 = pet(pet2)
至:
mypet1 = pet.fromtype(**pet1)
mypet2 = pet.fromtype(**pet2)
并且当您需要直接构造对象时,您可以将普通参数传递给普通构造函数,而不是构造一个未使用的 dict
。
我必须解决以下问题。我有一个数据输入,其中定义了一个类型(下例中为动物)。基于这种类型,我需要不同的子类,因为我想根据类型具有不同的属性。这是一个例子:
class pet:
def __init__(self, dict):
self.name = dict['name']
self.type = dict['type']
class dog(pet):
def __init__(self, dict):
pet.__init__(self, dict)
self.weight = dict['weight']
class cat(pet):
def __init__(self, dict):
pet.__init__(self, dict)
self.color = dict['color']
if __name__ == '__main__':
pet1 = {'name': 'Harry', 'type': 'dog', 'weight': 100}
pet2 = {'name': 'Sally', 'type': 'cat', 'color': 'blue'}
mypet1 = pet(pet1)
mypet2 = pet(pet2)
我想根据类型参数自动将宠物对象分别转换为狗或猫。最后一点很关键,因为会有很多宠物,我无法手读类型并显式使用相应的子类。 有办法吗?
提前致谢
假设你在对象中有 str 类型(在你的例子中是类型):
def pet_factory(pet_obj):
return globals()[pet_obj['type']](pet_obj)
mypet1 = pet_factory(pet1)
不确定使用全局变量是否正确
你想要的有时被称为 虚拟构造函数 因为子 class 实例是由基础 class 构造函数创建的。这通常是通过使用某种 "factory" 函数来处理的。
但是,我不喜欢大多数工厂函数实现的一件事是,它们的实现方式通常需要每次都手动修改工厂函数subclass 添加到 class 层级中。更好的实现可以将其简化为简单地调用其他 "helper" 函数来注册每个子 class.
在 Python 中,可以通过重写基 class 的默认 __new__()
方法(有效地使其成为静态工厂函数)来实现这样的功能。然后,在该方法中,可以使用 class 对象的 __subclasses__()
方法来查找它们,而无需首先手动调用某些 "register" 辅助方法。因此,将 subclass 添加到虚拟构造的 class 层次结构中基本上是自动的。
以下是如何将这些概念应用到问题中的示例 class。另请注意,我还修改了您的代码,使其更严格地遵循 PEP 8 - Style Guide for Python Code 准则。
class Pet:
class UnknownType(Exception): pass # Custom Exception subclass.
def __init__(self, dictionary):
self.name = dictionary['name']
self.type = dictionary['type']
@classmethod
def _get_all_subclasses(cls):
""" Recursive generator of all subclasses of a class. """
for subclass in cls.__subclasses__():
yield subclass
for subclass in subclass._get_all_subclasses():
yield subclass
def __new__(cls, dictionary):
""" Create instance of appropriate subclass using string
value of 'type' in dictionary.
"""
kind = dictionary['type']
for subclass in cls._get_all_subclasses():
if subclass.kind == kind:
# Using "object" base class method avoids recursion here.
return object.__new__(subclass)
else: # no subclass with matching type found.
raise Pet.UnknownType(
'type "{}" is not recognized'.format(kind))
class Dog(Pet):
kind = 'Dog'
def __init__(self, dictionary):
super().__init__(dictionary)
self.weight = dictionary['weight']
class Cat(Pet):
kind = 'Cat'
def __init__(self, dictionary):
super().__init__(dictionary)
self.color = dictionary['color']
if __name__ == '__main__':
pet1 = {'name': 'Harry', 'type': 'Dog', 'weight': 100}
pet2 = {'name': 'Sally', 'type': 'Cat', 'color': 'blue'}
pet3 = {'name': 'Joe', 'type': 'Frog', 'eyecolor': 'brown'}
mypet1 = Pet(pet1)
mypet2 = Pet(pet2)
print(mypet1.__class__.__name__) # -> Dog
print(mypet2.__class__.__name__) # -> Cat
# Example showing use of custom Exception subclass.
try:
mypet3 = Pet(pet3)
except Pet.UnknownType as exc:
print('Error occurred:', exc)
# -> Error occurred: type "Frog" is not recognized
这基本上只是我对
您可以为 pet
创建一个 class 方法,该方法遍历其子 class 以找到名称与给定 type
匹配的方法,然后实例化具有给定属性 dict:
class pet:
@classmethod
def get_pet(cls, attributes):
for c in cls.__subclasses__():
if c.__name__ == attributes['type']:
return c(attributes)
这样:
dog = pet.get_pet(pet1)
print(dog.__class__.__name__, dog.name, dog.type, dog.weight)
将输出:
dog Harry dog 100
首先,不要只是传递 dict
s;隐藏了实际需要的参数,并丑化了代码。为每个初始化程序识别的参数使用常规名称,将其余的捕获为 **kwargs
并将它们向上传递到初始化程序链。
其次,为了实现您的目标,在 Pet
上制作一个替代构造函数作为 classmethod
并使用它。 classmethod
可以 return 一个新对象,并且它们不限于对已创建的对象进行操作,例如 __init__
(__new__
可以替换 __init__
来达到类似的效果,但它更繁琐,而且通常不太明显):
class pet:
def __init__(self, name, type):
self.name = name
self.type = type
@classmethod
def fromtype(cls, type, **kwargs):
for c in cls.__subclasses__():
if c.__name__ == type:
break
else:
raise ValueError("Unknown type: {!r}".format(type))
return c(type=type, **kwargs)
class dog(pet):
def __init__(self, weight, **kwargs):
pet.__init__(self, **kwargs)
self.weight = weight
class cat(pet):
def __init__(self, color, **kwargs):
pet.__init__(self, **kwargs)
self.color = color
用法仅略有变化,来自:
mypet1 = pet(pet1)
mypet2 = pet(pet2)
至:
mypet1 = pet.fromtype(**pet1)
mypet2 = pet.fromtype(**pet2)
并且当您需要直接构造对象时,您可以将普通参数传递给普通构造函数,而不是构造一个未使用的 dict
。