Mehrdimensionales Array abfragen

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.
trumm
User
Beiträge: 14
Registriert: Freitag 4. September 2015, 08:06

Hallo zusammen,

ich beschäftige mich gerade ganz frisch mit Python, aber ich komme an einer stelle gerade nicht weiter.
Ich habe eine Array und möchte wissen ob ein bestimmter Wert in dem zweiten Tail de Liste vorkommt.

Die Liste sieht wie folgt aus.

Code: Alles auswählen

[[['1', 'GigabitEthernet1', '6'],
  ['2', 'GigabitEthernet2', '6'],
  ['3', 'GigabitEthernet3', '6'],
  ['4', 'Null0', '1'],
  ['5', 'Loopback10', '24'],
  ['6', 'Tunnel25', '131'],
  ['7', 'Tunnel5025', '131']],
 [['3'], ['7']]]
Ziel ist es erst auf die 131 zu filtern. In diesem Fall soll nur Index 6 und 7 überbleiben.
Anschliessen soll in der zweiten Liste, ich hoffe überhaupt das es eine ist, gepüft werden ob 6 oder 7 darin vorkommt.

Den ersten Teil habe ich wie folgt gelöst :

Code: Alles auswählen

   for line in info:
  	interface = line[1]
  	type = line[2]
  	if type == "131":
Ich bin mir nicht sicher ob das alles so klappt und hoffe Ihr könnt mir helfen.

Danke & Gruß
Sascha
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ja, das dürfte so funktionieren, wie du es beschrieben hast. Warum probierst du es nicht einfach selbst aus? Gibt es ein Problem bei der konkreten Umsetzung?
Benutzeravatar
/me
User
Beiträge: 3552
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Du möchtest Daten anhand eines bestimmten Kriteriums herausfiltern, so dass nur die übrigbleiben die die Bedingung erfüllen.

Nehmen wir mal an du hast folgende Definition.

Code: Alles auswählen

data = [[['1', 'GigabitEthernet1', '6'],
         ['2', 'GigabitEthernet2', '6'],
         ['3', 'GigabitEthernet3', '6'],
         ['4', 'Null0', '1'],
         ['5', 'Loopback10', '24'],
         ['6', 'Tunnel25', '131'],
         ['7', 'Tunnel5025', '131']],
        [['3'], ['7']]]
info = data[0]
Dann kannst du eine neue leere Liste definieren und alle Elemente dort anfügen die die Bedingung erfüllen. Das ist platt runtergeschrieben und funktioniert.

Code: Alles auswählen

filtered_info = []
for element in data[0]:
    if element[2] == '131':
        filtered_info.append(element)
print(filtered_info)

Du kannst aber natürlich auch die filter Funktion einsetzen. Diese erwartet als ersten Parameter eine Funktion die ein Element übergeben bekommt und als zweites ein Iterable (wie z.B. eine Liste) auf das der Filter angewendet wird. Das Iterable haben wir, das sind deine Ausgangsdaten.

Die Filterfunktion würde dann wie folgt aussehen.

Code: Alles auswählen

# Filterfunktion
def is_correct_type(element):
    if element[2] == '131':
        return True
    else:
        return False

# alternative Filterfunktion (sinnvoll gekürzt)
def is_correct_type(element):
    return element[2] == '131'
Der Aufruf ist jetzt einfach.

Code: Alles auswählen

filtered_info = list(filter(is_correct_type, info))
Mit einem lambda-Ausdruck kannst du das sogar in einer Zeile unterbringen.

Code: Alles auswählen

filtered_info = list(filter(lambda element: element[2] == '131', info))
garreth
User
Beiträge: 41
Registriert: Donnerstag 23. Oktober 2014, 12:04

Ich wollte nur noch kurz einwerfen, dass "type" ein schlechter Bezeichner ist. Man überschreibt so die eingebaute Funktion "type()".
Benutzeravatar
Kebap
User
Beiträge: 686
Registriert: Dienstag 15. November 2011, 14:20
Wohnort: Dortmund

