Question: I'm having trouble to define the `__delitem__` or similar method for the class `Container` in the example below. How can I rectify this? Thx.

```
import numpy as np
import pandas as pd

class XLData(object):
    def __init__(self, name):
        self.name = name
        self.data = pd.DataFrame({self.name: list("ASDF" * 2),
                                  'x': np.random.randint(1, 100, 8) })
    def __repr__(self):
        return repr(self.data.head(2))

class Container(object):
    def __init__(self):
        self.counter = 0
        self.items = []
    def append(self, item):
        self.counter += 1
        self.items = self.items + [item]
    def __delitem__(self, name):
        for c in self.items:
            print("element name:{}, to delete:{}".format(c.name, name))
            if c.name == name:
                pass #!
                #del c
    def __iter__(self):
        for c in self.items:
            yield c

a = XLData('a')
b = XLData('b')
c = XLData('c')

dl = Container()
dl.append(a)
dl.append(b)
dl.append(c)
del dl['b']

for c in dl:
    print(c)
# 'b' is still in ..

```

Answer: 
As you seem to be aware, given your code's comments, you can't usefully do `del c` in your loop, because that only removes the `c` variable from the function's local namespace temporarily, it doesn't change the list structure at all.

There are a few different ways you could make it work.

One idea would be to use `enumerate` while looping over the values in the list, so that you'll have the index at hand when you need to delete an item from the list:

```
for i, item in enumerate(self.items):
    if item.name == name:
        del self.items[i]
        return

```

Note that I return from the function immediately after deleting the item. If multiple items with the same name could exist in the list at once, this may not be what you want, but this code can't properly handle that case because once you delete an item from the list, the iteration won't work properly (it will let you keep iterating, but it will have skipped one value).

A better option might be to rebuild the list so that it only includes the values you want to keep, using a list comprehension.

```
self.items = [item for item in self.items if item.name != name]

```

That's nice and concise, and it will work no matter how many items have the name you want to remove!

One flaw of both of the approaches above share is that they'll be fairly slow for large lists. They need to iterate over all the items, they can't tell ahead of time where the item to remove is stored. An alternative might be to use a dictionary, rather than a list, to store the items. If you use the item names as keys, you'll be able to look them up very efficiently.

Here's an implementation that does that, though it only allows one item to have any given name (adding another one will replace the first):

```
class Container(object):
    def __init__(self):
        self.counter = 0
        self.items = {}                # create a dict!
    def append(self, item):
        self.counter += 1
        self.items[item.name] = item   # add items to it, keyed under their names
    def __delitem__(self, name):
        del self.items[name]           # this becomes *really* simple, and efficient
    def __iter__(self):
        for c in self.items.values():    # loop over the dict's values to the items
            yield c

```
It is a good idea not to modify the array we are looping over in the loop itself. So just pick the index of the item and delete it outside the loop.

```
def __delitem__(self, name):
    idx = -1
    found = False
    for c in self.items:
        idx += 1
        print("element name:{}, to delete:{}".format(c.name, name))
        if c.name == name:
            found = True
            break
    if found:
        del self.items[idx]

```