0

Newbie Pythonista here.

I am having a bit of an issue. I want to print a specific output which needs to be like Point(x=1, y=2, z=3) (xyz can of course be different in values).

This is the code:

class Point:
    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

    def __str__(self):
        return f"Point(x={self.x}, y={self.y}, z={self.z})"

This is the test code:

import unittest

from point import Point


class PointTests(unittest.TestCase):

    """Tests for Point."""

    def test_attributes(self):
        point = Point(1, 2, 3)
        self.assertEqual((point.x, point.y, point.z), (1, 2, 3))
        point.x = 4
        self.assertEqual(point.x, 4)

    def test_string_representation(self):
        point = Point(1, 2, 3)
        self.assertEqual(str(point), 'Point(x=1, y=2, z=3)')
        self.assertEqual(repr(point), 'Point(x=1, y=2, z=3)')
        point.y = 4
        self.assertEqual(str(point), 'Point(x=1, y=4, z=3)')
        self.assertEqual(repr(point), 'Point(x=1, y=4, z=3)')

    def test_equality_and_inequality(self):
        p1 = Point(1, 2, 3)
        p2 = Point(1, 2, 4)
        p3 = Point(1, 2, 3)
        self.assertNotEqual(Point(1, 2, 3), Point(1, 2, 4))
        self.assertEqual(Point(1, 2, 3), Point(1, 2, 3))
        self.assertFalse(Point(1, 2, 3) != Point(1, 2, 3))
        self.assertNotEqual(p1, p2)
        self.assertEqual(p1, p3)
        p3.x, p3.z = p3.z, p3.x
        self.assertNotEqual(p1, p3)
        self.assertTrue(p1 != p3)
        self.assertFalse(p1 == p3)

The problem I am having is that in str function when I am using a "Point" in the print format I am thrown with an assertion error:

AssertionError: 'point.Point object at 0x7fbd8850b190' != 'Point(x=1, y=2, z=3)'

But when I use anything else it gets printed. For example lets say I use points instead of Point:

'points(x=1, y=2, z=3)' != 'Point(x=1, y=2, z=3)'

Why does that happen and how can I get around this?

2 Answers 2

5

The error occurrs at

self.assertEqual(repr(point), 'Point(x=1, y=2, z=3)')

Since you have customly defined only the __str__ method on __Point__, __repr__ is still the built-in-method, resulting in something like "point.Point object at 0x7fbd8850b190". To fix this, define the __repr__ of Point.

When you define the __repr__ method, python uses it as a default for __str__, but it does not do the other way around.

With your (not so minimal) code example, this will still lead to an error at

self.assertEqual(Point(1, 2, 3), Point(1, 2, 3))

This is because this line does not test for equality of the representations, but for the equality of objects. Since you instantiate Point twice, the first argument of your assertEqual is actually different from the second argument, even if they are both instances of Point and even if they are equal with regard to their instance variables.

You can understand these objects as something like "twins" or "clones": even if all their attributes are the same, they still have a different "identity".

If we want to consider "twins" as "equal", Python allows us to define an equality relation used by Python to check for equality of objects. We can define this by implementing the __eq__ method. Most probably you want something like:

class Point:
    def  __init__(x, y, z):
        self.x = x
        self.y = y
        self.z = z

    def __repr__(self):
        return f"Point(x={self.x}, y={self.y}, z={self.z})"

    def __eq__(self, other):
        return self.__repr__() == other.__repr__()

It might be a good idea to read an introduction into Python's "magic methods", such as https://rszalski.github.io/magicmethods/

Sign up to request clarification or add additional context in comments.

3 Comments

It managed to fix the "0x7fbd8850b190" part but now when running I am getting: AssertionError: Point(x=1, y=2, z=3) != Point(x=1, y=2, z=3). How does that make sense?
If I understood from your comment, I replaced str with repr . This is the traceback: Traceback (most recent call last): File "<string>", line 29, in test_equality_and_inequality AssertionError: Point(x=1, y=2, z=3) != Point(x=1, y=2, z=3)
Thank you for your feedback and your help! It worked :D
1

On my end with python 3, you have two failing tests.

======================================================================
FAIL: test_equality_and_inequality (test_point.PointTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/josef/tmp/deleteme/test_point.py", line 29, in test_equality_and_inequality
    self.assertEqual(Point(1, 2, 3), Point(1, 2, 3))
AssertionError: <point.Point object at 0x7f7a20cff050> != <point.Point object at 0x7f7a20cff090>

This one is happening because the return from declaring a new object is the object itself with its unique id. Evaluating Point(1, 2, 3) returns a unique thing like <point.Point object at 0x7f7a20cff050>, and not the class itself. So if you instantiate two instances of a class, they are necessarily different.

======================================================================
FAIL: test_string_representation (test_point.PointTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/josef/tmp/deleteme/test_point.py", line 19, in test_string_representation
    self.assertEqual(repr(point), 'Point(x=1, y=2, z=3)')
AssertionError: '<point.Point object at 0x7f7a20d75f90>' != 'Point(x=1, y=2, z=3)'
- <point.Point object at 0x7f7a20d75f90>
+ Point(x=1, y=2, z=3)

Python classes can have a __str__ method and a __repr__ method. The former is mostly used to return string representations of the object, so you have something that looks nice if you're displaying it to a human. The latter I see less used (I still usually define it anyhow) but is to my understanding supposed to enable a "object-to-representation-to-object" roundtrip; to display the instance in such a way that you could in theory just eval() that string representation and faithfully reproduce the object.

Your class definition doesn't have a __repr__ so that test of course fails. Sometimes (for large values of sometimes) if I'm lazy I'll do this:

def __str__(self):
    return self.__repr__()

3 Comments

This is not the way of doing, it. You should implement __repr__ and str will rely on it without needing to implement it
It fixed the weird 0xXXXXXXXX part but now I am getting AssertionError: Point(x=1, y=2, z=3) != Point(x=1, y=2, z=3). They are the same for as much as I know
That's because it's still comparing instance to instance I think, not textual representation to textual representation. :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.