What does it mean if a Python object is “subscriptable” or not?

后端 未结 6 2137
情深已故
情深已故 2020-11-22 06:05

Which types of objects fall into the domain of \"subscriptable\"?

6条回答
  •  野趣味
    野趣味 (楼主)
    2020-11-22 06:30

    As a corollary to the earlier answers here, very often this is a sign that you think you have a list (or dict, or other subscriptable object) when you do not.

    For example, let's say you have a function which should return a list;

    def gimme_things():
        if something_happens():
            return ['all', 'the', 'things']
    

    Now when you call that function, and something_happens() for some reason does not return a True value, what happens? The if fails, and so you fall through; gimme_things doesn't explicitly return anything -- so then in fact, it will implicitly return None. Then this code:

    things = gimme_things()
    print("My first thing is {0}".format(things[0]))
    

    will fail with "NoneType object is not subscriptable" because, well, things is None and so you are trying to do None[0] which doesn't make sense because ... what the error message says.

    There are two ways to fix this bug in your code -- the first is to avoid the error by checking that things is in fact valid before attempting to use it;

    things = gimme_things()
    if things:
        print("My first thing is {0}".format(things[0]))
    else:
        print("No things")  # or raise an error, or do nothing, or ...
    

    or equivalently trap the TypeError exception;

    things = gimme_things()
    try:
        print("My first thing is {0}".format(things[0]))
    except TypeError:
        print("No things")  # or raise an error, or do nothing, or ...
    

    Another is to redesign gimme_things so that you make sure it always returns a list. In this case, that's probably the simpler design because it means if there are many places where you have a similar bug, they can be kept simple and idiomatic.

    def gimme_things():
        if something_happens():
            return ['all', 'the', 'things']
        else:  # make sure we always return a list, no matter what!
            logging.info("Something didn't happen; return empty list")
            return []
    

    Of course, what you put in the else: branch depends on your use case. Perhaps you should raise an exception when something_happens() fails, to make it more obvious and explicit where something actually went wrong? Adding exceptions to your own code is an important way to let yourself know exactly what's up when something fails!

    (Notice also how this latter fix still doesn't completely fix the bug -- it prevents you from attempting to subscript None but things[0] is still an IndexError when things is an empty list. If you have a try you can do except (TypeError, IndexError) to trap it, too.)

提交回复
热议问题