创建对象后更改对象类型(Python 中的类型转换)
Change type of an object after its creation (typecasting in Python)
在我的项目中,我生成了一个类型为 CubicObject
的对象 obj
。在运行时,应允许 GUI 设置将 obj
的类型更改为 Tofu
或 Box
(并返回),具体取决于用户想要做什么以及他(她)们做什么认为该对象最能代表。那么用户应该受益于相应 classes 中实现的特定算法。我正在寻找这种行为的一个很好的实现。我玩过下面的代码,它改变了 __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
我的两个问题是:
- class
A
的哪些属性被转移到对象 'obj'
当我更改其 __class__
属性时?调用了哪些函数
更新了哪些属性,或者 obj 又是如何发生的
将其名称更改为 A?
- 还有哪些更简洁的方法可以实现我想要的行为?如果
必要时,我可以销毁对象
obj
并重新创建另一个对象,
但在这种情况下,我想以一种通用的方式这样做(比如
obj = RoundObject(subclasstype='Tofu')
因为其他地方的
代码)。
潜在的问题是我允许用户在 CubicObject
的子 class 中实现自己的功能,并且应该能够在这些子 class 之间切换,同时该程序是 运行。
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__
属性 - 保留。并且对象的 class 有效地更改为分配的那个。 (Python 运行时禁止 __class__
为具有不同内存布局的对象赋值)。即:新 class 上的所有方法和 class 属性都可用于实例,并且方法的 none 或先前 class 的 class 属性在那里,就好像这个对象是在这个新的 class 中创建的。
赋值不会触发副作用(如:没有调用特殊方法)
所以 - 对于你正在制作的东西,它 "works".
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).
是的,正如您所指出的,这不是最好的做事方式。
你可能拥有的是一个 class 层次结构,它具有你需要的不同方法,将你的对象作为一个属性 - 并且根据你正在做的事情,你创建一个新对象 os 这个外部层次结构 - 和保持核心对象的属性不变。这被称为 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()
您可以自行滚动,手动 classes,或使用实现功能的众所周知且经过测试的库来自动化部分过程。一个这样的库是 zope.interface
,它允许您使用适配器模式编写庞大而复杂的系统。因此,您可以拥有数百种不同类型的对象 - 只要它们具有 side_length
属性,您就可以将它们标记为具有接口 "Cubic"。然后你有几十个 classes 用 side_length
属性做 "Cube" 事情 - zope.interface
设施将允许你使用这几十个 [=48] 中的任何一个=] 与任何具有 Cubic 接口的对象一起使用,只需调用所需方法的接口,将原始对象作为参数传递。
但是 zope.interfaces
可能有点难以掌握,因为在将近二十年的使用中根据需要完成的文档很差(并且在某些时候,人们诉诸于使用 XML 文件来声明接口和适配器 - 只需跳过任何处理 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).
由于您的 real-use 示例很大,因此确实需要学习和使用 zope.interface
- 但如果您想允许访问 interface/registry/adapter 系统的另一种解决方法Tofu
上的几个 Cube
方法和其他方法是在 BaseMethods
class 上实现的 我有上面的魔法 __getattr__
Python 方法可以让你以透明的方式检索引用对象的方法和属性,不需要 re-write:
class BaseMethods(object):
def __init__(self, related):
self.related = related
def __getattr__(self, attr):
return getattr(self.related, attr)
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
将由双方共享。
在我的项目中,我生成了一个类型为 CubicObject
的对象 obj
。在运行时,应允许 GUI 设置将 obj
的类型更改为 Tofu
或 Box
(并返回),具体取决于用户想要做什么以及他(她)们做什么认为该对象最能代表。那么用户应该受益于相应 classes 中实现的特定算法。我正在寻找这种行为的一个很好的实现。我玩过下面的代码,它改变了 __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
我的两个问题是:
- class
A
的哪些属性被转移到对象 'obj' 当我更改其__class__
属性时?调用了哪些函数
更新了哪些属性,或者 obj 又是如何发生的 将其名称更改为 A? - 还有哪些更简洁的方法可以实现我想要的行为?如果
必要时,我可以销毁对象
obj
并重新创建另一个对象, 但在这种情况下,我想以一种通用的方式这样做(比如obj = RoundObject(subclasstype='Tofu')
因为其他地方的 代码)。
潜在的问题是我允许用户在 CubicObject
的子 class 中实现自己的功能,并且应该能够在这些子 class 之间切换,同时该程序是 运行。
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__
属性 - 保留。并且对象的 class 有效地更改为分配的那个。 (Python 运行时禁止 __class__
为具有不同内存布局的对象赋值)。即:新 class 上的所有方法和 class 属性都可用于实例,并且方法的 none 或先前 class 的 class 属性在那里,就好像这个对象是在这个新的 class 中创建的。
赋值不会触发副作用(如:没有调用特殊方法)
所以 - 对于你正在制作的东西,它 "works".
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).
是的,正如您所指出的,这不是最好的做事方式。 你可能拥有的是一个 class 层次结构,它具有你需要的不同方法,将你的对象作为一个属性 - 并且根据你正在做的事情,你创建一个新对象 os 这个外部层次结构 - 和保持核心对象的属性不变。这被称为 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()
您可以自行滚动,手动 classes,或使用实现功能的众所周知且经过测试的库来自动化部分过程。一个这样的库是 zope.interface
,它允许您使用适配器模式编写庞大而复杂的系统。因此,您可以拥有数百种不同类型的对象 - 只要它们具有 side_length
属性,您就可以将它们标记为具有接口 "Cubic"。然后你有几十个 classes 用 side_length
属性做 "Cube" 事情 - zope.interface
设施将允许你使用这几十个 [=48] 中的任何一个=] 与任何具有 Cubic 接口的对象一起使用,只需调用所需方法的接口,将原始对象作为参数传递。
但是 zope.interfaces
可能有点难以掌握,因为在将近二十年的使用中根据需要完成的文档很差(并且在某些时候,人们诉诸于使用 XML 文件来声明接口和适配器 - 只需跳过任何处理 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).
由于您的 real-use 示例很大,因此确实需要学习和使用 zope.interface
- 但如果您想允许访问 interface/registry/adapter 系统的另一种解决方法Tofu
上的几个 Cube
方法和其他方法是在 BaseMethods
class 上实现的 我有上面的魔法 __getattr__
Python 方法可以让你以透明的方式检索引用对象的方法和属性,不需要 re-write:
class BaseMethods(object):
def __init__(self, related):
self.related = related
def __getattr__(self, attr):
return getattr(self.related, attr)
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
将由双方共享。