Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert an object back into the code used to create it?

Tags:

python

For example if I have a custom Python object like this;

#!/usr/bin/env python3
import os
base_dir = os.path.abspath(".")
class MyFile(dict):
    def __init__(self, name, size = None, dir = base_dir):
        self.name = name
        self.path = os.path.join(dir, name)
        self.bytes = size

and somewhere in my program, I initialize my object class;

a = MyFile(name = "foo", size = 10)

I want to be able to return the code used to create the object in the first place. For example;

print(a)
# <__main__.MyFile object at 0x102b84470>
# should instead print:
# MyFile(name = "foo", size = 10)

But since my object has some default attribute values, I only want those to show up in the output if they were explicitly included when the object was initialized;

b = MyFile(name = "bar", dir = "/home")
print(b)
# <__main__.MyFile object at 0x102b845c0>
# should instead print:
# MyFile(name = "bar", dir = "/home")

And to be clear, I am not trying to pull this from the source code, because a lot of my objects will be created dynamically, and I want to be able to return the same thing for them as well;

l = [ ("baz", 4), ("buzz", 12) ]
f = [ MyFile(name = n, size = s) for n, s in l ]
print(f)
# [<__main__.MyFile object at 0x1023844a8>, <__main__.MyFile object at 0x102384828>]
# should instead print:
# [ MyFile(name = "baz", size = 4), MyFile(name = "buzz", size = 12) ]

I saw the inspect library (https://docs.python.org/3/library/inspect.html) but it does not seem to have anything that does this. What am I missing? This functionality would be pretty analogous to R's dput function.

like image 867
user5359531 Avatar asked Dec 05 '25 10:12

user5359531


2 Answers

At a very basic level you can do this:

class MyClass:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def __repr__(self):
        return f'{self.__class__.__name__}({self.a}, {self.b})'


class MyOtherClass(MyClass):
    def method(self):
        pass


c = MyClass(1, 2)
oc = MyOtherClass(3, 4)
print(c, oc)

Result:

MyClass(1, 2) MyOtherClass(3, 4)

This does what you ask, as well as taking subclassing into account to provide the correct class name. But of course things can get complicated for several reasons:

class MyClass:
    def __init__(self, a, b):
        self.a = a + 1
        self.b = b if b < 10 else a
        self.c = 0
        
    def inc_c(self):
        self.c += 1
        
    def __repr__(self):
        return f'{self.__class__.__name__}({self.a - 1}, {self.b})'

The value of c isn't covered by the constructor, so the proposed call would set it to 0. And Although you could compensate for the + 1 for a, the value of b will be more complicated - even more so if you realise someone could have changed the value later.

And then you need to consider that subclasses can override behaviour, etc. So, doing something like this only makes sense in very limited use cases.

like image 59
Grismar Avatar answered Dec 07 '25 00:12

Grismar


As simple as replacing your code snippet with the following:

import os
base_dir = os.path.abspath(".")
class MyFile(object):
    def __init__(self, name, size = None, dir = base_dir):
        self.name = name
        self.path = os.path.join(dir, name)
        self.bytes = size
        self.remember(name,size, dir)
    def remember(self, name,size, dir):
        self.s= '{}(name = \'{}\'{}{})'.format(self.__class__.__name__,name, ", size="+str(size) if size!=None else "", ', dir="'+dir+'"' if dir!=base_dir else "")

        
    def __repr__(self):
        return self.s

a) for a it returns:

MyFile(name = 'foo', size=10)

b) for b it returns:

MyFile(name = 'bar', dir="/home")

c) for f it returns:

[MyFile(name = 'baz', size=4), MyFile(name = 'buzz', size=12)]
like image 20
Fatemeh Sangin Avatar answered Dec 07 '25 00:12

Fatemeh Sangin



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!