Hey Sascha, willkommen bei Python und hier im Forum!
Ich bin mir nicht sicher ob das alles so klappt und hoffe Ihr könnt mir helfen.
Da empfiehlt sich die interaktive Python Konsole. Du kannst dort einfach paar Befehle eingeben, und sie werden direkt ausgeführt. So kannst du etwas herumprobieren, ob die Logik wohl funktioniert, die du dir gerade ausdenkst. Wenn ja, kannst du sie in deinen Programmcode aufnehmen und dich dem nächsten Teilproblem zuwenden. :mrgreen:
MorgenGrauen: 1 Welt, 8 Rassen, 13 Gilden, >250 Abenteuer, >5000 Waffen & Rüstungen,
>7000 NPC, >16000 Räume, >200 freiwillige Programmierer, nur Text, viel Spaß, seit 1992.
trumm
User
Beiträge: 14
Registriert: Freitag 4. September 2015, 08:06

Herzlichen Dank euch allen.
Erstmal, ich werde den Einwand für Type beherzigen und den Ausdruck ändern.
Mit der interaktiven Shell das hab ich verstenden nur auf die schnelle keine gefunden. Ich hab mir jetzt so eine Python für Windows installiert, aber damit komme ich gerade nicht so zurecht, der will nicht mal ein print ausführen. Egal.

Meinen besonderen Dank gilt /me :)
Dieses ausführliche Anleitung ist ganz toll, aber ich verstehe es leider noch nicht.
Ich hab verstanden, dass ich eine neue Liste generiere.
Aber das ausfiltern will nicht in meinen Kopf.

Ich brauche erstmal die Interfaces vom Type 131 und deren Index ['6', 'Tunnel25', '131'],
['7', 'Tunnel5025', '131'] , aber eben auch den zweiten Teil der Liste in diesem Fall hinten die [3, 7].
Dann muss ich vergleichen ob 6 und 7 in Liste zwei [3, 7] vorkommt. Wenn dieser fehlt dann Error.

Wie kann ich Liste eins durchlaufen und prüfen ob in zwei der Wert auch vorhanden ist.

Gruß
Sascha
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Vielleicht solltest Du das dann in eine sinnvollere Datenstruktur überführen! Woher kommt denn die initiale Liste?

Für den "zweiten Teil" böte sich ein ``set`` an; damit kann man einfach auf das Vorhandensein eines Elements prüfen.
Für den ersten ggf. eine Liste oder gar ein Dictionary, wenn die Ids zu Beginn nicht fortlaufend sind.

Allerdings hast Du keine "zweite" Liste, denn Du hast darin lauter *einelementige* Listen. Ist das so gewollt?
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
trumm
User
Beiträge: 14
Registriert: Freitag 4. September 2015, 08:06

Die Liste kommt aus einer snmp Abfrage eines Cisco Switches.
Um die Infos zusammen zu bekommen, muss ich unterschiedliche OIDs abfragen. Darauf ergibt sich das Konstrukt, auch wenn ich jetzt noch nicht weiß wie die richtigen Fachbegriffe dafür sind. An dem Output kann ich wenig ändern, bzw fehlt mir bsiher das know how, ich bin schon froh, dass ich das überhaupt habe :)

Mein Abfrage auf True / False klingt ungefähr so:
Suche per SNMP alle Interfaces raus. Da kommt der erste Teil her [ Interface_Index, Interface_Name, Type ]
Dann sage ich, mich Interessieren nur die mit dem Typ 131 und dann davon der Interface_Index und Interface_Name.
Als zweites muss ich wissen on der Interface_Index in dem zweiten Teil vorhanden ist. wenn Nein dann False

Ich hab da noch nen Knoten im Kopf :)

Aktuell sieht es so aus, das ich eine weitere Liste aufgemacht habe. Die Idee hab ich aus dem Post von /me

Code: Alles auswählen

