GalSim-developers/GalSim — pull request #157 by barnabytprowe (2012-05-19 03:38:26+00:00)
Title: #129 - Added repr() and str() methods to position, bounds, shear and ellipse classes

Hi all,

I've added `__repr__` and `__str__` methods to the the position, bounds, shear and ellipse classes. This is for convenience while developing, and while writing these I actually useful for seeing at first hand what these classes are storing and how they work.

A little word for the non-Python gurus about what these methods do (from the official Python docs)...

* object.__repr__(self)

Called by the repr() built-in function and by string conversions (reverse quotes) 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.

* object.__str__(self)

Called by the str() built-in function and by the print statement to compute the “informal” string representation of an object. This differs from __repr__() in that it does not have to be a valid Python expression: a more convenient or concise representation may be used instead. The return value must be a string object.

As an example of this working in practice, for the `PositionD` class (for storing 2D positions as doubles):
```
In [1]: import galsim

In [2]: p = galsim.Pos
galsim.PositionD galsim.PositionI

In [2]: p = galsim.PositionD(3., 4.)

In [3]: p
Out[3]: PositionD(3.0, 4.0)

In [4]: print p
(3.0, 4.0)

In [5]: p+p
Out[5]: PositionD(6.0, 8.0)

In [6]: print p+p
(6.0, 8.0)

In [7]: c = eval("galsim."+repr(p+p))

In [8]: c
Out[8]: PositionD(6.0, 8.0)
```
Note, that I've here adopted the same usage as numpy, where the output of `repr()` on an array is, for example, `array([1, 2, 3])` rather than `numpy.array([1, 2, 3])` (I guess the convention is that as the user chooses which namespace the module contents are imported into, only the lowest level possible is assumed for `repr()` output).

Examples of the bounds, shear and ellipse outputs are as follows (this is totally up for debate):

For the bounds...
```
In [9]: b = galsim.BoundsI(0,100,0,100)

In [10]: b
Out[10]: BoundsI(0, 100, 0, 100)

In [11]: print b
(xMin, xMax, yMin, yMax) = (0, 100, 0, 100)
```

For the shear...
```
In [12]: s=galsim.Shear(e1=0.1, e2=0.4)

In [13]: s
Out[13]: Shear(e1=0.1, e2=0.4)

In [14]: print s
(e1=0.1, e2=0.4)
```

For the ellipse...
```
In [1]: import galsim

In [2]: e = galsim.Ellipse(e1=0., e2=0.3, mu=1.3, x=2., y=3.)

In [3]: e
Out[3]: Ellipse(e1=0.0, e2=0.3, mu=1.3, x=2.0, y=3.0)

In [4]: print e
(e1=0.0, e2=0.3, mu=1.3, x=2.0, y=3.0)
```

Quick additional notes:

* I renamed the kwarg `m` in the Python wrapper for Ellipse into `mu`... This then matches the `getMu()` method that was also wrapped, and I think is clearer.

* The `Shear` and `Ellipse` both use the (a^2 - b^2) / (a^2 + b^2) e1e2-type ellipticities as their stock in trade for input and output, so that is what I print. I've added a TODO in the shear.py file to keep this updated with whatever gets decided in Issue #134

Overall, I think this is a minor, yet useful, addition to the wrapped Python classes, and just doing it gave me a clearer picture of the data structures themselves. I'm now wondering where else we might want to define `__repr__` and `__str__` methods for our classes...

Comments / revisions to the above definitely welcomed - I'm not sure if I've accidentally contravened some usual Python conventions in the `__str__` methods, for example, and would welcome comments from the usual Python experts / any interested parties. Either way, as this addition is somewhat minor but handy I felt it was appropriate to open a pull request.