2

Let's say there is a library function called get_pack() which returns a Pack object:

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

What I want to do is to wrap this object into my object which is let's say CommercialPack which has a couple of useful functions:

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

And then create a function which returns CommercialPack instead of Pack. In some other languages like Delphi or Java, you can type cast that object on the fly. So I want to have something like:

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

and then you have all you need with no hassle. Because I would like my CommercialPack object to have all the properties and functions that Pack object has. I just want to wrap it inside my new class. Basically I don't want to do something like this:

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

OR

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

I'm looking for an elegant solution instead like I said, some kind of type casting or whatever I can do elegantly in Python.

Thanks very much.

1

3 Answers 3

4

Will this work for you?

#!/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)

You can use this like:

>>> p = Pack(10, 20)
>>> cp = CommercialPack(p)
>>> cp.weight
20
>>> cp.get_delivery_price(10)
2000
>>> cp.test_fn()
test
Sign up to request clarification or add additional context in comments.

3 Comments

Yeah, this is good but when you need to access to the functions that Pack object has, you still need to do: p.pack.some_function_of_pack_object() which is not looking nice since your CommercialPack is still a pack object. pack.pack... is looking weird. I'm not really sure if what I want is possible in Python.
@pocoa: That is not true. If there is def test_fn(self): print "test" in Pack, you can still call it as cp.test_fn(). Try it.
This is not casting though, this is wrapping. CommercialPack now both inherits from Pack and contains an instance of Pack. When you call test_fn on a CommercialPack instance it will not be using the test_fn of the Pack instance, but the inherited test_fn of the CP instance.
1

Perhaps something like this

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

You can even do this if you don't mind sharing state between the pack and the commercial pack

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

It should be ok in your example since you aren't keeping any other references to the pack object

eg.

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)

4 Comments

Thank you for your answer. Rumple Stiltskin was the first one to answer but I liked this one more. I wish Python would have something like commercial_object = (CommercialPack)pack_object. Because with this solution, you still need to write some code. So, basically CommercialPack will have two different constructors one with a parameter pack and the other one with name and weight. Seems like this is the best I can have..
@pocoa: Python is dynamically typed, which means in particular that each object includes information on its type. A typecast in C or similar languages reinterprets the data at some address in memory as a different type. This is not possible in Python, since the type is stored alongside with the data of an object. You can change the type of an object under certain circumstances by assigning to the __class__ attribute, but this is considered confusing and bad style.
Thanks for the explanation Sven. But you can do str(obj) right? I don't know how it's implemented but that's why I thought it's possible to do it in Java/Delphi way.
@pocoa: str(obj) actually calls obj.__str__(). It is not really a typecast, and only yields a useful result if obj implements a useful __str__() method.
1

Note: this is a duplicate of this answer nearby (hopefully above.)

So you don't want to copy a boring list of Pack's fields, but want to add a bit. There's an easy way to delegate resolution of unknown names using __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!

Now the magic works:

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

So you can add methods and any other attributes to CommercialPack and transparently access those of Pack.

Please note that if you add a name that is already present in Pack, CommercialPack's attribute will shadow the same-named Pack's attribute. But you can always access it via the pack attribute.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.