1

I am writing a simple Python class named "Bag":

class Bag:
def __init__(self, items=None):
    if self.items == None:
        self.items = defaultdict()
    else:
        temp_dict = defaultdict()
        for key in self.items:
            temp_dict[key] += 1
        self.items = temp_dict 

The variable items takes in a list of objects, such as:

['d','a','b','d','c','b','d']

From there, "def __init__(self, items=None)" will either:

  1. Initialize items as an empty defaultdict, if nothing was passed into items, or
  2. Initialize the passed in argument, if a list of objects were passed into items.

For example, this should work:

b = Bag()

The absence of an argument should be fine, as items is set, by default, to _None.

However, this always raises an exception(from a script that checks for errors):

*Error: b = Bag() raised exception AttributeError: 'Bag' object has no attribute 'items'

I want to initialize Bag() without putting a passing an argument into items.

Hope everything is clear, tell me if it isn't.

Any ideas or anything wrong with the code?

Justin Yum
  • 189
  • 2
  • 12

2 Answers2

2
from collections import defaultdict

class Bag(object):

def __init__(self, items=None):
    self.items = items or list()
    if not self.items:
        self.items = defaultdict()
    else:
        temp_dict = defaultdict()
        for key in self.items:
            temp_dict[key] += 1
        self.items = temp_dict

I made a few corrections:

1) create self.items and initialize with items 2) compare with None using is None , or simply not self.items, since empty lists evaluate to False

labheshr
  • 2,858
  • 5
  • 23
  • 34
  • 3
    Beware of the mutable default. http://stackoverflow.com/questions/1132941/least-astonishment-and-the-mutable-default-argument – OneCricketeer Apr 22 '17 at 02:22
  • 1
    Please never use a mutable value as a default for an argument. It is cerated a t function creation time and _shared across invocations,_ so if your function happens to update `items`, the next call will see it. Use `None` as default, then use `items or []` (or `iems or defaultdict(int)` in your case) when accessing it. – 9000 Apr 22 '17 at 02:23
  • @labheshr Thank you! I made the changes, and it worked. I realized that I never initialized self.items before the if-else statement. – Justin Yum Apr 22 '17 at 02:26
2

'Bag' object has no attribute 'items' means: somewhere in the Bag's method you're accessing self.items that has not been defined.

And indeed you do. In the constructor, you write: if self.items == None: before you make the first assignment to self.items, thus creating it.

I think it's a typo, and you meant if items is None:, referring to the parameter.

This code can be simplified, though. My take:

import collections  # Batteries included.

...
def __init__(self, items=None):
    if not items:  # Covers both None and empty list.
        self.items = {}
    else:
        self.items = collections.Counter(items)

This can be simplified even more:

     ...
     self.items = collections.Counter(items or [])

If you strictly want a to use a defaultdict, you can:

     self.items = collections.defauldict(int, collections.Counter(items or []))

(Note that defaultdict() without an argument makes rather little sense.)

9000
  • 39,899
  • 9
  • 66
  • 104