Create new class instance from class method [duplicate] - python

This question already has answers here:
How can I create a copy of an object in Python?
(4 answers)
What is the purpose of the return statement? How is it different from printing?
(15 answers)
Closed 5 months ago.
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())?

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()

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]
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

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.

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?

from copy import copy
class Organism(object):
def __init__(self,name):
self.name=name
def setName(self,name):
self.name=name
def reproduce(self,childname):
#use deepcopy if necessary
cp = copy(self)
cp.setName("descendant from " + self.name + " " + childname)
return cp
def __str__(self):
return self.name
first = Organism("first")
second = first.reproduce("second")
print first
print second

Related

Promote instantiated class/object to a class in python?

Is there are a way in Python to store instantiated class as a class 'template' (aka promote object to a class) to create new objects of same type with same fields values, without relying on using data that was used to create original object again or on copy.deepcopy?
Like, for example I have the dictionary:
valid_date = {"date":"30 february"} # dict could have multiple items
and I have the class:
class AwesomeDate:
def __init__(self, dates_dict):
for key, val in dates_dict.items():
setattr(self, key, val);
I create the instance of the class like:
totally_valid_date = AwesomeDate(valid_date)
print(totally_valid_date.date) # output: 30 february
and now I want to use it to create new instances of the AwesomeDate class using the totally_valid_date instance as a template, i.e. like:
how_make_it_work = totally_valid_date()
print(how_make_it_work.date) # should print: 30 february
Is there are way to do so or no? I need a generic solution, not a solution for this specific example.
I don't really see the benefit of having a class act both as a template to instances, and as the instance itself, both conceptually and coding-wise. In my opinion, you're better off using two different classes - one for the template, one for the objects it is able to create.
You can think about awesome_date as a template class that stores the valid_date attributes upon initialization. Once called, the template returns an instance of a different class that has the expected attributes.
Here's a simple implementation (names have been changed to generalize the idea):
class Thing:
pass
class Template:
def __init__(self, template_attrs):
self.template_attrs = template_attrs
def __call__(self):
instance = Thing()
for key, val in self.template_attrs.items():
setattr(instance, key, val)
return instance
attrs = {'date': '30 february'}
template = Template(template_attrs=attrs)
# Gets instance of Thing
print(template()) # output: <__main__.Thing object at 0x7ffa656f8668>
# Gets another instance of Thing and accesses the date attribute
print(template().date) # output: 30 february
Yes, there are ways to do it -
there could even be some tweaking of inheriting from type and meddling with __call__ to make all instances automatically become derived classes. But I don't think that would be very sane. Python's own enum.Enum does something along this, because it has some use for the enum values - but the price is it became hard to understand beyond the basic usage, even for seasoned Pythonistas.
However, having a custom __init_subclass__ method that can inject some code to run prior to __init__ on the derived class, and then a method that will return a new class bound with the data that the new classes should have, can suffice:
import copy
from functools import wraps
def wrap_init(init):
#wraps(init)
def wrapper(self, *args, **kwargs):
if not getattr(self, "_initalized", False):
self.__dict__.update(self._template_data or {})
self._initialized = True
return init(self, *args, **kwargs)
wrapper._template_wrapper = True
return wrapper
class TemplateBase:
_template_data = None
def __init_subclass__(cls, *args, **kwargs):
super().__init_subclass__(*args, **kwargs)
if getattr(cls.__init__, "_template_wraper", False):
return
init = cls.__init__
cls.__init__ = wrap_init(init)
def as_class(self):
cls= self.__class__
new_cls = type(cls.__name__ + "_templated", (cls,), {})
new_cls._template_data = copy.copy(self.__dict__)
return new_cls
And using it:
class AwesomeDate(TemplateBase):
def __init__(self, dates_dict):
for key, val in dates_dict.items():
setattr(self, key, val)
On the REPL we have:
In [34]: x = AwesomeDate({"x":1, "y":2})
In [35]: Y = x.as_class()
In [36]: y = Y({})
In [37]: y.x
Out[37]: 1
Actually, __init_subclass__ itself could be supressed, and decorating __init__ could be done in one shot on the as_class method. This code takes some care so that mixin classes can be used, and it will still work.
It seems like you are going for something along the lines of the prototype design pattern.
What is the prototype design pattern?
From Wikipedia: Prototype pattern
The prototype pattern is a creational design pattern in software development. It is used when the type of objects to create is determined by a prototypical instance, which is cloned to produce new objects. This pattern is used to avoid subclasses of an object creator in the client application, like the factory method pattern does and to avoid the inherent cost of creating a new object in the standard way (e.g., using the 'new' keyword) when it is prohibitively expensive for a given application.
From Refactoring.guru: Prototype
Prototype is a creational design pattern that lets you copy existing objects without making your code dependent on their classes. The Prototype pattern delegates the cloning process to the actual objects that are being cloned. The pattern declares a common interface for all objects that support cloning. This interface lets you clone an object without coupling your code to the class of that object. Usually, such an interface contains just a single clone method.
The implementation of the clone method is very similar in all classes. The method creates an object of the current class and carries over all of the field values of the old object into the new one. You can even copy private fields because most programming languages let objects access private fields of other objects that belong to the same class. An object that supports cloning is called a prototype. When your objects have dozens of fields and hundreds of possible configurations, cloning them might serve as an alternative to subclassing. Here’s how it works: you create a set of objects, configured in various ways. When you need an object like the one you’ve configured, you just clone a prototype instead of constructing a new object from scratch.
Implementing this for your problem, along with your other ideas
From your explanation, it seems like you want to:
Provide a variable containing a dictionary, which will be passed to the __init__ of some class Foo
Instantiate class Foo and pass the variable containing the dictionary as an argument.
Implement __call__ onto class Foo, allowing us to use the function call syntax on an object of class Foo.
The implementation of __call__ will COPY/CLONE the “template” object. We can then do whatever we want with this copied/cloned instance.
The Code (edited)
import copy
class Foo:
def __init__(self, *, template_attrs):
if not isinstance(template_attrs, dict):
raise TypeError("You must pass a dict to instantiate this class.")
self.template_attrs = template_attrs
def __call__(self):
return copy.copy(self)
def __repr__(self):
return f"{self.template_attrs}"
def __setitem__(self, key, value):
self.template_attrs[key] = value
def __getitem__(self, key):
if key not in self.template_attrs:
raise KeyError(f"Key {key} does not exist in '{self.template_attrs=}'.")
return self.template_attrs[key]
err = Foo(template_attrs=1) # Output: TypeError: You must pass a dict to instantiate this class.
# remove err's assignment to have code under it run
base = Foo(template_attrs={1: 2})
print(f"{base=}") # Output: base={1: 2}
base_copy = base()
base_copy["hello"] = "bye"
print(f"{base_copy=}") # Output: base_copy={1: 2, 'hello': 'bye'}
print(f"{base_copy[1]=}") # Output: base_copy[1]=2
print(f"{base_copy[10]=}") # Output: KeyError: "Key 10 does not exist in 'self.template_attrs={1: 2, 'hello': 'bye'}'."
I also added support for subscripting and item assignment through __getitem__ and __setitem__ respectively. I hope that this helped a bit with your problem! Feel free to comment on this if I missed what you were asking.
Reasons for edits (May 16th, 2022 at 8:49 PM CST | Approx. 9 hours after original answer)
Fix code based on suggestions by comment from user jsbueno
Handle, in __getitem__, if an instance of class Foo is subscripted with a key that doesn't exist in the dict.
Handle, in __init__, if the type of template_attrs isn't dict (did this based on the fact that you used a dictionary in the body of your question)