info = [[['1', 'GigabitEthernet1', '6'],
         ['2', 'GigabitEthernet2', '6'],
         ['3', 'GigabitEthernet3', '6'],
         ['4', 'Null0', '1'],
         ['5', 'Loopback10', '24'],
         ['6', 'Tunnel25', '131'],
         ['7', 'Tunnel5025', '131']],
        [['3'], ['7']]]

inter = info[0]
alive = info[1]
		
for line in inter:
	int_index = line[0]
	interface = line[1]
	cat = line[2]
	if cat == "131":
		print(int_index, interface)

print(alive)
		
Benutzeravatar
Kebap
User
Beiträge: 686
Registriert: Dienstag 15. November 2011, 14:20
Wohnort: Dortmund

trumm hat geschrieben: Mit der interaktiven Shell das hab ich verstenden nur auf die schnelle keine gefunden. Ich hab mir jetzt so eine Python für Windows installiert, aber damit komme ich gerade nicht so zurecht, der will nicht mal ein print ausführen.
Die Shell ist bei jeder Python Version automatisch dabei. Öffne mal deine Windows Shell und tippe "python"

Print verhält sich etwas anders in Python 2 und Python 3. Vielleicht hast du eine Version installiert, die nicht zu deiner Anleitung passt. Lässt sich aber leicht beheben.

Code: Alles auswählen

print "hello" # py 2
print("world") # py 3
MorgenGrauen: 1 Welt, 8 Rassen, 13 Gilden, >250 Abenteuer, >5000 Waffen & Rüstungen,
>7000 NPC, >16000 Räume, >200 freiwillige Programmierer, nur Text, viel Spaß, seit 1992.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

trumm hat geschrieben:Die Liste kommt aus einer snmp Abfrage eines Cisco Switches.
Ist das JSON? Oder kommt da tatsächlich eine Python-Datenstruktur "zurück"?

(Letztlich) Unabhängig davon woher das kommt, kann man es dennoch so umbauen, dass man intern damit komfortabler zum Ziel kommt. Und imho sollte man das auch.

Ohne die Grundlagen von Python zu lernen - und dazu gehören die grundlegenden Datenstrukturen wie Listen, Dictionaries und Sets - wirst Du letztlich nicht alleine zum Ziel kommen fürchte ich.

Hier mal der Ansatz (die In[1] und Out[1]-Präfixe ignorieren, die stammen aus der Python-Shell iPython):

Code: Alles auswählen

In [3]: info[0]
Out[3]:
[['1', 'GigabitEthernet1', '6'],
 ['2', 'GigabitEthernet2', '6'],
 ['3', 'GigabitEthernet3', '6'],
 ['4', 'Null0', '1'],
 ['5', 'Loopback10', '24'],
 ['6', 'Tunnel25', '131'],
 ['7', 'Tunnel5025', '131']]

In [4]: info[1]
Out[4]: [['3'], ['7']]

In [5]: from itertools import chain

In [6]: ids = set(chain.from_iterable(info[1]))

In [7]: ids
Out[7]: {'3', '7'}

In [8]: info[0][0][0]
Out[8]: '1'

In [9]: info[0][0][0] in ids
Out[9]: False

In [10]: for element in info[0]:
   ...:     print("{} {}".format(element[0], element[0] in ids))
   ...:
1 False
2 False
3 True
4 False
5 False
6 False
7 True
Grundlegende Idee ist es, deine IDs (?) des zweiten Listenteils in ein Set zu bekommen [6] und dann mittels ``in`` Operator zu prüfen, ob eine Id aus der ersten Liste in der zweiten enthalten ist [9].
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
trumm
User
Beiträge: 14
Registriert: Freitag 4. September 2015, 08:06

Hallo Hyperion,

ich hab ja vor Python zu lernen, meine Lektüre liegt an der arbeit auch schon auf dem Tisch, doch beschäftige ich mich eben erst seit zwei Tagen damit :)
Ich bin aktuell soweit, das ich die DAten aus Info gesplittet habe einfach in zwei neue

