I would read up on the __repr__
dunder method. Classes innately don’t have some print defined on them other than the object and address location, because how would Python know what’s important about representing that class?
A toy example might help:
Let’s try without repr, define a constructor and instantiate 2 objects:
class Foo:
def __init__(self, name):
self.name = name
foo = Foo("i am the first foo")
foo2 = Foo("i am the second foo")
print(foo)
# <__main__.Foo object at 0x7feee742a700>
# which is the name of the object and the hexademical address
# where it is stored in the heap in memory
print(foo2)
# <__main__.Foo object at 0x7feee742a9d0>
# notice this is not the same address, since it is
# a different instance of the same Class of object
Great, now with repr!
class Foo:
def __init__(self, name):
self.name = name
def __repr__(self):
return self.name + " @ " + hex(id(self))
foo = Foo("i am the first foo")
foo2 = Foo("i am the second foo")
print(foo)
# i am the first foo @ 0x7feee765c130
print(foo2)
# i am the second foo @ 0x7feee765c7c0
Note the addresses are different from the first time we printed them, because every new allocation is not going to be in the same place (if you’re curious about the heap: 9.1: Heap Dynamic Memory - Engineering LibreTexts)
The heap details might seem like they’re a bit extra, but when it comes to object equality, assignment, and copies, it’s quite important to know what happens. Two new instances of foo initialized to the same value are not the same by default because they are in different locations in memory, and therefore considered different entities. There are ways to override that!! (The __eq__
method but that is a tangent indeed).
A tangent that’s maybe useful if you’re looking at nodes.
class Foo:
def __init__(self, name):
self.name = name
def __eq__(self, other):
return self.name == other.name
foo = Foo("1")
foo2 = Foo("1")
print(foo == foo2)
# True !
Notice i defined the custom conditions for the class’ equality. It is not the default behavior.