Skip to content
This repository was archived by the owner on Aug 15, 2019. It is now read-only.

add __repr__ for Board and Markers #86

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open

add __repr__ for Board and Markers #86

wants to merge 9 commits into from

Conversation

Adimote
Copy link
Member

@Adimote Adimote commented Mar 6, 2018

Currently Board and Markers don't show pretty prints if you print(markers). They do have __str__() functions, but print() calls __repr__(), so implement __repr__()

Copy link
Contributor

@PeterJCLaw PeterJCLaw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good not to call __str__ directly -- the string formatting will do that for us.

Requesting changes due to test failures.

robot/markers.py Outdated
@@ -168,9 +168,12 @@ def spherical(self) -> SphericalCoord:
"""
return SphericalCoord(*self._raw_data['spherical'])

def __repr__(self):
return "<robot.markers.{} >".format(self.__str__())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You shouldn't need to call __str__ explicitly here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in f6b302c

robot/board.py Outdated
@@ -155,6 +155,9 @@ def close(self):
"""
self.socket.detach()

def __repr__(self):
return "<{}>".format(self.__str__())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You shouldn't need to call __str__ explicitly here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in f6b302c

def __str__(self):
bearing = self.spherical.rot_y_degrees
return "<Marker {}: {:.0f}° {}, {:.2f}m away>".format(
return "Marker {}: {:.0f}° {}, {:.2f}m away".format(
self.id,
abs(bearing),
"right" if bearing > 0 else "left",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to specify "left" and "right" here if this is a bearing?

Also, I think clockwise and anti-clockwise might be more appropriate here as they are less ambiguous.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the issue here is that 'bearing' is the wrong variable name. I don't like mentioning clockwise or counter clockwise, as it's given without context here, and they might think it's the orientation of the marker

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

either way, this isn't changed in this PR and thus should be fixed separately

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think bearing is probably fine here, if not 100% precise. The variable holds the bearing of the marker relative to the bearing of the camera. I feel using "left/right" to indicate "to the left/right of the camera" is acceptable here.

robot/markers.py Outdated
@@ -168,9 +168,12 @@ def spherical(self) -> SphericalCoord:
"""
return SphericalCoord(*self._raw_data['spherical'])

def __repr__(self):
return "<robot.markers.{} >".format(str(self))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think replacing robot.markers. with nothing will lead to a nicer output. <Marker 1: ... m away> looks quite nice

@kierdavis
Copy link
Member

IMO __repr__ should aim to produce a string that could be passed back to exec to reproduce the object - if you want a pretty string that's what __str__ is for. I'm open to objections though.

Anyone know if there's a PEP for the recommended behaviour of __repr__?

@Adimote
Copy link
Member Author

Adimote commented Mar 7, 2018

@kierdavis I tend to agree, however in this case it would be a really long string. I believe the agreed method for this is to try for something which can be passed to exec(), but if it's not possible or it produces less useful output, it's fine to do your own thing. In our case we pass radians around everywhere but it would be more intuitive for students to read if our outputs were in degrees.

@trickeydan
Copy link
Contributor

trickeydan commented Mar 7, 2018

From Python 3 docs: For many types, this function(__repr__) makes an attempt to return a string that would yield an object with the same value when passed to eval(), otherwise the representation is a string enclosed in angle brackets that contains the name of the type of the object together with additional information often including the name and address of the object.

@Adimote
Copy link
Member Author

Adimote commented Mar 7, 2018

One thing I do think should always happen though is the output of __repr__ should be unique for the reading. I don't think this means we need to give a hash of the object, but it should produce accurate enough numbers for slightly different readings to output a different __repr__

robot/board.py Outdated
@@ -155,8 +155,11 @@ def close(self):
"""
self.socket.detach()

def __repr__(self):
return "<{}>".format(str(self))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please drop redundant str call here.

>>> class Foo:
...   def __repr__(self):
...     return 'repr'
...   def __str__(self):
...     return 'str'
... 
>>> print('{}'.format(Foo()))
str

robot/markers.py Outdated
@@ -168,9 +168,12 @@ def spherical(self) -> SphericalCoord:
"""
return SphericalCoord(*self._raw_data['spherical'])

def __repr__(self):
return "<{}>".format(str(self))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please drop redundant str call here (as above).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

half-fixed (& broken something unrelated) in 7f9cfee
Then undid the stupid breakage in 515e0a3 (not sure what I was on when I thought I fixed that.)
Then the other half is fixed in d4e364e

@Adimote Adimote dismissed PeterJCLaw’s stale review March 29, 2018 22:57

please re-review, have fixed the suggested changes (after 22 days lol)

@Adimote Adimote requested a review from PeterJCLaw March 29, 2018 22:57
Copy link
Contributor

@PeterJCLaw PeterJCLaw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes look ok, though the premise of this PR is wrong -- print(x) calls __str__ not __repr__:

In [1]: class Foo:
   ...:     def __repr__(self):
   ...:         return 'repr(Foo)'
   ...:     def __str__(self):
   ...:         return 'str(Foo)'
   ...:     

In [2]: print(Foo())
str(Foo)

Note that the default __str__ calls __repr__, which may have been the source of this confusion.

I would also rather that our __repr__s behaved more like they're meant to. For the markers, I think having an abstract description is OK, though for the boards I think we should probably do something more accurate (see inline).

@@ -155,8 +155,11 @@ def close(self):
"""
self.socket.detach()

def __repr__(self):
return "<{}>".format(self)
Copy link
Contributor

@PeterJCLaw PeterJCLaw Mar 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest that this be:

    return '{}({!r})'.format(self.__class__.__name__, self.serial)

That would be truer repr behaviour, though would admittedly rely on classes which take other construction parameters overriding the default.

If we do that, we can then drop the __str__ entirely.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree, this would not be truer repr behaviour. Please see my above comment and link to the Python 3 documentation.

Copy link
Contributor

@PeterJCLaw PeterJCLaw Mar 31, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah. Apparently I meant self.socket_path, not self.serial.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In which case we might want to keep the __str__.

@PeterJCLaw
Copy link
Contributor

@Adimote was there a particular use-case or specific instance where you had seen a non-useful stringified board instance which this was trying to fix?

If not, I suggest we shelve this until after the competition.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants