Typ einer Variablen bestimmen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
api
User
Beiträge: 181
Registriert: Donnerstag 7. August 2008, 21:23

Hallo zusammen,

ich habe zum Beispiel die folgende Liste mit Variablen-Inhalten:
a_list = ['auto', '17', '['eine liste', 2]', '{TEST: 40, HAUS: 50}']
Nun möchte ich herausfinden, dass
  • "a_list[0]" vom Typ String
    "a_list[1]" vom Typ Integer
    "a_list[2]" vom Typ List
    "a_list[3]" vom Typ Dict
ist. Wie stelle ich das an? Mit "type" bekomme ich ja immer nur raus, dass es ein String ist - aber was ist mit dem Inhalt des Strings?

Ich hab da im Augenblick keinen Ansatz für - glaube aber, dass es so schwierig doch nicht sein kann?
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Der Inhalt eines Strings sind Strings und das ist auch ihr Typ. Wenn sie Literale enthalten, sind das keine Literale, denn es sind ja Strings.

Wenn du denn String aber auswertest, zb mit `ast.literal_eval` kannst du den Typ _davon_ bestimmen.
api
User
Beiträge: 181
Registriert: Donnerstag 7. August 2008, 21:23

Das ist genau das, was ich haben wollte :!: :D

Was ich noch nicht ganz verstehe, warum ich bei:

Code: Alles auswählen

type(ast.literal_eval('auto'))
eine Fehlermeldung erhalte?

Code: Alles auswählen

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.6/ast.py", line 68, in literal_eval
    return _convert(node_or_string)
  File "/usr/lib/python2.6/ast.py", line 67, in _convert
    raise ValueError('malformed string')
ValueError: malformed string
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Weil du wahrscheinlich

Code: Alles auswählen

type(ast.literal_eval('"auto"'))
meinst ;-)
Das Leben ist wie ein Tennisball.
api
User
Beiträge: 181
Registriert: Donnerstag 7. August 2008, 21:23

@EyDu: Ok. Aber wie bekomme ich das mit meiner Liste hin, da steht als "a_list[0]" ja nur 'auto' drin - und nicht '"auto"' :?:
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

api hat geschrieben:@EyDu: Ok. Aber wie bekomme ich das mit meiner Liste hin, da steht als "a_list[0]" ja nur 'auto' drin - und nicht '"auto"' :?:

Code: Alles auswählen

>>> print(a_list[0])
auto
>>> print(repr(a_list[0]))
'auto'
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Code: Alles auswählen

>>> type(ast.literal_eval('["test", 42]')[0])
<type 'str'>
Ehrlich gesagt sehe ich dein Problem nicht. Vielleicht solltest du ein konkretes Beispiel mit einer Liste geben, welches nicht funktioniert.
Das Leben ist wie ein Tennisball.
api
User
Beiträge: 181
Registriert: Donnerstag 7. August 2008, 21:23

@/me: Danke soweit. Das Problem ist nur - um mal wieder auf mein ursprüngliches Problem zurückzukommen -, dass ich zum Zeitpunkt des Umwandelns ja gar nicht weiss, dass es sich um einen String handelt. Das bekomme ich ja erst mit "ast.literal_eval()" heraus.

Somit funktioniert zwar..

Code: Alles auswählen

type(ast.literal_eval(repr(a_list[0])))
<type 'str'>
aber

Code: Alles auswählen

type(ast.literal_eval(repr(a_list[1])))
<type 'str'>
liefert halt auch 'str', obwohl es 'int' ist.
api
User
Beiträge: 181
Registriert: Donnerstag 7. August 2008, 21:23

@EyDu: Bei deinem Beispiel bin ich mit dir einer Meinung - das geht.

Aber nehmen wir mal die Liste:

Code: Alles auswählen

a_list = ['auto', 17]
und fragen dann ab:

Code: Alles auswählen

type(ast.literal_eval(a_list[0]))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.6/ast.py", line 68, in literal_eval
    return _convert(node_or_string)
  File "/usr/lib/python2.6/ast.py", line 67, in _convert
    raise ValueError('malformed string')
ValueError: malformed string
Wo ist denn da mein Fehler?
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Dein Fehler ist, dass du inhomogene Daten hast: Strings, die andere Daten enthalten, und Strings die fuer sich selbst stehen sollen. Das kann nicht funktionieren (oder bestenfalls nur mit Heuristiken).

Wie andere schon schon gesagt haben, damit ein String fuer ast.literal_eval wie ein String aussieht muss es ein String-Literal in dem String geben.

Du koenntest jetzt den ValueError abfangen und sagen, wenn das fehlschlug, muss das wohl ein richtiger String gewesen sein, allerdings kannst du dann keine Strings von fehlerhaften Literalen unterscheiden.

Und noch ein Problem: Was ist mit einem String-Literal wie '12', soll das immer eine Zahl werden oder auch nicht immer?

Die beste Loesung™ ist es homogene Daten zu nutzen, d.h. alles ist ein String und echte String-Literale sehen so aus: '"string"'.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Na einfach mit

