Consider a class;
class temp:
def __init__(self):
pass
I make an object of it;
obj = temp()
Convert it to string;
strObj = str(obj)
Now, how can i convert strObj to an object of temp class??
org = temp(strObj)
Consider a class;
class temp:
def __init__(self):
pass
I make an object of it;
obj = temp()
Convert it to string;
strObj = str(obj)
Now, how can i convert strObj to an object of temp class??
org = temp(strObj)
For those who are looking for overriding conversion builtins such as int(obj)
, float(obj)
, and str(obj)
, see Overload int() in Python. You need to implement __int__
, __float__
, or __str__
on the object.
To answer the question, one way of doing this is by "abusing" __repr__
in combination with eval()
. Let's first have a look at the __repr__
docs (emphasis: mine):
Called by the repr() built-in function to compute the “official” string representation of an object. If at all possible, this should look like a valid Python expression that could be used to recreate an object with the same value (given an appropriate environment). If this is not possible, a string of the form <...some useful description...> should be returned. The return value must be a string object. If a class defines
__repr__()
but not__str__()
, then__repr__()
is also used when an “informal” string representation of instances of that class is required.This is typically used for debugging, so it is important that the representation is information-rich and unambiguous.
With this in mind, we know that it is recommended to return a string from __repr__
which can be used with eval()
. This is implied by the statement that the value "should look like a valid Python expression".
Here is an example which uses this. The example also overrides __eq__
, but only for convenience for the print-outs. And for completeness we also add a value to the instance.
The example creates a new instance. Then the value is converted to a string using __repr__
(by using the repr()
function. Next that string value is passed to eval()
which will evaluate the string and return the result. The result will be a new instance of the same class and is stored in second_instance
. We also print out the id()
to visualise that we have indeed two different instances. Finally we show that first_instance == second_instance
is indeed True
:
class MyClass:
def __init__(self, value):
self.value = value
def __eq__(self, other):
return isinstance(self, MyClass) and self.value == other.value
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, self.value)
first_instance = MyClass(123)
print('First instance: repr=%r, id=%d' % (first_instance, id(first_instance)))
stringified = repr(first_instance)
print('Stringified: %r' % stringified)
second_instance = eval(stringified) # !!! DANGEROUS (see below) !!!
print('Second instance: repr=%r, id=%d' % (second_instance, id(second_instance)))
print('First == Second: %r' % (first_instance == second_instance))
This is 100% acceptable if absolutely everything going into eval()
is under your control! This means:
eval()
is called is under your controlKeeping all this in mind and guaranteeing that at no point in the future of the project I/O will end up in an eval()
call is almost impossible. As such I strongly recommend avoiding this in important production code as it opens up nasty security holes.
For code not running in production, this is absolutely acceptable. For example unit-tests, personal utility scripts, e.t.c. But the risk should always be taken into consideration.
eval()
is executed inside the Python process calling it, with the same privileges. Example: You read a value from a DB where multiple users have access and you eval()
it. In that case, another user may inject code via the database and that code will run as your user!eval()
when the values come from outside sources opens up the possibility of code-injections.repr()
will return a valid Python expression. This is only a recommendation by the docs. Hence the call to eval
with __repr__
is prone to run-time errors.eval()
needs to "know" about the class MyClass
(it must be imported). It only looks for the name. So if by pure chance that same name exists in the scope, but pointing to another object, you will call something else unintentionally and may run into weird bugs. Granted, this is an edge-case.Use one of the many available serialisation options. The most popular, and simplest one to use is to convert the object to/from JSON strings. The above example could be made safe like this:
import json
class MyClass:
@staticmethod
def from_json(document):
data = json.loads(document)
instance = MyClass(data['value'])
return instance
def __init__(self, value):
self.value = value
def __eq__(self, other):
return isinstance(self, MyClass) and self.value == other.value
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, self.value)
def to_json(self):
data = {
'value': self.value
}
return json.dumps(data)
first_instance = MyClass(123)
print('First instance: repr=%r, id=%d' % (first_instance, id(first_instance)))
stringified = first_instance.to_json()
print('Stringified: %r' % stringified)
second_instance = MyClass.from_json(stringified)
print('Second instance: repr=%r, id=%d' % (second_instance, id(second_instance)))
print('First == Second: %r' % (first_instance == second_instance))
This is only marginally more difficult but much safer.
The same approach can be used with other serialisation methods. Popular formats are:
As noted by Anand in comments, what you're looking for is object serialization and deserialization. One way to achieve this is through the pickle (or cPickle) module:
>>> import pickle
>>> class Example():
... def __init__(self, x):
... self.x = x
...
>>> a = Example('foo')
>>> astr = pickle.dumps(a) # (i__main__\nExample\np0\n(dp1\nS'x'\np2\nS'foo'\np3\nsb.
>>> b = pickle.loads(astr)
>>> b
<__main__.Example instance at 0x101c89ea8>
>>> b.x
'foo'
Note, however, that one gotcha in using the pickle module is dealing with implementation versions. As suggested in the Python docs, if you want an unpickled instance to automatically handle implementation versioning, you may want to add a version instance attribute and add a custom __setstate__ implementation: https://docs.python.org/2/library/pickle.html#object.setstate. Otherwise, the version of the object at serialization time will be exactly what you get at deserialization time, regardless of code changes made to the object itself.
What if you define a class constructor depending on the type of argument being passed. Moreover, if you want to allow the creation of an object without the arguments passed, the arguments can be put in *args. In the code below constructor checks the number and type of argument(s) and depending on it write to the self.attr
different values.
Something like that:
class temp():
def __init__(self, *args):
if len(args) > 0:
if isinstance(args[0],str):
self.attr = args[0]
else:
self.attr = 'Not a string'
else:
self.attr = 'Not a string'
def __str__(self):
return self.attr
obj = temp()
obj.attr = 'Hello World'
strObj = str(obj)
org = temp(strObj)
You can create a dedicated class for handling casting leveraging the json library and built in methods.
Sample Cast class:
import json
class Cast(object):
def __init__(self):
super(Cast, self).__init__()
@staticmethod
def _to(clazz, obj):
if (clazz == str):
return json.dumps(obj, default=lambda obj: obj.__dict__)
else:
newinstance = clazz()
if (isinstance(obj, str)):
jsonObj = json.loads(obj)
sharedKeys = jsonObj.keys()
for objKey in sharedKeys:
setattr(newinstance, objKey, jsonObj[objKey])
else:
# Test/Convert for other standard or custom types
pass
return newinstance
The Cast._to method, is used to cast your custom object, to the desired class. Use the flow control to handle various cases.
In this example, if casting to a str class, it will use the json dumps to convert the object to a json string.
When casting to anything other than string, use the built in isinstance method to handle how a specified object will be converted via flow control.
In this example, for the first else block, it is assumed that the object is a json string.
The json loads method then converts it into a dict representing the clazz object. The keys are extracted, which will be the same keys as any instance of the clazz object.
The keys are used to extract the values from the json object and pass them to a new class instance using the built in setattr.
Key thing to note about the Cast._to. For the first else block to work, the custom "clazz" to cast to, must have either a no args constructor or default values for all arguments. This is a similar approach to Java, which always has a default no args constructor, except in this in case you can use a constructor with args as long as the args have default values.
class temp(object):
def __init__(self, attr1=None, attr2=None, attr3=None):
super(temp, self).__init__()
self.attr1 = attr1
self.attr2 = attr2
self.attr3 = attr3
self.attr4 = None
def __main__():
obj = temp()
strObj = Cast._to(str, obj)
org = Cast._from(strObj, temp)
print(type(org))
print(org.attr2)
obj2 = temp("OdinsBeard", "Gungnir")
obj2.attr4 = "Fenrir"
strObj2 = Cast._to(str, obj2)
org2 = Cast._from(strObj2, temp)
print(type(org2))
print(org2.attr2)
__main__()
<class 'main.temp'>
None
<class 'main.temp'>
Gungnir
If you want your custom object to automatically be eligible as a json string when calling str() and when printing from an array, then you would need to override the custom class's __str__ and __repr__ methods respectively.
def __str__(self):
return json.dumps(self, default=lambda obj: obj.__dict__)
def __repr__(self):
return json.dumps(self, default=lambda obj: obj.__dict__)
Using the same setup as the first test above but with the newly added __str__ and __repr__ methods, the outputs is as below.
print(str(obj))
print(str(obj2))
#Output Below:
{"attr4": null, "attr2": null, "attr3": null, "attr1": null}
{"attr4": "Fenrir", "attr2": "Gungnir", "attr3": null, "attr1": "OdinsBeard"}
I think what you're looking for is a constructor to your class. Simply define a constructor that checks if the argument passed is a string. Then do any more changes that you might want to do to create the object from the string.
class obj():
def __init__(self, attribs*):
if len(attribs)==1 and type(attribs[0])=='str':
#your string manipulations go here
self.content = attribs[0].upper()