Was bedeutet es, wenn ein Python-Objekt "subskriptierbar" ist oder nicht?

372

Welche Arten von Objekten fallen in den Bereich "subscriptable"?

Alistair
quelle

Antworten:

357

Dies bedeutet im Grunde, dass das Objekt die __getitem__()Methode implementiert . Mit anderen Worten, es beschreibt Objekte, die "Container" sind, dh sie enthalten andere Objekte. Dies umfasst Zeichenfolgen, Listen, Tupel und Wörterbücher.

Mipadi
quelle
2
Wie zuverlässig wäre es: hasattr(SomeClassWithoutGetItem, '__getitem__')festzustellen, ob eine Sache abonnierbar ist?
jmunsch
2
Die [... ]Indexierungssyntax wird als Index bezeichnet , da sie der mathematischen Notation entspricht, bei der tatsächliche Indizes verwendet werden. zB a[1]ist Python für das, was Mathematiker als a₁ schreiben würden . "Subskriptierbar" bedeutet also "subskriptierbar". Was in Python-Begriffen bedeutet, dass es implementiert werden muss __getitem__(), da a[1]es sich nur um syntaktischen Zucker handelt a.__getitem__(1).
Mark Reed
Dieser Aufruf hasattrsollte gut funktionieren, aber es ist nicht die pythonische Art, Dinge zu tun. Die Python-Praxis fördert das Entenschreiben . Das heißt, wenn Sie versuchen, ein Element mithilfe eines Index von Ihrem Objekt abzurufen, fahren Sie fort. Wenn Sie der Meinung sind, dass dies möglicherweise nicht funktioniert, weil das Objekt nicht tiefgestellt werden kann, schließen Sie es in einen tryBlock mit einem ein except TypeError.
Mark Reed
77

Auf den ersten Blick sind die folgenden integrierten Funktionen nur abonnierbar:

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

Aber Mipadis Antwort ist richtig - jede Klasse, die implementiert, __getitem__ist tiefstellbar

Dan
quelle
17

Ein skriptfähiges Objekt ist ein Objekt, das die mit ihm durchgeführten Vorgänge aufzeichnet und sie als "Skript" speichern kann, das wiedergegeben werden kann.

Beispiel: Application Scripting Framework

Wenn Alistair nicht wusste, was er fragte und wirklich "abonnierbare" Objekte meinte (wie von anderen bearbeitet), dann ist dies (wie auch Mipadi antwortete) das richtige:

Ein tiefstellbares Objekt ist jedes Objekt, das die __getitem__spezielle Methode implementiert (Denklisten, Wörterbücher).

tzot
quelle
2
Beachten Sie, dass ich auf die ursprüngliche Frage zu "skriptfähigen" Objekten antworte, nicht zu "subskriptierbar", wie von anderen bearbeitet, nicht zu Alistair. Ich möchte wirklich, dass Alistair einen Kommentar abgibt.
Zot
Ah, ein neues Abzeichen für meine Sammlung! :) Nur ein Scherz, offensichtlich. Das einzige, was die Bearbeitung der Frage rechtfertigte, war, dass Alistair eine Antwort wählte; Ich bin mir immer noch nicht sicher, ob Alistair sich bei der Auswahl sicher war.
Zot
17

Die Bedeutung des Index beim Rechnen ist: "Ein Symbol (fiktiv als Index geschrieben, in der Praxis jedoch normalerweise nicht), das in einem Programm allein oder mit anderen verwendet wird, um eines der Elemente eines Arrays anzugeben."

Nun zu dem einfachen Beispiel von @ user2194711 wir nun sehen, dass das anhängende Element aus zwei Gründen nicht Teil der Liste sein kann:

1) Wir rufen die Methode append nicht wirklich auf; weil es braucht() es nennen muss.

2) Der Fehler zeigt an, dass die Funktion oder Methode nicht tiefgestellt werden kann. bedeutet, dass sie nicht wie eine Liste oder Sequenz indizierbar sind.

Nun sehen Sie dies: -

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

Das heißt, es gibt keine Indizes oder sagen Elemente, functionwie sie in Sequenzen vorkommen. und wir können nicht wie wir mit Hilfe von auf sie zugreifen[] .

Ebenfalls; wie Mipadi in seiner Antwort sagte; Dies bedeutet im Grunde, dass das Objekt die __getitem__()Methode implementiert . (wenn es abonnierbar ist). So entstand der Fehler:

arr.append["HI"]

TypeError: Das Objekt 'builtin_function_or_method' kann nicht tiefgestellt werden

Vicrobot
quelle
7

Ich hatte das gleiche Problem. ich habe getan

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

Die Verwendung [verursachte also einen Fehler. Es sollte seinarr.append("HI")

user2194711
quelle
3

Als Folge der früheren Antworten hier ist dies sehr oft ein Zeichen dafür, dass Sie glauben, eine Liste (oder ein Diktat oder ein anderes abonnierbares Objekt) zu haben, wenn Sie dies nicht tun.

Zum Beispiel, sagen wir , Sie haben eine Funktion , die sollte eine Liste zurückkehren;

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

Was passiert nun, wenn Sie diese Funktion aufrufen und something_happens()aus irgendeinem Grund keinen TrueWert zurückgeben? Das ifscheitert, und so fallen Sie durch; gimme_thingsmacht nicht explizit returnetwas - also wird es tatsächlich implizit return None. Dann dieser Code:

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

wird mit " NoneTypeObjekt ist nicht abonnierbar" fehlschlagen, weil, na ja , thingsist Noneund Sie versuchen, dies zu tun, None[0]was keinen Sinn ergibt, weil ... was die Fehlermeldung sagt.

Es gibt zwei Möglichkeiten, diesen Fehler in Ihrem Code zu beheben: Die erste besteht darin, den Fehler zu vermeiden, indem Sie überprüfen, ob er thingstatsächlich gültig ist, bevor Sie versuchen, ihn zu verwenden.

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

oder äquivalent die TypeErrorAusnahme abfangen;

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

Eine andere Möglichkeit besteht darin, das Design gimme_thingsso zu gestalten , dass sichergestellt ist, dass immer eine Liste zurückgegeben wird. In diesem Fall ist dies wahrscheinlich das einfachere Design, da es bedeutet, dass es viele Orte gibt, an denen Sie einen ähnlichen Fehler haben, diese einfach und idiomatisch gehalten werden können.

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 []

Was Sie in die else:Branche einfügen, hängt natürlich von Ihrem Anwendungsfall ab. Vielleicht sollten Sie eine Ausnahme something_happens()auslösen, wenn dies fehlschlägt, um klarer und deutlicher zu machen, wo tatsächlich etwas schief gelaufen ist? Das Hinzufügen von Ausnahmen zu Ihrem eigenen Code ist eine wichtige Möglichkeit, um genau zu erfahren, was passiert, wenn etwas fehlschlägt!

(Beachten Sie auch , wie diese letztere fix immer noch nicht vollständig um den Fehler zu beheben - es verhindert , dass Sie zu Index versucht , Noneaber things[0]eine immer noch , IndexErrorwenn thingseine leere Liste ist , wenn Sie eine haben. trySie tun können , except (TypeError, IndexError)zu stoppen es auch.)

Tripleee
quelle