Best practise implementation for equal by value objects

Slaunger Slaunger at gmail.com
Thu Aug 7 08:16:52 CEST 2008


On 6 Aug., 21:36, Terry Reedy <tjre... at udel.edu> wrote:
> Slaunger wrote:
> > Hi,
>
> > I am new here and relatively new to Python, so be gentle:
>
> > Is there a recommended generic implementation of __repr__ for objects
> > equal by value to assure that eval(repr(x)) == x independet of which
> > module the call is made from?
>
> The CPython implementation gives up on that goal and simply prints
> <modname.classname object at address> for at least two reasons ;-).
>
> 1. In general, it require fairly sophisticated analysis of __init__ to
> decide what representation of what attributes to include and decide if
> the goal is even possible.  If an attribute is an instance of a user
> class, then *its* __init__ needs to be analyzed.  If an attribute is a
> module, class, or function, there is no generic evaluable representation.

OK, the situation is more complicated than that then. In the case here
though,
the attributes would always be sinmple bulit-in types, where
eval(repr(x))==x
or, where the attribute is a user-defined equal-by-value class, that I
have
control over.

The classes I am making as struct type classes with some added
functionlity for
human readable string representation, packing into a stream or
unpacking from a stream
using a "private" class Struct.

I come from a Java and JUnit world, where, if I am used to always
overriding the default reference based implementations of the
equals(), toString(),
and hashCode() methods for "equals-by-value" objects such that they
work well
and efficient in, e.g., hash maps.

With my swich-over to Python, I looked for equivalent features and
stumbled over the
eval(repr(x))==x recommendation. It is not that I actually (yet) need
the repr implementations,
but mostly because I find the condition very useful in PyUnit to check
in a test that I have remembered
to initialize all instance fields in __init__ and that I have
remembered to include all relevant
attributes in the __eq__ implementation.

Whereas this worked fine in a unit test module dedicated to only test
the specific module, the test failed
when called from other test package modules, wrapping the unit tests
from several unit test modules.

>
> 2. Whether eval(repr(x)) even works (returns an answer) depends on
> whether the name bindings in the globals and locals passed to eval
> (which by default are the globals and locals of the context of the eval
> call) match the names used in the repr.  You discovered that to a first
> approximation, this depends on whether the call to repr comes from
> within or without the module containing the class definition.  But the
> situation is far worse.  Consider 'import somemod as m'.  Even if you
> were able to introspect the call and determine that it did not come from
> somemod**, prepending 'somemod.' to the repr *still* would not work.
> Or, the call to repr could come from one context, the result saved and
> passed to another context with different name bindings, and the eval
> call made there.  So an repr that can be eval'ed in any context is hopeless.
>
Ok, nasty stuff

> If this is a practical rather than theoretical question, then use your
> first repr version that uses the classes definition name and only eval
> the result in a context that has that name bound to the class object.
>
> from mymod import Age
> #or
> import mymod
> Age = mymod.Age
>
> #in either case
> eval(repr(Age(10))) == Age(10)
>
> > class Age:
>
> >     def __init__(self, an_age):
> >         self.age = an_age
>
> >     def __eq__(self, obj):
> >         self.age == obj.age
>
> >     def __repr__(self):
> >         return self.__class__.__name__ + \
> >                "(%r)" % self.age
>
Yes, it is most from a practicl point of view, altough I was surprised
that I could not find more material on it in the Python documentation
or mailing groups, and I moight just do what you suggest in the unit
test modules to at least make it robust in that context.

Hmm... a bit of a dissapointment for me that this cannot be done
cleaner
> **
> While such introspection is not part of the language, I believe one
> could do it in CPython, but I forgot the details.  There have been
> threads like 'How do I determine the caller function' with answers to
> that question, and I presume the module of the caller is available also.
OK, I think CPython, for the moment, is too much new stuff to dig into
right now.
Just grasping some of all the possibilities in the API, and how to do
things the right way
is giving me enough challenges for now...

>
> Terry Jan Reedy

Again, thank you for your thorough answer,

Slaunger



More information about the Python-list mailing list