Create new class instance from class method

52,462

Solution 1

class Organism(object):
    def reproduce(self):
        #use self here to customize the new organism ...
        return Organism()

Another option -- if the instance (self) isn't used within the method:

class Organism(object):
    @classmethod
    def reproduce(cls):
        return cls()

This makes sure that Organisms produce more Organisms and (hypothetical Borgs which are derived from Organisms produce more Borgs).

A side benefit of not needing to use self is that this can now be called from the class directly in addition to being able to be called from an instance:

new_organism0 = Organism.reproduce()  # Creates a new organism
new_organism1 = new_organism0.reproduce()  # Also creates a new organism

Finally, if both the instance (self) and the class (Organism or subclasses if called from a subclass) are used within the method:

class Organism(object):
    def reproduce(self):
        #use self here to customize the new organism ...
        return self.__class__()  # same as cls = type(self); return cls()

In each case, you'd use it as:

organism = Organism()
new_organism = organism.reproduce()

Solution 2

why not simply use the copy module?

import copy
organism = Organism()
replica = copy.deepcopy(organism)

Solution 3

What about something like this:

class Organism(object):

    population = []

    def __init__(self, name):
        self.name = name
        self.population.append(self)
    def have_one_child(self, name):
        return Organism(name)
    def reproduce(self, names):
        return [self.have_one_child(name) for name in names]

Result:

>>> a = Organism('a')
>>> len(Organism.population)
1
>>> a.reproduce(['x', 'y', 'z']) # when one organism reproduces, children are added
                                 # to the total population
                                 # organism produces as many children as you state
[<__main__.Organism object at 0x05F23190>, <__main__.Organism object at 0x05F230F0>, <__main__.Organism object at 0x05F23230>]
>>> for ele in Organism.population:
...     print ele.name
... 
a
x
y
z
>>> Organism.population[3].reproduce(['f', 'g'])
[<__main__.Organism object at 0x05F231D0>, <__main__.Organism object at 0x05F23290>]
>>> for ele in Organism.population:
...     print ele.name
... 
a
x
y
z
f
g

Solution 4

I believe you are asking how to copy an object. Surprisingly (maybe), there is (almost) no standard method for this, and this is by design. The issue comes from the intrinsic ambiguity of the idea of copying, i.e.: when you copy an object property do you mean to copy it as reference (copy) or as value (deepcopy)?

However, in the majority of cases you want a consistent behavior (deepcopy or copy for all properties), in this case you can use copy module as

import copy
new_obj = copy.copy(old_obj)

or

new_obj = copy.deepcopy(old_obj)

In a generic case in which you want a more customized behavior, you use the same commands, but override the __copy__ and __deepcopy__ methods of your objects.

See more answers for details and examples, e.g.: How to override the copy/deepcopy operations for a Python object?

Solution 5

The same way you did originally, but then you have to do something with it!

organism = Organism() calls the class Organism (parentheses directly after a name is the "call" operation). This creates and returns a new instance of the class, which you then bind to the name organism.

When you execute that line in the interpreter, you now have a variable organism referring to the new Organism instance you just created.

When you write that line inside a function (including a method, because there's no difference between a method and a function "from the inside"), it does the same thing, but the variable organism is a local variable. Local variables are thrown away when the function is finished, so this does create a new Organism instance, but it doesn't achieve anything because you never gain access to it.

Your function should return any information it wants to communicate to its caller. Any local variables that you don't return are only useful if you use those variables to create something you do return.

Note that this has nothing to do with your particular problem of creating an instance inside a method; it's just how functions/methods work in general. You will need to learn how functions work before you can successfully write object-oriented programs using classes and instances; I would strongly suggest you work through some tutorials.

Share:
52,462
ohblahitsme
Author by

ohblahitsme

Updated on July 09, 2022

Comments

  • ohblahitsme
    ohblahitsme almost 2 years

    I want to be able to create a new instance of an object by calling a method on an already instantiated object. For example, I have the object:

    organism = Organism()

    I want to be able to call organism.reproduce() and have two objects of type Organism. My method at this point looks like this:

    class Organism(object):
        def reproduce():
            organism = Organism()
    

    and I'm pretty sure it doesn't work (I'm not really even sure how to test it. I tried the gc method in this post). So how can I make my object create a copy of itself that's accessible just like the first object I created (with organism = Organism())?

  • isosceleswheel
    isosceleswheel about 7 years
    I found this explanation quite helpful, but can you elaborate on what you meant by "if the instance doesn't matter" vs. "if both the instance and the class matter"? Both the @classmethod approach and the self.__class__() approach work in my case: I have a base class method get_copy that creates a new instance (?) of the subclass and then I use the __dict__ to turn the new subclass into a "copy" of the original. Is one approach more appropriate than the other in this case?
  • mgilson
    mgilson about 7 years
    @isosceleswheel -- I've updated the text slightly. Basically I was talking about what data the function needs to create a new instance. In other words, "If the instance doesn't matter" was the same thing as saying "If self isn't used in the function". Hopefully the updated text makes it more clear.
  • TheGrimmScientist
    TheGrimmScientist almost 3 years
    good answer, but attached to the wrong question.
  • Vincenzooo
    Vincenzooo almost 3 years
    Thanks! but why do you say that? I understand this is what is asked when it says "So how can I make my object create a copy of itself that's accessible just like the first object.." (also from first comment to question, I am not the only one who understands the same.
  • TheGrimmScientist
    TheGrimmScientist almost 3 years
    Does you solution show how your copy is made "by calling a method on an already instantiated object"? No. You're making the copy yourself, not by defining then calling a function on / within old_obj.
  • Vincenzooo
    Vincenzooo over 2 years
    For a generic copy, you might write a method (e.g. old_obj.replicate()) that returns copy.copy(self), but there is little reason for using such a method: you can just call copy.copy(old_obj). I think this is the standard way to make a copy, since it works even with more object-specific methods (e.g. copy some properties and reference others) which override the __copy__ method of the object.