2016-07-08 33 views
0

Ich habe ein Stück Hardware Ich schreibe eine Klasse für die mehrere Eingänge hat. Jeder Eingang (Kanal) hat einen Namen, so habe ich eine Liste:Erstellen Sie Eigenschaften in der Klasse von Liste/Attribut in __init__

CHANNELS = {"T0": 0, "Tend": 1, "A": 2, "B": 3, "C" : 4, "D" : 5, 
    "E" : 6, "F" : 7, "G" : 8, "H" : 9} 

Nun würde Ich mag eine Eigenschaft erstellen, den Wert jedes Kanals zuzugreifen:

@property 
def readT0(self) 
    return self._query("REQUEST STRING 0") 
    # the request string expects a number, instead of the real name T0 

@property 
def readTend(self) 
    return self._query("REQUEST STRING 1") 

usw.

ich würde eher so etwas wie dieses (weniger Quellcode) tun:

def read(self, channel) 
    return self._query("REQUEST STRING %s" % channel) 

und eine Art von Übersetzung verwenden, um das erstellen Attribute:

def __init__ bla bla: 
    bla bla 

    for x in CHANNELS: 
     setattr(self, "read" + str(x), self.read(CHANNELS[x]) 

so

class.readA # channel A value: 10 
> 10 
class.readT0 # channel T0 value: 0.11 
> 0.11 

Auf diese Weise, wenn die Hardware mehr Kanäle verwendet, ich zum CHANNELS Wörterbuch nur hinzufügen kann. Oder gibt es einen besseren Weg?

Da ich nur Werte lesen möchte, würde ich hier aufhören. Aber kann man das auch mit einem Setter kombinieren?

edit:

ich klären müssen: Ich will nicht, um das Wörterbuch ändern oder die Werte des Wörterbuchs zur Laufzeit zugreifen, möchte ich es auf Klassenerstellung verwenden, um mehrere Attribute für eine Hardware zu erstellen Lesen Sie die Hardwarewerte.

Die Hardware ist ein ADC mit Kanälen. Ich kann mit

someclass._query("REQUEST STRING i") 
# i is the channel number and returns the ADC value (e.g. 10.45 V) 
+0

Warum willst du da drin lesen? Wäre 'class.A0' nicht ordentlicher? Wie entspricht '_query' dem Wörterbuch? – jonrsharpe

+0

Hallo, 'Klasse.T0' oder' Klasse.A' wäre in der Tat schöner, aber die Kanäle haben unterschiedliche Eigenschaften, so dass 'lesen' sich gut in' class.delayT0' und 'class.offsetT0' ändern könnte ... Don ' Ich weiß, was Sie mit "korrespondieren" meinen: '_query()' ist eine com-Funktion, die von der Hardware liest. – Wolf82

+1

Verwenden Sie keine Eigenschaften für etwas, das eine Nebenwirkung hat, das ist schrecklich, verwenden Sie stattdessen Methoden. Erinnere dich an das [zen of python] (https://www.python.org/dev/peps/pep-0020/): Explizit ist besser als implizit. Eine Eigenschaft dient dazu, eine kleine Operation für ein Attribut zu verbergen, ohne eine kostspielige Methode in einem einfachen Attribut zu verschleiern. –

Antwort

1

Wenn Sie wirklich wollen, um dynamisch Funktionen erstellen und Mitglieder Ihrer Klasseninstanzen zu machen, können Sie lambda verwenden:

CHANNELS = {"T0": 0, "Tend": 1, "A": 2, "B": 3, "C" : 4, "D" : 5, 
    "E" : 6, "F" : 7, "G" : 8, "H" : 9} 

class Foo(object): 
    def __init__(self): 
     for x in CHANNELS: 
      setattr(self, "read{}".format(x), lambda x=x: self.read(CHANNELS[x])) 

    def read(self, channel): 
     return self._query("REQUEST STRING {}".format(channel)) 

    def _query(self, q): 
     print "query {}".format(q) 


f = Foo() 
f.readT0() 
f.readTend() 
f.readA() 
f.readB() 
f.readC() 

Es funktioniert, aber es gibt ein paar Nachteile:

  • es schafft der gleiche Satz von Funktionen für jede Instanz (ohne Grund in diesem Fall, da alle Instanzen auf der gleichen CHANNELS Definition basieren)
  • diese Funktionen nicht dokumentiert sind und erscheint nicht in dir(Foo) oder help(Foo)

der jonsharpe __getattr__ Lösung den ersten Punkt löst aber nicht die zweite. Die einfachste Lösung ist es, eine Klasse Dekorateur zu verwenden, die die Getter hinzufügen wird (entweder als Methoden oder Eigenschaften, es ist bis zu Ihnen) auf die Klasse selbst, dh (Eigenschaften Version):

def with_channel_props(cls): 
    for x in cls.CHANNELS: 
     getter = lambda self, x=x: self.read(self.CHANNELS[x]) 
     setattr(cls, "{}".format(x), property(getter)) 
    return cls 


@with_channel_props 
class Baaz(object): 
    CHANNELS = { 
     "T0": 0, "Tend": 1, "A": 2, "B": 3, "C" : 4, "D" : 5, 
     "E" : 6, "F" : 7, "G" : 8, "H" : 9 
     } 

    def read(self, channel): 
     return self._query("REQUEST STRING {}".format(channel)) 

    def _query(self, q): 
     return "query {}".format(q) 

b = Baaz() 
print b.T0 
print b.Tend 
print b.A 
print b.B 
print b.C 

Jetzt können Sie dir(Baaz) verwenden und help(Baaz) oder irgendein anderer Introspektionsmechanismus.

-1

den ADC-Wert jeden Kanals lesen Ich denke das ist, was Sie wollen:

class Something(object): 

    CHANNELS = {"T0": 0, "Tend": 1, "A": 2, "B": 3, "C": 4, "D": 5, 
       "E": 6, "F": 7, "G": 8, "H": 9} 

    def __getattr__(self, name): 
     """Handle missing attributes.""" 
     if name.startswith('read') and name[4:] in self.CHANNELS: 
      return self.CHANNELS[name[4:]] 
     return super(Something, self).__getattribute__(name) 

Im Einsatz:

>>> thing = Something() 
>>> thing.readA 
2 
>>> thing.readT0 
0 
>>> thing.garbage 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 8, in __getattr__ 
AttributeError: 'Something' object has no attribute 'garbage' 

Sie können dann diesen Kanal verwenden Attribute in Ihrem Anruf an _query wie erforderlich. Wenn Sie dem Wörterbuch auch zuweisen möchten, müssen Sie __setattr__ implementieren.

+0

Hallo, ich möchte nicht aus dem Wörterbuch, sondern von einem ADC-Eingang lesen. CHANNELS ist nur von 'A'zu 2 zu übersetzen, da die Hardware Nummern als Kanalbezeichner verwendet. – Wolf82

+0

@ Wolf82 ja, das zeigt Ihnen nur, wie Sie '__getattr__' verwenden, um fehlende Attribute zu behandeln und die relevante Kanalnummer aus dem Wörterbuch zu bekommen. Das in Ihre Code-Basis zu integrieren ist Ihre Aufgabe! – jonrsharpe