【问题标题】:Object type casting in Python (design suggestion)Python 中的对象类型转换(设计建议)
【发布时间】:2011-04-14 13:00:08
【问题描述】:

假设有一个名为 get_pack() 的库函数,它返回一个 Pack 对象:

class Pack(object):
    def __init__(self, name, weight):
        self.name = name
        self.weight = weight
        ...

我想要做的是将这个对象包装到我的对象中,比如说 CommercialPack,它有几个有用的功能:

class CommercialPack(Pack):
    def get_delivery_price(self, distance):
        return self.weight * PER_KM_PRICE * distance
    ...

然后创建一个返回 CommercialPack 而不是 Pack 的函数。在其他一些语言(如 Delphi 或 Java)中,您可以即时键入强制转换该对象。所以我想要这样的东西:

def get_commercial_pack():
    return CommercialPack(get_pack())

然后您就可以轻松获得所需的一切。因为我希望我的 CommercialPack 对象具有 Pack 对象所具有的所有属性和功能。我只想将它包装在我的新课程中。基本上我不想做这样的事情:

class CommercialPack(object):
    def __init__(self, pack):
        self.name = pack.name
        self.weight = pack.weight
        ...

class CommercialPack(object):
    def __init__(self, pack):
        self.pack = pack

我正在寻找一个优雅的解决方案,而不是像我说的那样,某种类型转换或任何我可以在 Python 中优雅地做的事情。

非常感谢。

【问题讨论】:

标签: python oop casting


【解决方案1】:

这对你有用吗?

#!/usr/bin/python
PER_KM_PRICE = 10

class Pack(object):
    def __init__(self, name, weight):
        self.name = name
        self.weight = weight

    def test_fn(self):
        print "test"

class CommercialPack(Pack):
    def __init__(self, pack):
        self.pack = pack

    def get_delivery_price(self, distance):
        return self.weight * PER_KM_PRICE * distance

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

你可以这样使用:

>>> p = Pack(10, 20)
>>> cp = CommercialPack(p)
>>> cp.weight
20
>>> cp.get_delivery_price(10)
2000
>>> cp.test_fn()
test

【讨论】:

  • 是的,这很好,但是当您需要访问 Pack 对象具有的功能时,您仍然需要这样做: p.pack.some_function_of_pack_object() 看起来不太好,因为您的 CommercialPack 仍然存在一个包对象。 pack.pack... 看起来很奇怪。我不太确定我想要的是否可以在 Python 中实现。
  • @pocoa:那不是真的。如果Pack中有def test_fn(self): print "test",你仍然可以调用它为cp.test_fn()。试试看。
  • 这不是铸造,这是包装。 CommercialPack 现在既继承自 Pack,又包含 Pack 的一个实例。当您在 CommercialPack 实例上调用 test_fn 时,它不会使用 Pack 实例的 test_fn,而是使用 CP 实例的继承 test_fn。
【解决方案2】:

大概是这样的

class CommercialPack(object):
    def __init__(self, pack):
        self.__dict__.update(pack.__dict__)

如果您不介意在包和商业包之间共享状态,您甚至可以这样做

class CommercialPack(object):
    def __init__(self, pack):
        self.__dict__ = pack.__dict__

在您的示例中应该没问题,因为您没有保留对包对象的任何其他引用

例如。

PER_KM_PRICE = 100

class Pack(object):
    def __init__(self, name, weight):
        self.name = name
        self.weight = weight


class CommercialPack(Pack):
    def __init__(self, pack):
        self.__dict__ = pack.__dict__

    def get_delivery_price(self, distance):
        return self.weight * PER_KM_PRICE * distance

def get_pack():
    return Pack("pack", 20)

cp = CommercialPack(get_pack())
print cp.get_delivery_price(3)

【讨论】:

  • 感谢您的回答。 Rumple Stiltskin 是第一个回答的,但我更喜欢这个。我希望 Python 有类似 Commercial_object = (CommercialPack)pack_object 的东西。因为有了这个解决方案,你仍然需要编写一些代码。因此,基本上 CommercialPack 将有两个不同的构造函数,一个带有参数包,另一个带有名称和权重。看来这是我能拥有的最好的了..
  • @pocoa:Python 是动态类型的,这尤其意味着每个对象都包含有关其类型的信息。 C 或类似语言中的类型转换重新解释内存中某个地址的数据作为不同类型。这在 Python 中是不可能的,因为类型与对象的数据一起存储。在某些情况下,您可以通过分配给__class__ 属性来更改对象的类型,但这被认为是令人困惑和不好的风格。
  • 感谢斯文的解释。但是你可以做 str(obj) 对吗?我不知道它是如何实现的,但这就是为什么我认为可以用 Java/Delphi 的方式来实现。
  • @pocoa: str(obj) 实际上调用了obj.__str__()。它不是真正的类型转换,只有在 obj 实现有用的 __str__() 方法时才会产生有用的结果。
【解决方案3】:

注意:这是附近this answer 的副本(希望在上面。)

所以你不想复制一个无聊的 Pack 字段列表,而是想添加一点。有一个easy way to delegate resolution of unknown names 使用__getattr__

class CommercialPack(object):
  def __init__(self, pack):
  self.pack = pack
  self.bar = 10

class CommercialPack(object):
  def __init__(self, pack):
    self.pack = pack
    self.bar = 10

  def __getattr__(self, name):
    return getattr(self.pack, name) # not called for bar!

现在魔法起作用了:

>>> p = Pack('I am foo')
>>> p.foo
'I am foo'
>>> cp = CommercialPack(p)
>>> cp.foo
'I am foo'
>>> cp.bar
10
>>> _

因此,您可以向CommercialPack 添加方法和任何其他属性,并透明地访问Pack 的那些。

请注意,如果您添加的名称已经存在于 Pack 中,CommercialPack 的属性将隐藏同名的 Pack 的属性。但您始终可以通过pack 属性访问它。

【讨论】:

    猜你喜欢
    • 2013-09-11
    • 1970-01-01
    • 2017-06-26
    • 2018-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-10
    • 1970-01-01
    相关资源
    最近更新 更多