Is this sound software engineering practice for class construction?

Is this a plausible and sound way to write a class where there is a syntactic sugar #staticmethod that is used for the outside to interact with? Thanks.
###scrip1.py###
import SampleClass.method1 as method1
output = method1(input_var)
###script2.py###
class SampleClass(object):
def __init__(self):
self.var1 = 'var1'
self.var2 = 'var2'
#staticmethod
def method1(input_var):
# Syntactic Sugar method that outside uses
sample_class = SampleClass()
result = sample_class._method2(input_var)
return result
def _method2(self, input_var):
# Main method executes the various steps.
self.var4 = self._method3(input_var)
return self._method4(self.var4)
def _method3(self):
pass
def _method4(self):
pass
Answering to both your question and your comment, yes it is possible to write such a code but I see no point in doing it:
class A:
def __new__(cls, value):
return cls.meth1(value)
def meth1(value):
return value + 1
result = A(100)
print(result)
# output:
101
You can't store a reference to a class A instance because you get your method result instead of an A instance. And because of this, an existing __init__will not be called.
So if the instance just calculates something and gets discarded right away, what you want is to write a simple function, not a class. You are not storing state anywhere.
And if you look at it:
result = some_func(value)
looks exactly to what people expect when reading it, a function call.
So no, it is not a good practice unless you come up with a good use case for it (I can't remember one right now)
Also relevant for this question is the documentation here to understand __new__ and __init__ behaviour.
Regarding your other comment below my answer:
defining __init__ in a class to set the initial state (attribute values) of the (already) created instance happens all the time. But __new__ has the different goal of customizing the object creation. The instance object does not exist yet when __new__is run (it is a constructor function). __new__ is rarely needed in Python unless you need things like a singleton, say a class A that always returns the very same object instance (of A) when called with A(). Normal user-defined classes usually return a new object on instantiation. You can check this with the id() builtin function. Another use case is when you create your own version (by subclassing) of an immutable type. Because it's immutable the value was already set and there is no way of changing the value inside __init__ or later. Hence the need to act before that, adding code inside __new__. Using __new__ without returning an object of the same class type (this is the uncommon case) has the addtional problem of not running __init__.
If you are just grouping lots of methods inside a class but there is still no state to store/manage in each instance (you notice this also by the absence of self use in the methods body), consider not using a class at all and organize these methods now turned into selfless functions in a module or package for import. Because it looks you are grouping just to organize related code.
If you stick to classes because there is state involved, consider breaking the class into smaller classes with no more than five to 7 methods. Think also of giving them some more structure by grouping some of the small classes in various modules/submodules and using subclasses, because a long plain list of small classes (or functions anyway) can be mentally difficult to follow.
This has nothing to do with __new__ usage.
In summary, use the syntax of a call for a function call that returns a result (or None) or for an object instantiation by calling the class name. In this case the usual is to return an object of the intended type (the class called). Returning the result of a method usually involves returning a different type and that can look unexpected to the class user. There is a close use case to this where some coders return self from their methods to allow for train-like syntax:
my_font = SomeFont().italic().bold()
Finally if you don't like result = A().method(value), consider an alias:
func = A().method
...
result = func(value)
Note how you are left with no reference to the A() instance in your code.
If you need the reference split further the assignment:
a = A()
func = a.method
...
result = func(value)
If the reference to A() is not needed then you probably don't need the instance too, and the class is just grouping the methods. You can just write
func = A.method
result = func(value)
where selfless methods should be decorated with #staticmethod because there is no instance involved. Note also how static methods could be turned into simple functions outside classes.
Edit:
I have setup an example similar to what you are trying to acomplish. It is also difficult to judge if having methods injecting results into the next method is the best choice for a multistep procedure. Because they share some state, they are coupled to each other and so can also inject errors to each other more easily. I assume you want to share some data between them that way (and that's why you are setting them up in a class):
So this an example class where a public method builds the result by calling a chain of internal methods. All methods depend on object state, self.offset in this case, despite getting an input value for calculations.
Because of this it makes sense that every method uses self to access the state. It also makes sense that you are able to instantiate different objects holding different configurations, so I see no use here for #staticmethod or #classmethod.
Initial instance configuration is done in __init__ as usual.
# file: multistepinc.py
def __init__(self, offset):
self.offset = offset
def result(self, value):
return self._step1(value)
def _step1(self, x):
x = self._step2(x)
return self.offset + 1 + x
def _step2(self, x):
x = self._step3(x)
return self.offset + 2 + x
def _step3(self, x):
return self.offset + 3 + x
def get_multi_step_inc(offset):
return MultiStepInc(offset).result
--------
# file: multistepinc_example.py
from multistepinc import get_multi_step_inc
# get the result method of a configured
# MultiStepInc instance
# with offset = 10.
# Much like an object factory, but you
# mentioned to prefer to have the result
# method of the instance
# instead of the instance itself.
inc10 = get_multi_step_inc(10)
# invoke the inc10 method
result = inc10(1)
print(result)
# creating another instance with offset=2
inc2 = get_multi_step_inc(2)
result = inc2(1)
print(result)
# if you need to manipulate the object
# instance
# you have to (on file top)
from multistepinc import MultiStepInc
# and then
inc_obj = MultiStepInc(5)
# ...
# ... do something with your obj, then
result = inc_obj.result(1)
print(result)
Outputs:
37
13
22

Change object's class

In a chess game, I define p1 = Pawn().
I'd like to be able to promote it:
def promote(self, piece):
pos = f'{Config.tile_convert(self.x)}{Config.tile_convert(self.y, True)}'
try:
self = piece(pos, color=self.color, num='s')
except NameError:
print(f'Cannot promote {self.pieceid} to {piece}')
However, feeding in Queen or Bishop doesn't actually change the object from being a Pawn. I think it's because of scoping:
class Chesspiece:
...
class Pawn(ChessPiece):
def promote()
...
class Queen(Chesspiece):
...
How can I change the class of an object?
Reassigning any bare name (including self) rebinds the name; whatever was in there before is thrown away, and the name now points to something unrelated.
So when you do:
self = piece(pos, color=self.color, num='s')
you just lose access to the "real" self (the object the method was called on), you don't change the object it was called on.
The only way to do what you want is kinda hacky; to reassign __class__ on self (and change any other attributes needed to make it a valid instance of the new class).
So in this case, you might be able to just do:
self.__class__ = piece
and if attribute values might need to be recreated/revalidated, or piece might be a factory function or weirdo class whose __new__ doesn't necessarily return the type it was called on, you'd create a new piece as a template, then copy from it, like so:
# Make a new piece to copy from
newpiece = piece(pos, color=self.color, num='s')
vars(self).clear() # Clear out existing attributes
vars(self).update(vars(newpiece)) # Copy attributes from new piece
self.__class__ = newpiece.__class__ # Using newpiece.__class__ ensures it's the resulting class
# in case of factory functions, weird __new__, etc.
Note: This is usually not what you want to do. As Rocky Li mentions in the comments, the usual approach is to replace the object in whatever structure holds it, rather than updating the class in place on an existing object, e.g. if you have variable named board which is a 2D list of lists, you'd just do:
a_piece = ... # Piece to replace
new_piece_type = ... # Type of piece it's being replaced with
pos = f'{Config.tile_convert(a_piece.x)}{Config.tile_convert(a_piece.y, True)}'
# Replace entire object (and local alias name) at known coordinates with new object
board[a_piece.x][a_piece.y] = a_piece = new_piece_type(pos, color=a_piece.color, num='s')

How do you create an instance of a class when the class type is stored in a variable?

So I have a somewhat long and growing list of classes in a script. At a certain point in the script I want to be able to test an arbitrary instance for its type, and then whatever that type is, I want to create a second object of the same type. I've tried researching this and I know I can accomplish this by storing every class in a dictionary, like so:
class Foo(object):
pass
class Bar(object):
pass
d = {"Foo": Foo, "Bar": Bar}
x = dict["Foo"]()
It does the trick, allowing me to use a variable or string containing the name of the class, in order to create an instance of the class. However, it requires that every time I create a new class I have to remember to also put a new entry in the dictionary--which isn't the worst thing in the world, but as they say, if you're doing the same task more than once you should make a computer do it.
Is there a better way? Can you somehow take a variable or string containing the name of a class, and without knowing what value the variable or string has, generate an instance of the class?
So this is answering your problem rather than your question, but it seems you actually want to create another instance of an object rather than find a class by name. So that is even easier because you can find the class of an object with the type function. So to create a new instance b of the same type as a but with constructor parameters args simply do:
b = type(a)(args)
All classes are in globals dictionary (dictionary containing the current scope's global variables). Get the dictionary with globals(), and then find it by name (string). As a result you will get a class, which can be instantiated with ().
class Foo(object):
pass
x = globals()['Foo']()
Not sure why Huazuo Gao didn't make that an answer, but it solves my problem exactly and I've never seen that solution in my research on this problem--so I'll go ahead and punch it up as an answer.
You can do it using the string of the name of the class and the eval() function. So
class Foo(object):
pass
a = Foo()
s = str(type(a))
# The string produced isn't quite the name of the class so it has to be stripped
# of some surrounding characters.
m = s.find('.')
n1 = s.find("'")
n2 = s[n1+1:].find("'")
s = s[m+1:n1+n2+1]
b = eval(s + "()")
produces the desired behavior.

Adding methods in type() generated objects in python

I am generating objects using type for using some of the code that is previously written
# Assume that myAppObjDict is already initialized.
myAppObj=type("myAppClass", (object,),myAppObjDict)
Now I want to add a method say myValue()in it so that if I should be able to call
value=myAppObj.myValue()
What should be the approach?
You should add the methods to myAppObjDict before you create the class:
def myValue(self, whatever):
pass
myAppObjDict['myValue'] = myValue
# Assume that myAppObjDict is already initialized.
myAppObj=type("myAppClass", (object,),myAppObjDict)
Alternatively define a base class containing the methods and include it in your tuple of base classes.
class MyBase(object):
def myValue(self): return 42
# Assume that myAppObjDict is already initialized.
myAppObj=type("myAppClass", (MyBase,),myAppObjDict)
You should be able to assign any function to the class after creation:
def method(self):
print self.__class__.__name__
def my_class (object):
pass
my_class.method = method
o = my_class()
o.method()
You can do this assignment at any time, and all object will have the new method added, event those already created.
After all, Python is a dynamic language :)

Categories