python - 创建对象后更改其类型(Python 中的类型转换)

标签 python class object dynamic-programming metaclass

在我的项目中,我生成了一个对象 obj类型 CubicObject 。在运行时,应该允许 GUI 设置更改 obj 的类型至TofuBox (然后返回),取决于用户想要做什么以及他认为该对象最能代表什么。那么用户应该受益于相应类中实现的特定算法。我正在寻找这种行为的良好实现。我玩过下面的代码,它改变了 __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/

相关文章:

python - 训练 Tesseract OCR 消除歧义

c++ - C++ 中的罗马数字输出总是 "-858993460",不知道为什么?

ios - 如何在同一个swift框架中访问其他类?

php - 如何重写异常类而不显示 fatal error

java - 在java中获取链表中的对象与数组中的对象

python - 如何将 Librosa 光谱图保存为特定大小的图像?

python - 在 Python 中设计长链

Python,如何在多个无限循环之间切换?

JavaScript 字符串和数组中的对象

Ruby object to_s object id 的编码是什么?