1

EDIT: This got flagged as a duplicate because of my asking about how to properly print a class item. That was part of my question yes, but I was more confused about the interaction of object instances and the way things are referenced in general. This has been helpful. Also, I should have done it originally, but I've included MITx's (online mooc) solution for the 'bag' creation of the 0/1 knapsack problem I've been learning. It probably makes it more clear why they wrote the class and buildItems function as they did.

I've read a gazillion tutorials now to try to understand the object referencing and creation of class instances in python and can't figure out why I'm getting this particular output that I haven't seen.

I have this program snippet:

class Item(object):
     def __init__(self, n, v, w):
         self.name = n
         self.value = float(v)
         self.weight = float(w)
     def getName(self):
         return self.name
     def getValue(self):
         return self.value
     def getWeight(self):
         return self.weight
     def __str__(self):
         return '<' + self.name + ', ' + str(self.value) + ', '\
                    + str(self.weight) + '>'


def buildItems():
    return [Item(n,v,w) for n,v,w in (('clock', 175, 10),
                                      ('painting', 90, 9),
                                      ('radio', 20, 4),
                                      ('vase', 50, 2),
                                      ('book', 10, 1),
                                      ('computer', 200, 20))]

EDIT (rest of code)

def yieldAllCombos(items):
    N = len(items)
    # Enumerate the 3**N possible combinations
    for i in range(3**N):
        bag1 = []
        bag2 = []
        for j in range(N):
            if (i // (3 ** j)) % 3 == 1:
                bag1.append(items[j])
            elif (i // (3 ** j)) % 3 == 2:
                bag2.append(items[j])
        yield (bag1, bag2)

Edit end

These are the parts of a introductory knapsack problem I've been working on, this problem made me realize I didn't understand what is really going on with classes and object instances.

If I understand buildItems(), it will create 6 different instances of the Item class and return a list containing those instances.

Trying to do something simple, I want to create that single list containing those default values in the buildItems() function. Just to test I do this:

myitems = buildItems()
print(myitems)

I'm expecting to see a list of lists like this:

[[clock,175,10], [painting,90,9], [radio,20,4], [vase, 50, 2], [book, 10, 1], [computer, 200, 20]]

What I get back though is:

[[<__main__.Item object at 0x7f2ce7819650>, <__main__.Item object at 0x7f2ce67cfa90>, <__main__.Item object at 0x7f2ceb766a50>, <__main__.Item object at 0x7f2ceb766d10>, <__main__.Item object at 0x7f2cebc9ba90>, <__main__.Item object at 0x7f2ceb4ec210>]]

Which I realize are the actual objects and there locations in memory, correct? What do I need to do return the actual data? Also, I have tried putting both buildItems inside and outside the class definition, if I put it inside, it requires me to include the self in the def buildItems(self), but then more confusing for me, if I do this:

items = Item.buildItems()

I get:

TypeError: buildItems() missing 1 required positional argument: 'self'

It was my understanding that 'self' was always implied. If I do this:

items = Item.buildItems(self)

it returns an "error 'self' is undefined" obviously.

I'm just very confused, I can't seem to figure out what is different about this example and when I was learning about classes originally.

Any help guiding me through this would be greatly appreciated, I feel like it would unlock a lot conceptually for me.

Mahendra Gunawardena
  • 1,956
  • 5
  • 26
  • 45
spence
  • 67
  • 7
  • 1
    Possible duplicate of [How to print instances of a class using print()?](https://stackoverflow.com/questions/1535327/how-to-print-instances-of-a-class-using-print) – Carcigenicate Oct 24 '19 at 22:56
  • It's like how in C++ you need to override `<<` to tell it how to print. In Python, you need to define the class's `__repr__` instead of `__str__`. You're doing it fine, it's just printing the default representation instead of pretty printing. You could also use a `dataclass` or `NamedTuple` to automatically get pretty-printing for your class. – Carcigenicate Oct 24 '19 at 22:56
  • If your objects are stored in a list then `myitems[0].name` will return the name value of the first object in the list so clock – Matthew Barlowe Oct 24 '19 at 22:57
  • You are seeing "the actual data". There is nothing special here, other than your class not implementing any `__repr__` method, so it uses the default one. – juanpa.arrivillaga Oct 25 '19 at 00:22
  • You should ask yourself **why** are you expecting to see a list of lists, `[[clock,175,10], [painting,90,9], [radio,20,4], [vase, 50, 2], [book, 10, 1], [computer, 200, 20]]` when you seem to understand that you haven't created any `list` objects, rather, you created `Item` objects. – juanpa.arrivillaga Oct 25 '19 at 00:24
  • ok, that makes sense, I realized I was probably missing something simple, which is why I was misunderstanding my output. I didn't realize the proper way to call `__str__` method in the class, for `str` output. – spence Oct 25 '19 at 00:28
  • yes, I guess I was misunderstanding the code in general, (its MITx's code for a problem exercise), the **why** is important I realize, which was what I wanted to understand, `Item` objects vs `list` objects. Thanks! – spence Oct 25 '19 at 00:30

3 Answers3

1

There are several things to consider here:

  1. your function buildItemsis not part of your Item class. That means calling Item.buildItems() doenst work. This should give the error AttributeError: type object 'Item' has no attribute 'buildItems'. Your provided error message is probably due to the fact that the indentation is actually different?

  2. your function __str__(self) will be called if you actually convert your items into string as follow:

    myitems = buildItems()
    print(str(myitems[0])) # results in '<clock, 175.0, 10.0>'
    
  3. However you are calling print to the list of objects and there the __repr__(self) function will be used to generate a string for each of your elements inside the list. Thus you need to override the __repr__(self) function as follows:

    class Item(object): 
          def __init__(self, n, v, w): 
              self.name = n 
              self.value = float(v) 
              self.weight = float(w) 
          def getName(self): 
              return self.name 
          def getValue(self): 
              return self.value 
          def getWeight(self): 
              return self.weight 
          def __str__(self): 
              return '<' + self.name + ', ' + str(self.value) + ', '\ 
                         + str(self.weight) + '>' 
          def __repr__(self): 
              return str([self.name, self.value,self.weight])
    

EDIT:

This would produce the output as you wanted. However this disagrees with the intention behind the __repr__ function as mentioned in the comment by @ShadowRanger. A better way to achieve an ouptut which could by copy pasted into the terminal is as follows:

def __repr__(self): 
    return self.__class__.__name__+ repr((self.name, self.value,self.weight))

`

v.tralala
  • 1,444
  • 3
  • 18
  • 39
  • Note: This is a *terrrible* `__repr__` implementation. When at all possible, the `repr` should be something you could paste into the terminal to recreate the class. [See the canonical subclass-friendly version here](https://stackoverflow.com/q/44342081/364696). The OP should not be trying to make a `list` of `Item`s look like a `list` of `list`s in the first place; if that's what they need, then don't convert to `Item` in the first place, or convert back to `list` on demand. – ShadowRanger Oct 24 '19 at 23:21
  • @ShadowRanger Ah thank you for the explanation! Happy to have learned something again. So the correct way would be `self.__class__.__name__+ repr((self.name, self.value,self.weight))` ? Almost the same as the third version of ChrisDoyle's answer? ` – v.tralala Oct 24 '19 at 23:30
  • this has all been very helpful, I actually didn't write any of this code, I'm taking the MITx python course on Edx and this is their code from one of their problem exercises. – spence Oct 25 '19 at 00:23
1

Your already on your way and have already defined a __str__(self) method. This overrides the default object __str__ method. So any time you print the item it will print in the format you have defined, infact any time you try to use your class in a stringwise fashion it will call this method.

However when you have them in a list and print the list, the object represenation will call the __repr__ method. This is normally meant to be a representation of how to create this instance but can be anything you like it to be.

if you want to just return your same string representation you can just do

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

if you want your output to be as you showed in your question to look like a list

 def __repr__(self):
     return str([self.name, self.value, self.weight])

if you want to represent your class as it was constructed

 def __repr__(self):
     return self.__class__.__name__ + "(" + ",".join((self.name, str(self.value), str(self.weight))) + ")"

OUTPUTS

[<clock, 175.0, 10.0>, <painting, 90.0, 9.0>, <radio, 20.0, 4.0>, <vase, 50.0, 2.0>, <book, 10.0, 1.0>, <computer, 200.0, 20.0>]
[[clock,175.0,10.0], [painting,90.0,9.0], [radio,20.0,4.0], [vase,50.0,2.0], [book,10.0,1.0], [computer,200.0,20.0]]
[Item(clock,175.0,10.0), Item(painting,90.0,9.0), Item(radio,20.0,4.0), Item(vase,50.0,2.0), Item(book,10.0,1.0), Item(computer,200.0,20.0)]

So you need to override the __repr__ method but how it presents itself is really up to you.

Chris Doyle
  • 10,703
  • 2
  • 23
  • 42
  • 2
    Note: If you want the `repr` and `str` form to be identical, don't implement `__repr__` in terms of `__str__`, just implement *only* `__repr__`; the default `__str__` implementation inherited from `object` automatically uses the `__repr__` for you as long as you don't override it. – ShadowRanger Oct 25 '19 at 00:25
0

A section of your question ask's how to develop code like below

myitems = buildItems()
print(myitems)

to generate an output like below

[[clock,175,10],
[painting,90,9], 
[radio,20,4], 
[vase, 50, 2], 
[book, 10, 1], 
[computer, 200, 20]]

Below is a code snip that would generate similar output.

myitems = buildItems()
for i in range(len(myitems)):
    print(str(myitems[i]))

Output using __str__ define in the Item object

<clock, 175.0, 10.0>
<painting, 90.0, 9.0>
<radio, 20.0, 4.0>
<vase, 50.0, 2.0>
<book, 10.0, 1.0>
<computer, 200.0, 20.0>

Reference:

Mahendra Gunawardena
  • 1,956
  • 5
  • 26
  • 45