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

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.
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

api hat geschrieben: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 ...

Code: Alles auswählen

class Visitor(object):

    def __init__(self):
        self.indent = 0

    def spaces(self):
        return ' ' * (4 * self.indent)

    def visit(self, visitable):
        getattr(self, u'visit_' + type(visitable).__name__)(visitable)

    def visit_list(self, alist):
        print self.spaces() + 'list:'
        self.indent += 1
        for each in alist:
            self.visit(each)
        self.indent -= 1

    def visit_dict(self, adict):
        print self.spaces() + 'dict:'
        self.indent += 1
        for key, value in adict.iteritems():
            self.visit_pair(key, value)
        self.indent -= 1

    def visit_pair(self, key, value):
        print self.spaces() + 'pair:', repr(key), ':', repr(value)

    def visit_int(self, anint):
        print self.spaces() + 'int:', repr(anint)

    def visit_str(self, astr):
        try:  # Obacht!
              # Das wird nicht immer funktionieren, und zudem ist es gefährlich, su.:
            item = eval(astr)
        except (NameError, SyntaxError):
            print self.spaces() + 'string:', repr(astr)
        except:
            raise
        else:
            self.visit(item)


a_list = ['auto', '17', '["eine liste", 42]', '{"TEST":40, "HAUS": 50}']
visitor = Visitor()
visitor.visit(a_list)
Ergebnis:

Code: Alles auswählen

list:
    string: 'auto'
    int: 17
    list:
        string: 'eine liste'
        int: 42
    dict:
        pair: 'TEST' : 40
        pair: 'HAUS' : 50
Für evtl. Schäden, die durch den Einsatz von eval() entstehen, lehne ich jede Verantwortung ab.

Das Problem bei der ganzen Angelegenheit ist, wie schon erwähnt wurde, dass dein Datenformat Strings enthält, die für sich selbst stehen, und solche, die für Programmcode stehen. Angenommen, du verwendest in deinem Programm eine globale Variable namens TEST, wie willst du dann entscheiden, ob mit dem String 'TEST' diese Variable gemeint ist, oder ob es sich um einen String handelt, der nur zufällig aus genau derselben Buchstabenfolge besteht wie der Variablenname? Oder, noch schlimmer, was wenn der String aus der Buchstabenfolge '__import__("subprocess").call(["rm", "-rf", "/"])' oä. besteht?
In specifications, Murphy's Law supersedes Ohm's.
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

pillmuncher: Was spricht dagegen, statt eval ast.literal_eval zu verwenden?
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

derdon hat geschrieben:pillmuncher: Was spricht dagegen, statt eval ast.literal_eval zu verwenden?
Du meinst so?

Code: Alles auswählen

    def visit_str(self, astr):
        try:
            item = ast.literal_eval(astr)
        except (NameError, SyntaxError, ValueError):
            print self.spaces() + 'string:', repr(astr)
        except:
            raise
        else:
            self.visit(item)
Ja, das wäre gescheiter. Daran hatte ich gar nicht gedacht. Und gerade sehe ich, dass der Vorschlag ja schon breitestens ausgewälzt wurde. :oops:
In specifications, Murphy's Law supersedes Ohm's.
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

Ja, so meinte ich das.
Antworten