创建对象后更改对象的类型(Python中的类型转换) [英] Change type of an object after its creation (typecasting in Python)

查看:69
本文介绍了创建对象后更改对象的类型(Python中的类型转换)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的项目中,生成类型为 CubicObject 的对象 obj .在运行时,应允许GUI设置将 obj 的类型更改为 Tofu Box (并返回),具体取决于用户的类型想要做什么以及他认为最能代表该对象的是什么.然后,用户应受益于在相应类中实现的特定算法.我正在寻找这种行为的一个很好的实现.我玩过下面的代码,该代码更改了 __ class __ 属性,但是我确信这是不好的风格.

In my project, I generate an object obj of type CubicObject. At runtime, a GUI setting should be allowed to change the type of obj to Tofu or Box (and back), depending on what the user wants to do and what (s)he thinks the object is best represented by. Then the user should benefit from specific algorithms implemented in the corresponding classes. I am looking for a nice implementation of this behaviour. I have played with the code below, which changes the __class__ attribute, but I am sure that this is bad style.

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'),因为代码).
  • 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?
  • 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).

潜在的问题是,我允许用户在 CubicObject 的子类中实现自己的功能,并且应该能够在程序运行时在这些子类之间进行切换.

The underlying problem is that I allow the user to implement own functions in subclasses of CubicObject and that one should be able to switch between these subclasses while the program is running.

推荐答案

将类A的哪种属性传递给对象'obj'当我更改其 class 属性时?所谓的功能是什么更新了哪些属性,或者obj如何进行其他操作将其名称更改为A吗?

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 __ ).也就是说:新类上的所有方法和类属性都可用于该实例,而先前类的方法或类属性均不存在,就好像该对象是在此新类中创建的一样.分配不会触发任何副作用(例如:没有调用特殊方法)所以-对于您正在做的事情,它有效".

All instance assigned attributes are kept - that is, Python objects normally have a __dict__ attribute where all instance attributes are recorded - that is kept. And the object's class effectively changes to the one assigned. (Python runtime forbids __class__ assignment for objects which have a different memory layout). That is: all methods and class attributes on the new class are available for the instance, and none of the methods or class attributes of the previous class are there, as if the object had been created in this new class. No side effects are triggered by the assignment (as in: no special method is called) So - for what you are making, it "works".

还有什么更清洁的方式可以实现我想要的行为?如果必要时,我可以销毁对象obj并重新创建一个对象,但在这种情况下,我想以一种通用的方式进行操作(例如obj =RoundObject(subclasstype ='Tofu'),因为代码的其他部分.

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).

是的,正如您已经指出的那样,这不是最好的处理方式.您可能拥有的类层次结构具有所需的不同方法,这些方法将您的对象作为属性-并根据您正在执行的操作在此外部层次结构中创建一个新对象-并保持其核心对象的属性不变.这就是适配器模式.

Yes, as you've noted, this not is the best way of doing things. What you could have is a class hierarchy with the distinct methods you need, that get your object as an attribute - and depending on what you are doing, you create a new object os this outer hierarchy - and keep your core object with its attributes unchanged. This is known as the 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 属性即可.然后,您会拥有数十个类,它们使用 side_length 属性执行多维数据集"操作- zope.interface 工具将允许您将以下数十个类中的任何一个用于任何具有Cubic接口的对象,只需通过将原始对象作为参数传递给所需方法的接口即可.

You can roll it on your own, manual classes, or use a well known and tested library that implements features to automate parts of the process. One such library is zope.interface, that allows you to write huge and complex systems using the adapter pattern. So you can have hundreds of different types of objects - you can mark them as having the interface "Cubic" as long as they have a side_length attribute. And then you cna have tens of classes that do "Cube" things with the side_length attribute - the zope.interface facilities will allow you to use any of these tens of classes with any of the objects that have the Cubic interface by simply calling the interface for the desired method passing the original object as a parameter.

但是 zope.interfaces 可能有点难掌握,因为在使用了近二十年后,根据需要编写的文档不多(有时,人们诉诸于使用XML文件来声明接口)和适配器-只需跳过任何处理XML的文档即可),因此对于较小的项目,您可以按上述方法手动滚动.

But zope.interfaces may be a little hard to grasp, due to poor documentation done as needed over nearly two decades of use (and at some point, people resorted to use XML files to declare interfaces and adapters - just skip any docs that deal with XML), so for smaller projects, you can just roll it manually as above.

我当前的实现已经使用了一个委托对象,但是不切实际,因为它隐藏了API中所有有趣的功能我想在该委托对象中提供的内容(我通常会重复委托对象的所有功能,但这很容易混淆人们).

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 -但如果要允许访问,则是完全接口/注册表/适配器系统的另一种解决方法豆腐上的几种 Cube 方法中的一种,而其他方法则是在魔术 __ getattr __ Python之上的 BaseMethods 类上实现的允许您以透明的方式检索所引用对象上的方法和属性的方法,而无需重新编写:

Since your real-use example is big, that s a case to indeed learn and use zope.interface - but another workaround to a fully interface/registry/adapter system, if you want to permit access of several Cube methods on Tofu and others is to implement on the BaseMethods class I have above the magic __getattr__ Python method that would allow you to retrieve methods and attributes on the referred object in a transparent way, with no re-write needed:

class BaseMethods(object):
    def __init__(self, related):
         self.related = related

    def __getattr__(self, attr):
        return getattr(self.related, attr)

这篇关于创建对象后更改对象的类型(Python中的类型转换)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