Code: Alles auswählen

inter = info[0]
alive = info[1]
Jetzt bäuchte ich nur noch eine/zwei for Schleifen die prüfen ist es Type/Cat inter[2] = 131 ist
Wenn JA
Prüfe ob ob Interface_Index inter[0] in alive irgendwo enthalten ist
Wenn Ja = Ok
Else Nein = Kaputt

So grob stelle ich mir das vor.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Den ersten Teil hat Dir doch schon /me gezeigt und den zweiten ich ;-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
trumm
User
Beiträge: 14
Registriert: Freitag 4. September 2015, 08:06

Geht nicht sowas wie

Code: Alles auswählen

for int_index, interface, cat in info[0]:
   if cat == '131' in info[0]:
      if int_index in info[1]:
         print(Ok)
      else:
         print(Kaputt)
   print(Keine Ahnung)
Ich verstehe das imemr noch nicht :(
Benutzeravatar
/me
User
Beiträge: 3552
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

info[1] sieht wie folgt aus

Code: Alles auswählen

[['3'], ['7']]
Du hast eine Liste mit zwei Elementen. Das erste Element ist eine Liste mit dem Element '3', das zweite eine Liste mit dem Element '7'. Das heißt nichts anderes, als dass '3' nicht in info[1] enthalten ist (['3'] hingegen ist enthalten). int_index in info[1] kann also niemals wahr sein wenn int_index ein String ist.

Das einfachste ist es folglich eine simple Datenstruktur zu erstellen die alle Werte der inneren Listen enthält und schnell getestet werden kann. Dafür hat Hyperion ein Beispiel geliefert.

Code: Alles auswählen

ids = set(chain.from_iterable(info[1]))
Jetzt musst du nur noch das kaputte if cat == '131' in info[0] reparieren.
trumm
User
Beiträge: 14
Registriert: Freitag 4. September 2015, 08:06

DANKE! Euch allen, deswegen kann ich das nicht vergleiche. Jetzt fällt langsam Licht ins dunkle Zimmer :)

Aktuell habe ich das jetzt so abgeschrieben

Code: Alles auswählen

def check_mist(item, _no_params, info):
   from itertools import chain
   ids = set(chain.from_iterable(info[1]))
   for int_index, interface, cat in info[0]:
      if cat == "131":
         if int_index in ids:
            return (0, "OK")
         else:
            return (2, "Not found")
Was ich am cat reparieren soll erschliesst mich mir noch nicht, genauso wenig warum immer noch ein "Not Found" ergibt.
Muss ich die cat auch noch irgendiwe umwandeln in etwas für "lesbares" ? Vielleicht sogar genauso ?

Ich wünsche mir heute noch ein Erfolgserlebnis zum Wochenende. Ist crazy, ich bastele den ganzen Tag und ich vermute viele von euch hätten das in 5 Minuten fertig :)

Danke nochmals
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Lasse Dir die Inhalte der Namen in der Schleife einfach mal ausgeben. Dann siehst Du, was *wirklich* darin steht. Damit kann man dann oftmals schlussfolgern, wieso es noch ein Problem gibt.

Du hast übrigens meine Fragen nach der Herkunft der Daten noch nicht beantwortet ;-) (Also wie Du dieses Datenformat bekommst!)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
trumm
User
Beiträge: 14
Registriert: Freitag 4. September 2015, 08:06

Entschulige, das war nicht meine Absicht :)
Also die Daten kommen von einem Cisco - Switch die per SNMP abgefragt werden, die Dinger sind echt einfachter zu Konfigurieren, als mit Python Wert, Zahlen Strings zu vergleichen :)

