创建对象后更改对象的类型(在Python中进行类型转换)

时间:2017-02-08 16:32:52

标签: 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的子类中实现自己的函数,并且应该能够在程序运行时在这些子类之间切换。

2 个答案:

答案 0 :(得分:3)

  

A类的哪些属性被转移到对象'obj'   当我更改属性时?什么功能被称为和   什么属性被更新,或者它如何发生obj   将其名称更改为A?

保留所有实例分配的属性 - 也就是说,Python对象通常具有__dict__属性,其中记录所有实例属性 - 保留。对象的类有效地改变为指定的类。 (Python运行时禁止为具有不同内存布局的对象分配__class__。也就是说:新类的所有方法和类属性都可用于实例,并且前一个类的方法或类属性都不存在,就像在这个新类中创建了对象一样。 赋值不会触发任何副作用(如:没有调用特殊方法) 所以 - 对于你正在制作的东西,它“有效”。

  

实现我想要的行为还有哪些更清洁的方法?如果   必要的,我可以摧毁对象obj并重新创建另一个,   但在这种情况下,我想以一般的方式这样做(如obj =   由于代码的其他部分,RoundObject(subclasstype ='Tofu')。

是的,正如您所指出的,这不是最好的做事方式。 你可以拥有一个类层次结构,它具有你需要的不同方法,它们将你的对象作为一个属性 - 并且根据你正在做的事情,你在这个外部层次结构中创建一个新对象 - 并保持你的核心对象的属性不变。这称为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”界面。然后你有几十个用side_length属性做“立方体”事物的类 - zope.interface设施允许你将这几十个类中的任何一个用于任何具有Cubic的对象通过简单地调用所需方法的接口来传递原始对象作为参数。

但是zope.interfaces可能有点难以掌握,因为在近二十年的使用过程中根据需要做了很差的文档(并且在某些时候,人们使用XML文件来声明接口和适配器 - 只是跳过任何处理XML的文档,所以对于较小的项目,你可以像上面那样手动滚动它。

  

我当前的实现已经使用了委托对象,但确实如此   不切实际,因为它隐藏了API中所有有趣的功能   我想在该委托对象中提供(我通常会重复   委托对象的所有功能,但这可以理解混淆   人)。

由于您的实际使用示例很大,确实需要学习和使用zope.interface - 但是如果您想允许访问多个{{1},则需要另外解决完全接口/注册表/适配器系统的问题。 Cube和其他方法上的方法是在Tofu类上实现我在魔术BaseMethods Python方法之上,它允许您在透明的方法中检索被引用对象的方法和属性方式,无需重写:

__getattr__

答案 1 :(得分:2)

borg pattern的变体可能会有所帮助:

class CubicObject(object):
    name = 'Baseclass'

    def __init__(self, __shared_state, sidelength):
        self.__dict__ = __shared_state
        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))

现在,创建共享相同状态的多个实例:

def make_objs(classes, *args, **kwargs):
    __shared_state = {}
    return tuple(cls(__shared_state, *args, **kwargs) for cls in classes)


box, tofu = make_objs(sidelength=1.0, classes=(Box, Tofu))

切换回来并强制它们保持相同的状态:

obj = box
obj.paint()
obj = tofu
obj.eat()
obj.paint() 

sidelength将由两者共享。