3

I have a base class:

class Animal(object):
    def __init__(self, name=None, food=None):
        self.name = name
        self.food = food

    def eat(self):
        print "The %s eats some %s" % (self.name, self.food)

And a class that extends it:

class Pet(Animal):
    def pet(self):
        print "You pet the %s." % self.name
    def feed(self):
        print "You put some %s into the bowl." % self.food
        self.eat()

Let's say I have an Animal object, created thusly:

>>> fluffy_as_animal = Animal('dog', 'kibbles')

Is there a straightforward way in python to create a Pet object from fluffy_as_animal, short of actually writing out:

>>>> fluffy_as_pet = Pet(fluffy_as_animal.name, fluffy_as_animal.food)

Update

The reason I'm doing this implementation is that I have a class with a function that returns a set of objects of type Animal all which I want to actually use as Pet objects:

class AnimalStore(object):
    def fetch(count, query=None, server=None, *args, **kwargs):
        connection = connect_to_server(server)
        resp = connection.fetch_animals(query)
        objects = parse_response(resp)
        animals = []
        for a in objects[:count]:
            animals.append(Animal(a['name'], a.['food']))
        return animals

class PetStore(AnimalStore):
    def fetch(count, query=None, server=None, *args, **kwargs):
        query = 'type=pet AND %s' % query
        animals = super(PetFactory, self).fetch(count, query, server, *args, **kwargs)
        pets = []
        for animal in animals:
            pets.append(magic_function(animal))
        return pets

AnimalStore and PetStore are in separate modules. AnimalStore is part of a standard Animal API and doesn't change. Basically I want to extend the Animal API by fetching a list of pets using the same mechanism used for animals, and I want to be able to take advantage of all of the other built-in functions that the Animal API already provides for its Animal and AnimalStore classes.

3
  • 3
    Don't Do That. That's simply a bad way to do OO programming. Why can't you simply create the Pet from the beginning? Or. Why aren't you using Delegation between Animal (the object) and it's role as a Pet? Commented Apr 1, 2011 at 16:42
  • Delegation is probably the answer! I'll give that a shot. Commented Apr 1, 2011 at 17:19
  • Notice the super(PetFactory, self) in PetStore's fetch(). Is this wrong? Commented Apr 11, 2011 at 13:34

4 Answers 4

4

You can write the __init__() for Pet to accept an instance of Animal (or really, any ojbect with the necessary attributes -- duck typing!) as well as the individual attributes. Then you could just do fluffy_as_pet = Pet(fluffy_as_animal). Or provide a class method (e.g. Pet.from_object()) to do the same, if you don't want to overload __init__(). Even if you don't control the Animal and Pet classes, you could make your own subclasses of them that behave as you need.

However, it would be best to go a step further and simply don't write code that accepts Animal but not Pet. Then you don't have to convert it. Again, duck typing -- if an object has the right attributes, you can use it regardless of its type.

Sign up to request clarification or add additional context in comments.

1 Comment

I would like to state that duck typing is my favorite and least favorite thing about Python.
1

You could do

fluffy_as_pet = object.__new__(Pet)
fluffy_as_pet.__dict__ = fluffy_as_animal.__dict__.copy()

or

from copy import copy
fluffy_as_pet = copy(fluffy_as_animal)
fluffy_as_pet.__class__ = Pet

but both those ways are probably more of a hack than a clean solution.

Comments

1

I ended up using delegation (credit should go to S.Lott):

class Animal(object):
    def __init__(self, name=None, food=None):
        self.name = name
        self.food = food

    def eat(self):
        print "The %s eats some %s" % (self.name, self.food)

class Pet(object):
    def __init__(self, animal=None):
        self.animal = animal

    def __getattr__(self, key):
        return getattr(self.animal, key)

    def __setattr__(self, key, value):
        return setattr(self.animal, key, value)

    def eat(self):
        return self.animal.eat()

    def pet(self):
        print "You pet the %s." % self.name

    def feed(self):
        print "You put some %s into the bowl." % self.food
        self.eat()

I only hope I implemented it correctly; either way, it works!

Comments

-1

Personally I would use a classmethod for this:

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

    @classmethod
    def fetch(cls, name):
        # DB code left out intentionally.
        return cls(name)


class Dog(Animal):
    def bark(self):
        print("woof")

That way calling Dog().get('fido') will return an object of type Dog and Animal().get('Mickey') will return an object of type Animal.

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.