Ich komme jetzt zumindest an den Punkt, das er die 131 frisst, aber in der unteren Schleife komme ich nicht zurecht.
Er findet die info[0] und ids Werte nicht :(
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

trumm hat geschrieben: Also die Daten kommen von einem Cisco - Switch die per SNMP abgefragt werden, die Dinger sind echt einfachter zu Konfigurieren, als mit Python Wert, Zahlen Strings zu vergleichen :)
Das hattest Du schon gesagt. Woher kommt aber *konkret* diese Python-Datenstruktur? Ganz platt formuliert: Wie kommt der Inhalt von ``info`` in Deinen Quellcode?
trumm hat geschrieben: Ich komme jetzt zumindest an den Punkt, das er die 131 frisst, aber in der unteren Schleife komme ich nicht zurecht.
Er findet die info[0] und ids Werte nicht :(
Was ist die untere Schleife? Ich sehe da nur *eine* Schleife‽
Gehe Schritt für Schritt durch. Was hat welcher Name in welchem Durchlauf für einen Wert. Lass Dir alles mittels ``print`` ausgeben. Dann überlege, welche Bedingung nicht eingetreten ist und wieso das der Fall sein muss.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
trumm
User
Beiträge: 14
Registriert: Freitag 4. September 2015, 08:06

Naja das ganze Teil ist für Nagios, Check-MK und so sieht es aus :

Code: Alles auswählen

#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-

def inventory_mist(info):
   #print(info)
   #import pprint ; pprint.pprint(info)
   for int_index, interface, cat in info[0]:
      if cat == '131':
         yield interface, None

def check_mist(item, _no_params, info):
   from itertools import chain
   inter = info[0]
   alive = info[1]
   print(inter)
   ids = set(chain.from_iterable(alive))
   for element in inter:
      print(element)
      if element[2] == '131':
         if element[0] in ids:
            return (0, "OK")
         else:
            return (2, "Not found")
   return (3, "UNKNOWN - not yet implemented")

mist_info = [
                       ( ".1.3.6.1.2.1.2.2.1", ["1", "2", "3"]
                       ),
                       ( ".1.3.6.1.4.1.9.9.449.1.4.1.1", ["4"]
                       )
]

check_info["mist"] = {
    "check_function"        : check_mist,
    "inventory_function"    : inventory_mist,
    "service_description"   : "Status of %s",
    "snmp_info"             : mist_info
Ganz interessant finde ich gerade, das meine Schleife einen Teil "Verschluckt" Deswegen wird hier nichts grün.
Das habe ich auf der Konsole.
[['1', 'GigabitEthernet1', '6'], ['2', 'GigabitEthernet2', '6'], ['3', 'GigabitEthernet3', '6'], ['4', 'Null0', '1'], ['5', 'Loopback10', '24'], ['6', 'Tunnel25', '131'], ['7', 'Tunnel5025', '131']]
['1', 'GigabitEthernet1', '6']
['2', 'GigabitEthernet2', '6']
['3', 'GigabitEthernet3', '6']
['4', 'Null0', '1']
['5', 'Loopback10', '24']
['6', 'Tunnel25', '131']
Status of Tunnel25 CRIT - Not found
[['1', 'GigabitEthernet1', '6'], ['2', 'GigabitEthernet2', '6'], ['3', 'GigabitEthernet3', '6'], ['4', 'Null0', '1'], ['5', 'Loopback10', '24'], ['6', 'Tunnel25', '131'], ['7', 'Tunnel5025', '131']]
['1', 'GigabitEthernet1', '6']
['2', 'GigabitEthernet2', '6']
['3', 'GigabitEthernet3', '6']
['4', 'Null0', '1']
['5', 'Loopback10', '24']
['6', 'Tunnel25', '131']
Status of Tunnel5025 CRIT - Not found
Wie man ganz oben sieht, gibt es in der SNMP Abfrage der info einen index der mit 7 beginnt.
Später aber fehlt dieser dann auf einmal. Das macht mich ja jetzt ganz verrückt. Dummerweise ist die 7 normalerweise alive, die 6 down und die 3 wird aufgrund des Filters Ignoriert :(
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Irgend wie sehe ich immer noch nicht, *wie* ``info`` jetzt gefüllt wird :-(
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Antworten