Code: Alles auswählen

type(a_list[0])
a_list liegt doch bereits als Python-Objekt vor. Wenn das noch immer nicht die Lösung sein sollte, dann weise ich dich noch einmal darauf hin, dein Problem genauer zu beschreiben und ein zusammenhängendes Stück Code zu schreiben.
Das Leben ist wie ein Tennisball.
api
User
Beiträge: 181
Registriert: Donnerstag 7. August 2008, 21:23

@EyDu: Ich kann leider keinen Code posten, da ich ja noch gar keinen habe. :(

Mein Problem ist, dass ich aus einer Schnittstelle eine Liste geliefert bekomme, die zB wie schon beschrieben, so aussehen könnte:
(wohl etwas mehr Felder, aber dieser Schnipsel reicht ja bereits für die Beschreibung des Problems)

Code: Alles auswählen

a_list = ['auto', 17]
Nun muss ich in Abhängigkeit des Typs des Inhaltes des Strings verschiedene Dinge tun. Also so in der Form...

wenn vom Typ 'String' dann tue ...
wenn vom Typ 'Int' dann tue ...

Wenn ich den Typ erstmal habe, ist der Rest kein Problem mehr... Mit dem 'ast.literal_eval' geht das ja auch schon ganz gut und reicht mir eigentlich auch - nur wenn es halt nen String ist (aus meiner Sicht), dann gibts nen Problem.

Somit bin ich nun zu folgender Lösung gekommen:

Code: Alles auswählen

import ast

a_dict = {}
a_list = ['auto', '17', '["eine liste", 42]', '{"TEST":40, "HAUS": 50}']

for item in a_list:
  try:
    a_dict[item] = type(ast.literal_eval(item))

  except:
    a_dict[item] = type(ast.literal_eval(repr(item)))

print a_dict
Und das kommt dann heraus:

Code: Alles auswählen

{'["eine liste", 42]': <type 'list'>, 'auto': <type 'str'>, '17': <type 'int'>, '{"TEST":40, "HAUS": 50}': <type 'dict'>}
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Mal blöd nachgefragt: Es handelt sich dabei nicht um JSON?

Ansonsten kann man doch mit `isinstance` arbeiten...
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
api
User
Beiträge: 181
Registriert: Donnerstag 7. August 2008, 21:23

@Hyperion: Jetzt, wo du es so sagst... ja, mein Problem könnte ich auch mit 'isinstance()' erledigen... :D

Aber mit JSON hat es nichts zu tun...
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

api hat geschrieben: Aber mit JSON hat es nichts zu tun...
Seltsame Schnittstelle... die entspricht also keinem Standard? Darf man fragen, wo die herstammt? Oder ist das Format ggf. änderbar?
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
api
User
Beiträge: 181
Registriert: Donnerstag 7. August 2008, 21:23

Mit Schnittstelle meine ich, dass hier 1 hausinternes Programm Datensätze übergibt, die nunmal in diesem eigenen Format aufgebaut sind. Änderbar ist das nicht, muss als gegeben hingenommen werden. Es richtet sich aber nach keinem Standard...
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Vielleicht aber trotzdem JSON probieren. Die nicht-kompatiblen Strings lässt man halt so stehen, wie sie sind:

Code: Alles auswählen

import json

def strings_to_objects(strings):
    for s in strings:
        try:
            result = json.loads(s)
        except ValueError:
            result = s
        yield result
Zumindest für dein Beispiel klappt das ganz gut:

Code: Alles auswählen

In [43]: list(strings_to_objects(a_list))
Out[43]: ['auto', 17, [u'eine liste', 42], {u'HAUS': 50, u'TEST': 40}]
Leider bringt `json.loads()` keinen `error`-Paramter mit, wo man eine Standardbehandlung für ungültige Typen definieren könnte. Dann wäre das Ganze nämlich mit einer simplen List Comprehension implementierbar.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Hm... das ist natürlich doof und man kann da wenig machen :-( Man fragt sich nur, was den ursprünglichen Entwickler getrieben hat, so ein eigenes Format zu generieren...

Edit: Grad noch gesehen. snafus Idee ist vielleicht gar nicht so verkehrt!
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Wobei schon interesant wäre zu wissen, ob das Format Python-artig (`None`, `True`, ...) oder JSON-artig (`null`, `true`) ist. Generell finde ich jetzt nicht, dass soviel gegen die Anwendung von besagtem `literal_eval()` spricht, sofern der Input Python-Code ist bzw falls die uneinheitlichen Typbezeichnungen garnicht vorkommen.

Einen wirklich schönen Weg kann man bei einem nicht-standardisierten Format eh nicht gehen...
api
User
Beiträge: 181
Registriert: Donnerstag 7. August 2008, 21:23

@snafu: Also "Null" oder "None" kommen nicht vor, dafür könnte es aber "True" geben - was dann wieder die Lösung mit dem `literal_eval()` favorisiert.
Antworten