0

I am having some issues concerning the interaction between functions, methods and have a uncertainty of how to utilize the use of the built-in function __str__.

To start this of, I have a class called vara, which looks like:

class vara(object):

    def __init__(self, kod, namn, pris, butikantal):
        self.kod = kod
        self.namn = namn
        self.pris = pris
        self.antal = butikantal

and i have a function that creats a list of object for the vara klass, that looks like:

def mina_varor():

  varor_fran_fil = open("varor.txt", "r")
  varulista = []
  for rad in varor_fran_fil:
      varje_vara1 = rad
      varje_vara2 = varje_vara1.split("/")
      varorna = vara(varje_vara2[0], varje_vara2[1], varje_vara2[2], varje_vara2[3])
      varulista.append(varorna)

  return(varulista)

Now i want to be able to access a singel object in this list by simply typing then "kod" for the object. But I cant find any "kod" in my list. It seemed strange, so i tried printning the list, and got that:

[<__main__.vara object at 0x00000000031503C8>, <__main__.vara object at 0x00000000031507F0>, <__main__.vara object at 0x0000000003150710>, <__main__.vara object at 0x00000000031502B0>, <__main__.vara object at 0x00000000031505F8>, <__main__.vara object at 0x00000000031504E0>]

i looked it up, and it seems that python cant decide how to interpret my list. I think i need an __str__ method to do so, but how should i make my __str__ method look like if i want it to print something like:

Name: Hat
Price: 150
Quantity: 100
Code: 223

?

Pavel Anossov
  • 60,842
  • 14
  • 151
  • 124
Bardolf
  • 17
  • 4

2 Answers2

1

Issue 1: __str__() not being called when printing a container

It's not that Python can't interpret your list, its that it doesn't know how you expect it to be displayed.

Implement the __str__ method if you want to do something like

print(varulista[0])

Something like the following would give you something like what you expect for that list entry:

def __str__(self):
    s  = "Name:    %s\n" % self.namn
    s += "Price:   %s\n" % self.pris
    s += "Quantity:%s\n" % self.antal
    s += "Code:    %s\n" % self.kod

But if you want print(varulista) to make any sense, you'll also have to implement the __repr__ method, since varulista is a list and when printing it Python looks for the __repr__ method.

But that being said, the idea of __repr__ is to be able to pass it back into eval() and create an equivalent object. So you could go one of two ways:

1. Ignore the __repr__()/eval() interaction

In this case, just implement __repr__() however you want. Remember that this method is going to be called when it is an item in a container you're printing, so keeping the output to a single line might be helpful. Just know that unless you unambiguously represent the state of the object in the __repr__() output, you won't be able to recreate an equivalent object "down the road" using eval().

2. (Preferred option, IMO) Implement __repr__() correctly and simply don't print containers if you don't like the way it looks.

If output like

[Item{Name:"Hat",Price:150,Quantity:100,Code:223},Item{Name:"Shirt",Price:450,Quantity:10,Code:225}]

is too unfriendly because of how you had to implement __repr()__, just don't print containers. In other words, don't use

print(varulista)

but instead use

for item in varulista: print item

which would call the __str__() method you defined, the human-friendly one.

EDIT: @bernie's links to a great answer by @AlexMartelli regarding the difference between __str__ and __repr__ that is worth linking twice.


Issue 2: being able to access a list element by it's code

You say

Now i want to be able to access a singel object in this list by simply typing then "kod" for the object. But I cant find any "kod" in my list. It seemed strange, so i tried printning the list, and got that:

You have two main options here. (1) Use a dictionary keyed on the item code. (2) Search through list for code using list comprehension. I'm only going to show (1), because I think it's a much better alternative.

Option 1: Use a dictionary keyed on the item code

Consider this code:

# Define a convenience function to add items to the dictionary
def add_item(d,i): d[i.kod] = i

# Create a new dictionary
items = {}

# Add items to dictionary
add_item(items, vara(223, "Hat", 150, 100))
add_item(items, vara(225, "Shirt", 450, 10))

You can now access any defined item from the items dictionary as follows:

items[223] #<-- This is the "Hat" item
items[225] #<-- This is the "Shirt" item
Community
  • 1
  • 1
jedwards
  • 29,432
  • 3
  • 65
  • 92
  • Wow great help and well explained for someone like me who is pretty much a beginner at python! I only got 2 things I would like to ask you about. 1: I liked the idea about using for item in varulista, but you said that by doing so, i would call the __str__() method. I didnt see you calling it anywhere and got kind of confused. If i only make the for-loop, I won't call the __str__() method. I have to have some more code, right? 2: in the second part, about doing a dictionary. It would seem that i have to add each object to the dict. Can't i simply do a for-loop, that adds each line of my list – Bardolf Apr 05 '13 at 17:46
  • 1. `__str__()` is implicitly called by Python when it tries to convert your `vara` object to a string. So you don't need to call it, `print()` does it for you in that case. – jedwards Apr 05 '13 at 17:49
  • 2. Yes, you would have to add each item in some way. My suggestion was to skip the middle step of using the list, but if you have a list, a for loop like you describe would be fine. Maybe: `for item in varulista: add_item(items, item)` or even just `for item in varulista: items[item.kod] = item`, the second option eliminating the use of the `add_item` convenience function. – jedwards Apr 05 '13 at 17:52
  • Great help indeed! Have to try these out, will be back with some feedback after I have experimented a bit. Thanks a lot! – Bardolf Apr 05 '13 at 17:54
  • Works like a charm, recommend people with similar promblem to try these codes out! – Bardolf Apr 09 '13 at 14:52
0

You could overide __repr__ like this:

class vara():
    def __init__(self, kod, namn, pris, butikantal):
        self.kod = kod
        self.namn = namn
        self.pris = pris
        self.antal = butikantal

    def __repr__(self):
        return 'Name: %s\nPrice: %s\nQuantity:%s\nCode: %s\n'\
               % (self.namn, self.pris, self.antal, self.kod)

output:

>>> print([vara('223', 'Hat', '150', '100'), vara('115', 'Bla', '154', '200')])
[Name: Hat
Price: 150
Quantity:100
Code: 223
, Name: Bla
Price: 154
Quantity:200
Code: 115
]

Edit:

But as explained by @jedwards, it is a better approach to override __str__ just the same way I did for __repr__ and print each object individually rather than printing the whole list.

jurgenreza
  • 5,856
  • 2
  • 25
  • 37
  • 1
    This is not really a good approach but more of a lazy kluge. It suggests breaking the contract of `__repr__()` for no reason / without any discussion about alternatives. – jedwards Apr 05 '13 at 16:19
  • @jedwards No doubt that your answer is complete and informative but the OP may be looking for something simple to fix the problem quickly. My asnwer isn't wrong, is it? No need to vote down other answers to promote your own. – jurgenreza Apr 05 '13 at 17:21
  • It's not to promote my own answer, but if you want to edit your answer (just add a space somewhere so I can revoke my vote), I'll revoke it for CoI. – jedwards Apr 05 '13 at 17:22
  • @jedwards I see your point anyway, a little more explanation doesn't hurt. :) – jurgenreza Apr 05 '13 at 17:34