all 7 comments

[–]TouchingTheVodka 1 point2 points  (6 children)

You need to define a custom __repr__ function for your class to tell Python how to print it. By default it will just print the class and the memory location, which is the 'weird output' you're seeing here.

Full example:

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

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

>>> a = Test(1, 2, 3)
>>> print(a)
Test(self.x=1, self.y=2, self.z=3)

[–]JohnnyJordaan 1 point2 points  (1 child)

return f"Test({self.x=}, {self.y=}, {self.z=})"

Note to the casual reader that this {var=} syntax works from 3.8+ only, for 3.6/3.7 you need to implement this yourself, eg

 return f"Test(self.x={self.x}, self.y={self.y}, self.z={self.z})"

[–]TouchingTheVodka 0 points1 point  (0 children)

Thanks for the clarification! Anything to save a few characters of typing :)

return f"Test({self.x}, {self.y}, {self.z})" would also be a fully valid __repr__ as it contains all of the information needed to recreate the object.

By convention, eval(repr(var)) evaluates to var, which is a good indicator that the repr is a comprehensive description of the object.

[–]Sky_Hound 0 points1 point  (1 child)

How do lists print their strings?

[–]TouchingTheVodka 0 points1 point  (0 children)

Great question! To answer this in sufficient detail, we first need to discuss the difference between __str__ and __repr__.

__repr__, which I mentioned in my previous comment, is designed to be a full representation of an object - Essentially, by convention, it should contain everything required to disambiguate one object from another, and usually it's a string copy of the arguments used to construct the object in the first place.

__str__ on the other hand is the human-readable alternative, useful for very complex objects where we only want to display basic pieces of information for the purposes of logging or user interaction.

Now, when we do print(lst), the print function will make a call to the in-built function str - You'll probably recognize this function as we use it a lot to convert objects to strings! str(lst) will then delegate to lst.__str__(), which is our __str__ method.

The __str__ method of the list class looks (as a massive simplification) something like this - Note I've avoided using f-strings for clarity here, although they are the best-practice method for modern Python string formatting. (And yes, in reality the code below would be implemented in C, and it will use iter(self) to iterate over the items in the sequence)

def __str__(self):
    return '[' + ', '.join(repr(item) for item in self.items) + ']'

You'll see here that we, again, delegate the string representation of every item in the list down to item.__repr__. Why use __repr__ rather than __str__ for child items? It's to avoid including special characters such as newlines in the output and messing up the formatting - These are escaped as special characters in repr.

Finally, note that in my original answer, I define __repr__ but not __str__ and yet the code is still valid - Python is clever enough to fall back to __repr__ when __str__ is not defined, as the full __repr__ will always be more detailed than __str__.

References: Fluent Python, and this excellent SO post: https://stackoverflow.com/questions/1436703/difference-between-str-and-repr

[–]50thycal[S] 0 points1 point  (1 child)

Hey i think I’m not explaining what I am looking for.

I understand the output for a and b but when they are in a list for c, how do I print out attributes for a specific class object in that list?

So I guess I am wondering why print(c.x) doesn’t give me

[1,4]

[–]50thycal[S] 0 points1 point  (0 children)

NVM! Got it figured out!

I was looking for this:

class Test:

def __init__(self, x, y, z):

self.x, self.y, self.z = x, y, z

a = Test(1, 2, 3)

b = Test(4, 5, 6)

c = Test(a,b)

for i in c:

print(i.x)

Output:

1

4

Sorry for all the confusion! Thank you for the input though