简体   繁体   中英

Creating an object from a base class object in Python

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.

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 (eg 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.

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.

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!

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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM