在我的项目中,我生成了一个对象 obj
类型 CubicObject
。在运行时,应该允许 GUI 设置更改 obj
的类型至Tofu
或Box
(然后返回),取决于用户想要做什么以及他认为该对象最能代表什么。那么用户应该受益于相应类中实现的特定算法。我正在寻找这种行为的良好实现。我玩过下面的代码,它改变了 __class__
属性,但我确信这是不好的风格。
class CubicObject(object):
name = 'Baseclass'
def __init__(self, sidelength):
self.sidelength = sidelength
class Tofu(CubicObject):
name = 'Class A'
def eat(self):
print("I've eaten a volume of %s. " % (self.sidelength**3))
class Box(CubicObject):
name = 'Class B'
def paint(self):
print("I painted a surface of %s. " % (self.sidelength**2 * 6))
# user only knows the object is vaguely cubic
obj = CubicObject(sidelength=1.0)
# user thinks the object is a Box
obj.__class__ = Box
obj.paint()
# user changes mind and thinks its a piece of Tofu
obj.__class__ = Tofu
obj.eat()
obj.paint() # generates an error as it should, since we cannot paint Tofu
我的两个问题是:
- 类的属性
A
被转移到对象“obj” 当我改变它的__class__
时属性?什么函数被称为
以及更新了哪些属性,或者 obj 是如何发生的 将其名称更改为 A 之一? - 还有哪些其他更简洁的方法可以实现我想要的行为?如果
必要时,我可以销毁该物体
obj
并重新创建另一个, 但在这种情况下,我想以通用的方式这样做(比如obj = RoundObject(subclasstype='Tofu')
因为其他部分 代码)。
根本问题是我允许用户在CubicObject
的子类中实现自己的函数。并且在程序运行时应该能够在这些子类之间进行切换。
最佳答案
What kind properties of class A are transferred to the object 'obj' when I change its class attribute? What functions are called and what attributes are updated, or how else does it happen that obj changes its name to the one of A?
保留所有实例分配的属性 - 也就是说,Python 对象通常具有一个 __dict__
属性,其中记录了所有实例属性 - 该属性被保留。并且对象的类实际上更改为指定的类。 (Python 运行时禁止为具有不同内存布局的对象分配__class__
)。也就是说:新类上的所有方法和类属性都可用于该实例,并且前一个类的方法或类属性都不存在,就好像该对象是在这个新类中创建的一样。
赋值不会触发任何副作用(如:不调用特殊方法)
所以 - 对于你正在制作的东西来说,它“有效”。
What other, cleaner ways exist to implement the behaviour I want? If necessary, I could destroy the object obj and recreate another one, but in this case I would like to do so in a generic manner (like obj = RoundObject(subclasstype='Tofu') because of other parts of the code).
是的,正如您所指出的,这不是最好的做事方式。 您可以拥有一个具有所需不同方法的类层次结构,这些方法将您的对象作为属性 - 并且根据您正在做的事情,您可以在这个外部层次结构中创建一个新对象 - 并保持核心对象及其属性不变。这被称为 Adapter Pattern .
class CubicObject(object):
name = 'Baseclass'
def __init__(self, sidelength):
self.sidelength = sidelength
class BaseMethods(object):
def __init__(self, related):
self.related = related
class Tofu(BaseMethods):
name = 'Class A'
def eat(self):
print("I've eaten a volume of %s. " % (self.related.sidelength**3))
class Box(BaseMethods):
name = 'Class B'
def paint(self):
print("I painted a surface of %s. " % (self.related.sidelength**2 * 6))
# user only knows the object is vaguely cubic
obj = CubicObject(sidelength=1.0)
# user thinks the object is a Box
box = Box(obj)
box.paint()
# user changes mind and thinks its a piece of Tofu
tofu = Tofu(obj)
tofu.eat()
# or simply:
Tofu(obj).eat()
您可以在自己的手动类上滚动它,或者使用众所周知且经过测试的库来实现部分流程自动化的功能。此类库之一是 zope.interface
,这允许您使用适配器模式编写庞大而复杂的系统。因此,您可以拥有数百种不同类型的对象 - 只要它们具有 side_length
属性,您就可以将它们标记为具有“Cubic”接口(interface)。然后你就有了数十个使用 side_length
属性执行“Cube”操作的类 - zope.interface
工具将允许你使用这数十个类中的任何一个任何具有 Cubic 接口(interface)的对象,只需调用所需方法的接口(interface),并将原始对象作为参数传递即可。
但是 zope.interfaces
可能有点难以掌握,因为在近二十年的使用过程中,根据需要编写的文档很差(并且在某些时候,人们诉诸于使用 XML 文件来声明接口(interface)和适配器 - 只需跳过任何处理 XML 的文档),因此对于较小的项目,您可以像上面那样手动滚动它。
My current implementation already uses a delegate object, but it is impractical because it hides all the interesting functions in the API that I want to provide in that delegate object (I usually duplicate all functions of the delegate object, but that understandably confuses people).
由于您的实际使用示例很大,因此确实需要学习和使用 zope.interface
- 但如果您想允许访问,则可以使用完全接口(interface)/注册表/适配器系统的另一种解决方法Tofu 和其他方法上的几个 Cube
方法是在我在神奇的 __getattr__
Python 之上的 BaseMethods
类上实现的方法允许您以透明的方式检索所引用对象的方法和属性,而无需重新编写:
class BaseMethods(object):
def __init__(self, related):
self.related = related
def __getattr__(self, attr):
return getattr(self.related, attr)
关于python - 创建对象后更改其类型(Python 中的类型转换),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42118633/