Create new class instance from class method [duplicate]
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() )?
I suppose that when you say reproduce , you really mean clone , right? And if that’s the case then it would help tremendously to tell us what are the defining characteristics/attributes of your Organism , in order to do it right.
6 Answers 6
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()
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?
@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.
why not simply use the copy module?
import copy organism = Organism() replica = copy.deepcopy(organism)
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]
>>> 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 [, , ] >>> for ele in Organism.population: . print ele.name . a x y z >>> Organism.population[3].reproduce(['f', 'g']) [, ] >>> for ele in Organism.population: . print ele.name . a x y z f g
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.
Инстанцирование в Python
Какой метод вызывается первым при этом вызове Foo? Большинство новичков, да и, возможно, немало опытных питонистов тут же ответят: «метод __init__». Но если внимательно приглядеться к сниппетам выше, вскоре станет понятно, что такой ответ неверен.
__init__ не возвращает никакого результата, а Foo(1, y=2), напротив, возвращает экземпляр класса. К тому же __init__ принимает self в качестве первого параметра, чего не происходит при вызове Foo(1, y=2). Создание экземпляра происходит немного сложнее, о чём мы и поговорим в этой статье.
Порядок создания объекта
Инстанцирование в Python состоит из нескольких стадий. Понимание каждого шага делает нас чуть ближе к пониманию языка в целом. Foo — это класс, но в Питоне классы это тоже объекты! Классы, функции, методы и экземпляры — всё это объекты, и всякий раз, когда вы ставите скобки после их имени, вы вызываете их метод __call__. Так что Foo(1, y=2) — это эквивалент Foo.__call__(1, y=2). Причём метод __call__ объявлен в классе объекта Foo. Какой же класс у объекта Foo?
Так что класс Foo — это экземпляр класса type и вызов метода __call__ последнего возвращает класс Foo. Теперь давайте разберём, что из себя представляет метод __call__ класса type. Ниже находятся его реализации на C в CPython и в PyPy. Если надоест их смотреть, прокручивайте чуть дальше, чтобы найти упрощённую версию:
CPython
static PyObject * type_call(PyTypeObject *type, PyObject *args, PyObject *kwds) < PyObject *obj; if (type->tp_new == NULL) < PyErr_Format(PyExc_TypeError, "cannot create '%.100s' instances", type->tp_name); return NULL; > obj = type->tp_new(type, args, kwds); obj = _Py_CheckFunctionResult((PyObject*)type, obj, NULL); if (obj == NULL) return NULL; /* Ugly exception: when the call was type(something), don't call tp_init on the result. */ if (type == &PyType_Type && PyTuple_Check(args) && PyTuple_GET_SIZE(args) == 1 && (kwds == NULL || (PyDict_Check(kwds) && PyDict_Size(kwds) == 0))) return obj; /* If the returned object is not an instance of type, it won't be initialized. */ if (!PyType_IsSubtype(Py_TYPE(obj), type)) return obj; type = Py_TYPE(obj); if (type->tp_init != NULL) < int res = type->tp_init(obj, args, kwds); if (res < 0) < assert(PyErr_Occurred()); Py_DECREF(obj); obj = NULL; >else < assert(!PyErr_Occurred()); >> return obj; >
PyPy
def descr_call(self, space, __args__): promote(self) # invoke the __new__ of the type if not we_are_jitted(): # note that the annotator will figure out that self.w_new_function # can only be None if the newshortcut config option is not set w_newfunc = self.w_new_function else: # for the JIT it is better to take the slow path because normal lookup # is nicely optimized, but the self.w_new_function attribute is not # known to the JIT w_newfunc = None if w_newfunc is None: w_newtype, w_newdescr = self.lookup_where('__new__') if w_newdescr is None: # see test_crash_mro_without_object_1 raise oefmt(space.w_TypeError, "cannot create '%N' instances", self) w_newfunc = space.get(w_newdescr, self) if (space.config.objspace.std.newshortcut and not we_are_jitted() and isinstance(w_newtype, W_TypeObject)): self.w_new_function = w_newfunc w_newobject = space.call_obj_args(w_newfunc, self, __args__) call_init = space.isinstance_w(w_newobject, self) # maybe invoke the __init__ of the type if (call_init and not (space.is_w(self, space.w_type) and not __args__.keywords and len(__args__.arguments_w) == 1)): w_descr = space.lookup(w_newobject, '__init__') if w_descr is not None: # see test_crash_mro_without_object_2 w_result = space.get_and_call_args(w_descr, w_newobject, __args__) if not space.is_w(w_result, space.w_None): raise oefmt(space.w_TypeError, "__init__() should return None") return w_newobject
Если забыть про всевозможные проверки на ошибки, то коды выше примерно эквивалентны такому:
def __call__(obj_type, *args, **kwargs): obj = obj_type.__new__(*args, **kwargs) if obj is not None and issubclass(obj, obj_type): obj.__init__(*args, **kwargs) return obj
__new__ выделяет память под «пустой» объект и вызывает __init__, чтобы его инициализировать.
- Foo(*args, **kwargs) эквивалентно Foo.__call__(*args, **kwargs).
- Так как объект Foo — это экземпляр класса type, то вызов Foo.__call__(*args, **kwargs) эквивалентен type.__call__(Foo, *args, **kwargs).
- type.__call__(Foo, *args, **kwargs) вызывает метод type.__new__(Foo, *args, **kwargs), возвращающий obj.
- obj инициализируется при вызове obj.__init__(*args, **kwargs).
- Результат всего процесса — инициализированный obj.
Кастомизация
Теперь давайте переключим наше внимание на __new__. Этот метод выделяет память под объект и возвращает его. Вы вольны кастомизировать этот процесс множеством разных способов. Следует отметить, что, хотя __new__ и является статическим методом, вам не нужно объявлять его используя @staticmethod: интерпретатор обрабатывает __new__ как специальный случай.
Распространённый пример переопределения __new__ — создание Синглтона:
class Singleton(object): _instance = None def __new__(cls, *args, **kwargs): if cls._instance is None: cls._instance = super().__new__(cls, *args, **kwargs) return cls._instance
>>> s1 = Singleton() . s2 = Singleton() . s1 is s2 True
Обратите внимание, что __init__ будет вызываться каждый раз при вызове Singleton(), поэтому следует соблюдать осторожность.
Другой пример переопределения __new__ — реализация паттерна Борг («Borg»):
class Borg(object): _dict = None def __new__(cls, *args, **kwargs): obj = super().__new__(cls, *args, **kwargs) if cls._dict is None: cls._dict = obj.__dict__ else: obj.__dict__ = cls._dict return obj
>>> b1 = Borg() . b2 = Borg() . b1 is b2 False >>> b1.x = 8 . b2.x 8
Учтите, что хотя примеры выше и демонстрируют возможности переопределения __new__, это ещё не значит что его обязательно нужно использовать:
__new__ — одна из самых частых жертв злоупотреблений. То, что может быть сделано переопределением этого метода, чаще всего лучше достигается другими средствами. Тем не менее, когда это действительно необходимо, __new__ — крайне полезный и мощный инструмент.
— Арион Спрэг, Хорошо забытое старое в Python
Редко можно встретить проблему в Python, где лучшим решением было использование __new__. Но когда у вас есть молоток, каждая проблема начинает выглядеть как гвоздь, поэтому всегда предпочитайте использованию нового мощного инструмента использование наиболее подходящего.