Hallo zusammen, ich bastel aktuell an einem Python Package um diverse Messgeräte zu steuern. Solange das ganze nur für mich war, habe ich einfach so etwas wie die IP Adresse der Ethernet zu GPIB Bridge als default Parameter im Konstruktor gesetzte. Aber nun benutzen auch andere das Package in leicht anderen Setups. Daher die Frage: Wie speichert man am elegantesten Konfigurationsdaten, so dass das Package sie von überall aus finden kann. Sehr oft teste ich nur mal eben schnell etwas in der Konsole und da würde ich es gerne vermeiden jedes Mal IP Adressen usw. eintippen zu müssen. Ich hätte es gerne z.B. so: import package instrument = package.HP34401A(gpib=4) instrument.get_reading() Die IP Adresse des Ethernetadapters soll irgendwo gespeichert sein, wo das package ran kommt. Ändern würde ich sie dann gerne über Methoden des Packages z.B. package.set_bridge_ip("192.168.0.7") Gibt es hierfür einen konventionellen Weg? Sicher gibt es unter Windows und Linux irgendwelche Ordner die immer da sind und in die man eine config Datei legen könnte, aber vielleicht weiß ja jemand, wie man es "richtig" macht. Vielen Dank und viele Grüße Philipp
Phil schrieb: > Gibt es hierfür einen konventionellen Weg? Sicher gibt es unter Windows > und Linux irgendwelche Ordner die immer da sind und in die man eine > config Datei legen könnte, aber vielleicht weiß ja jemand, wie man es > "richtig" macht. Alle Wege führen nach Rom, von daher keine Ahnung, ob dieser Weg der richtige ist. Aber ich würde der Klasse HP34401A eine classmethode spendieren, welche die Klasse aus einer Konfiguration initialisiert. OB die Konfiguration dann in toml, yaml, json oder was anders ist spielt keine Rolle. Dann kann dein Programm die üblichen verdächtigen (Ordner) abklappern und wenn dort eine Konfiguration liegt wird die Klasse damit erzeugt. ungefähr so:
1 | class HP34401A: |
2 | |
3 | def __init__(self, ip='127.0.0.1', gpib=4): |
4 | self.ip = io |
5 | self.gpib= gpib |
6 | |
7 | [...] |
8 | |
9 | @classmethod |
10 | def from_json(cls, filename): |
11 | with open(filename) as json_file: |
12 | data = json.load(json_file) |
13 | return cls(data['ip'], data['gpid']) |
14 | |
15 | [...] |
16 | |
17 | instrument = package.HP34401A.from_json('myconfig.json') |
Imonbln schrieb: > würde der Klasse HP34401A eine classmethode spendieren, welche die > Klasse aus einer Konfiguration initialisiert. OB die Konfiguration dann > in toml, yaml, json oder was anders ist spielt keine Rolle. > [...] > ungefähr so: > >
1 | > |
2 | > class HP34401A: |
3 | > [...] |
4 | > |
5 | > @classmethod |
6 | > def from_json(cls, filename): |
7 | > [...] |
8 | > |
9 | > instrument = package.HP34401A.from_json('myconfig.json') |
10 | > |
11 | > |
Das wäre ein Ansatz, würde allerdings erfordern, daß Du für jede mögliche Methode zur Konfiguration eine eigene Klassenmethode schreiben mußt, was -- je nachdem, was Du alles anbieten möchtest, einerseits einen recht hohen Aufwand mit viel Boilerplate bedeuten kann und andererseits provoziert, daß wohlmeinende Anwender "freundlich anregen", Du mögest doch bitte außerdem noch ihre tolle und völlig unverzichtbare Möglichkeit anbieten. ;-) Möglicherweise könnte es daher einfacher sein, wenn der Konstruktor einfach ein Dictionary (dict()) mit den Konfigurationsdaten übernimmt, dieses auf Fehler und Vollständigkeit überprüft und diese Daten benutzt. Dann spielt es keine Rolle mehr, woher die Daten kommen: aus einem csv.DictReader, einer .ini-Datei die mit configparser gelesen und umgewandelt wurde, aus einem DictCursor einer Datenbank, einer toml / yaml / json-Datei, oder womöglich aus einer der einfachsten aller Konfigurationsdateiformate in Python, nämlich: Python-Code. ;-) Lustigerweise bietet Python für solche Dinge eine weitere hübsche Möglichkeit, nämlich die **kwargs, also beliebige Keyword-Argumente übergeben zu können:
1 | #!/usr/bin/env python
|
2 | |
3 | class HP34401A: |
4 | def __init__(self, **kwargs): |
5 | self.ip = kwargs.pop('ip') |
6 | self.gpib = kwargs.pop('gpib') |
7 | self.klaus = kwargs.get('klaus', 'Dieter') # yay! |
8 | |
9 | def __repr__(self): |
10 | return '<{}(ip={!r}, gpib={}>'.format( |
11 | self.__class__.__name__, self.ip, self.gpib) |
12 | |
13 | |
14 | if __name__ == '__main__': |
15 | import sys |
16 | import json |
17 | |
18 | try: |
19 | print( HP34401A(ip='192.168.0.1', gpib='4') ) |
20 | except Exception as e: |
21 | print(type(e).__name__, str(e), sep=':', file=sys.stderr) |
22 | |
23 | try: |
24 | print( HP34401A(ip='192.168.0.2', gpib='4', klaus='Hans') ) |
25 | except Exception as e: |
26 | print(type(e).__name__, str(e), sep=':', file=sys.stderr) |
27 | |
28 | try: |
29 | text = '{"ip": "192.168.0.3", "gpib": 6}' |
30 | js = json.loads(text) |
31 | print( HP34401A(**js) ) |
32 | except Exception as e: |
33 | print(type(e).__name__, str(e), sep=':', file=sys.stderr) |
34 | |
35 | try: |
36 | text = '{"ip": "192.168.0.4"}' |
37 | js = json.loads(text) |
38 | print( HP34401A(**js) ) # Syntax! |
39 | except Exception as e: |
40 | print(type(e).__name__, str(e), sep=':', file=sys.stderr) |
Der geneigte Leser möge beachten, wie ich im Initializer der Klasse für Parameter, die Methode "pop()" und für optionale Parameter die Methode "get()" benutze, siehe Kommentar "yay". Außerdem muß das Konfigurationsdictionary zum Aufruf der Klasse mit '**' übergeben werden, siehe Kommentar "Syntax!".
Sheeva P. schrieb: > Der geneigte Leser möge beachten, wie ich im Initializer der Klasse für > Parameter, die Methode "pop()" und für optionale Parameter die Methode Dein Vorschlag hat aber den Nachteil, dass er die Information welche Parameter die Klasse braucht hinter den Ominösen **kwargs, versteckt. Schön das eine Klasse dann ein Key-Error beim Erzeugen wirft, wenn Sie nicht alle Parameter bekommt, welche sie gerne hätte, durch den pop Einsatz. Aber sauberes Design (was du meisten mit Leidenschaft vertrittst) sieht in diesen Fall anders aus. Sicher es gibt fälle in welchen **kwargs eine gute Option ist (Datenbanken, Protokoll Header, etc) aber dieser ist keiner davon und der Nachteil das man nur durch Codelesen (oder Dokumentation, wenn Vorhanden) herausbekommt was der Konstruktor wirklich will widerspricht auch den Zen of Python in mindestens folgenden Punkten: * Beautiful is better than ugly. * Explicit is better than implicit. * Readability counts. Abgesehen davon kannst du die Klasse auch ganz klassisch so schrieben Deine Tricks gehen dennoch:
1 | class HP34401A: |
2 | def __init__(self, ip, gpib, klaus='Dieter'): |
3 | self.ip = ip |
4 | self.gpib = gpib |
5 | self.klaus = klaus |
6 | |
7 | def __repr__(self): |
8 | return '<{}(ip={!r}, gpib={}>'.format( |
9 | self.__class__.__name__, self.ip, self.gpib) |
10 | |
11 | |
12 | if __name__ == '__main__': |
13 | import json |
14 | import sys |
15 | |
16 | print( HP34401A(ip='192.168.0.1', gpib='4') ) |
17 | print( HP34401A(ip='192.168.0.2', gpib='4', klaus='Hans')) |
18 | |
19 | try: |
20 | text = '{"ip": "192.168.0.4"}' |
21 | js = json.loads(text) |
22 | print( HP34401A(**js) ) |
23 | except TypeError as e: |
24 | print(type(e).__name__, str(e), sep=':', file=sys.stderr) |
Das hat zudem denn Vorteil, das deine Ide oder das Builtin help von Python, dir denn richtigen Prototypen der Klasse sagen kann.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.