What does it mean if a Python object is "subscriptable" or not?
Asked Answered
W

8

636

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

Wednesday answered 19/10, 2008 at 21:8 Comment(0)
N
578

It basically means that the object implements the __getitem__() method. In other words, it describes objects that are "containers", meaning they contain other objects. This includes strings, lists, tuples, and dictionaries.

Nelly answered 19/10, 2008 at 21:11 Comment(4)
How reliable would: hasattr(SomeClassWithoutGetItem, '__getitem__') to determine if a thing is subscriptable?Paintbox
The [...] indexing syntax is called a subscript, because it's equivalent to mathematical notation that uses actual subscripts; e.g. a[1] is Python for what mathematicians would write as a₁. So "subscriptable" means "able to be subscripted". Which, in Python terms, means it has to implement __getitem__(), since a[1] is just syntactic sugar for a.__getitem__(1).Lucielucien
That call to hasattr should work fine, but it's not the Pythonic way to do things; Python practice encourages Duck Typing. Meaning, if you plan on trying to fetch an item from your object using a subscript, go ahead and do it; if you think it might not work because the object is not subscriptable, wrap it in a try block with an except TypeError.Lucielucien
super seems to be an exception. The object returned by super can have the attribute __getitem__, but it is not subscriptable, thus the bracket slicing does not work.Psychometry
B
114

Off the top of my head, the following are the only built-ins that are subscriptable:

string:  "foobar"[3] == "b"
tuple:   (1,2,3,4)[3] == 4
list:    [1,2,3,4][3] == 4
dict:    {"a":1, "b":2, "c":3}["c"] == 3

But mipadi's answer is correct - any class that implements __getitem__ is subscriptable

Bergwall answered 19/10, 2008 at 22:39 Comment(0)
R
26

The meaning of subscript in computing is: "a symbol (notionally written as a subscript but in practice usually not) used in a program, alone or with others, to specify one of the elements of an array."

Now, in the simple example given by @user2194711 we can see that the appending element is not able to be a part of the list because of two reasons:-

  1. We are not really calling the method append; because it needs () to call it.

  2. The error is indicating that the function or method is not subscriptable; means they are not indexable like a list or sequence.

Now see this:-

>>> var = "myString"
>>> def foo(): return 0
... 
>>> var[3]
't'
>>> foo[3]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'function' object is not subscriptable

That means there are no subscripts or say elements in function like they occur in sequences; and we cannot access them like we typically do, with the help of [].

Also; as mipadi said in his answer; It basically means that the object implements the __getitem__() method. (if it is subscriptable). Thus the error produced:

arr.append["HI"]

TypeError: 'builtin_function_or_method' object is not subscriptable

Rosauraroscius answered 31/3, 2018 at 13:14 Comment(0)
C
21

if “scriptable”

A scriptable object is an object that records the operations done to it and it can store them as a "script" which can be replayed.

For example, see: Application Scripting Framework

if “subscriptable”

Now, if Alistair didn't know what he asked and really meant "subscriptable" objects (as edited by others), then (as mipadi also answered) this is the correct one:

A subscriptable object is any object that implements the __getitem__ special method (think lists, dictionaries).

Churchgoer answered 19/10, 2008 at 22:5 Comment(5)
Note that I'm replying to the original question about "scriptable" objects, not "subscriptable" as edited by others, not Alistair. I really would like Alistair to comment.Churchgoer
Ah, a new badge for my collection! :) Just kidding, obviously. The only thing that justified the editing of the question was that Alistair chose an answer; I still am not sure if Alistair was sure about choosing.Churchgoer
The main reference is no longer available, so this is not an answerAnschluss
@Anschluss Thanks for pointing that the URL became dead over the years. Note that the dead link did not make the whole answer less useful, just the first part of it.Churchgoer
Jep. Noticed it after sending...Anschluss
F
12

I had this same issue. I was doing

arr = []
arr.append["HI"]

So using [ was causing error. It should be arr.append("HI")

Farahfarand answered 23/11, 2016 at 13:2 Comment(0)
I
10

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.)

Imminent answered 17/7, 2019 at 6:56 Comment(1)
Of course, another solution is to have gimme_things raise an exception when it can't do what it was supposed to do. This is scary for beginners, but actually a good pattern to learn. It's better to fail loudly than to pass back bogus information or make the return value ambiguous (as might be the case if you return an empty list when you could not find any items, but also when something unrelated failed; it is often better then to raise an exception for the latter case).Imminent
S
0

Basically this error will appear in case you are modifying or adding any field after type casting for the mentioned object instead of doing it before.

Splenic answered 16/11, 2022 at 8:42 Comment(1)
To supplement, be aware of auto-type casting. For example, if you assume you're handling a string and getting the error, make sure you're not actually working with an int. For instance, when doing mydept[-3] and getting it because mydept was set = 990201 instead of str(990201) from an API call. <whistles innocently>Forerunner
A
0

This question is the nr. 1 result on search and it has the most views, so I'll post here.

My case

(404, b'{"url":"https://example.com"}')

Tried to get the first value, accessing obj[0] returned object is not subscriptable.

As another answer suggested, tried looping through it but got object is not iterable.

Solution

obj.args[0]

Troubleshooting

Call print(dir(obj)) and see if there's a method or attribute that might give you access to the value.

Advise answered 25/2 at 9:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.