Which is a better __repr__ for a custom Python class?
Asked Answered
U

1

11

It seems there are different ways the __repr__ function can return.

I have a class InfoObj that stores a number of things, some of which I don't particularly want users of the class to set by themselves. I recognize nothing is protected in python and they could just dive in and set it anyway, but seems defining it in __init__ makes it more likely someone might see it and assume it's fine to just pass it in.

(Example: Booleans that get set by a validation function when it determines that the object has been fully populated, and values that get calculated from other values when enough information is stored to do so... e.g. A = B + C, so once A and B are set then C is calculated and the object is marked Valid=True.)

So, given all that, which is the best way to design the output of __ repr__?

    bob = InfoObj(Name="Bob")
    # Populate bob.

    # Output type A:
    bob.__repr__()
'<InfoObj object at 0x1b91ca42>'

    # Output type B:
    bob.__repr__()
'InfoObj(Name="Bob",Pants=True,A=7,B=5,C=2,Valid=True)'

    # Output type C:
    bob.__repr__()
'InfoObj.NewInfoObj(Name="Bob",Pants=True,A=7,B=5,C=2,Valid=True)'

... the point of type C would be to not happily take all the stuff I'd set 'private' in C++ as arguments to the constructor, and make teammates using the class set it up using the interface functions even if it's more work for them. In that case I would define a constructor that does not take certain things in, and a separate function that's slightly harder to notice, for the purposes of __repr__

If it makes any difference, I am planning to store these python objects in a database using their __repr__ output and retrieve them using eval(), at least unless I come up with a better way. The consequence of a teammate creating a full object manually instead of going through the proper interface functions is just that one type of info retrieval might be unstable until someone figures out what he did.

Undergo answered 15/6, 2011 at 2:47 Comment(7)
BTW, syntax is hasty and probably a bit rough. I did not design all three and then copy-paste their actual output. :)Undergo
You can use pickle to store your objects in the databaseVue
"Something useful for a developer (e.g. you) to identify an object by", whatever that may be. It needs not expose all data or be reversible. The use of eval() here is ... ickMcdougal
@Franklin Thanks, will check out Pickle. Haven't had to store-and-retrieve in previous projects.Undergo
@pst - but also it should have eval(repr(obj)) == obj, right?Undergo
@Undergo No. That is not a requirement at all. While that is how the basic types (numbers, strings, etc) happen work, there is no contract for the behavior. Consider: eval(repr(re.compile("doesn't work"))), where re is the regexp module.Mcdougal
@pst Ah ok, so I'm hearing that eval(repr(obj)) == obj is nice but gets tossed out for more complex objects, and readability for debugging is the only actual goal.Undergo
G
14

The __repr__ method is designed to produce the most useful output for the developer, not the enduser, so only you can really answer this question. However, I'd typically go with option B. Option A isn't very useful, and option C is needlessly verbose -- you don't know how your module is imported anyway. Others may prefer option C.

However, if you want to store Python objects is a database, use pickle.

import pickle
bob = InfoObj(Name="Bob")

> pickle.dumps(bob)
b'...some bytestring representation of Bob...'

> pickle.loads(pickle.dumps(bob))
Bob(...)

If you're using older Python (pre-3.x), then note that cPickle is faster, but pickle is more extensible. Pickle will work on some of your classes without any configuration, but for more complicated objects you might want to write custom picklers.

Git answered 15/6, 2011 at 2:52 Comment(5)
Wow, that is surprisingly easy.Undergo
Also, facility is Python 2.5.2.Undergo
Pickle is old. See docs.python.org/release/1.4/lib/…Git
Since we're already talking about pickle, you say custom picklers... If one custom class contains a list of another custom class, is that one of those situations? Given that neither is very complex, otherwise is just some ints and strings.Undergo
Probably not. I'd use a custom pickler to exclude cached values, or to allow pickling things that contain references to objects you shouldn't pickle, like sockets.Git

© 2022 - 2024 — McMap. All rights reserved.