Datenstruktur Variable mit 2 Namen.

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.
Antworten
rob87
User
Beiträge: 45
Registriert: Donnerstag 17. Oktober 2019, 14:24

Hallo,

ich bekommen von einem Event (raspbian) eine ID 1-5/300-307 usw und ein Value.

Also zum Bespiel ID 305 Value 1.0 oder ID 4 und -31566.

Jetzt würde ich gerne ein Stück Code entwickeln wo ich die Definition nur einmal machen muss UND die die werte mittels Punktoperator auslesen kann:

Code: Alles auswählen

for event in gamepad.read_loop():
    #Input speichern
    if event.code in HardwareID: # ID 305 / 4
        HardwareObj.HardwareID[event.code] = event.value #value 1.0 / -31566
   
   
   #Output
   if HardwareObj.taste = 1.0 #ID 305
       LED = true
   else
       LED = false
       
   PWMServo = formatint2PWM(HardwareObj.poti)
  
Achtung Code kann syntatische Fehler enthalten!

Ich habe viel über Tupel, Mengen, Dict und Klassen gelesen, allerdings fehlt mir die Verknüpfung wie ich nur einmal die ID (305) dem Attribut (taste) zuweisen kann.

Das einzige was mir eingefallen ist, ist ein dict mit ID und ein Klasse anlegen. Allerdings muss ich ja dann den Atributnamen 2x schreiben. Oder ein Switch Case Anweisung mit der ID als Statement.

Habe Ihr noch eine Idee?
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@rob87: Falls sich die Daten nach dem erstellen nicht ändern würde ich einen Typ mit `collections.namedtuple()` erstellen. Sonst eine Klasse. Ohne ein Wörterbuch. Wozu soll das gut sein?
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
rob87
User
Beiträge: 45
Registriert: Donnerstag 17. Oktober 2019, 14:24

Hi,

die Name und ID's ändern sich nicht die Value schon...Aber ich stelle gerade fest das meine Frage eine unschärfe aufweist. Richtig müsste die Frage lauten:
Wie kann ich Werte die Eventbasiert(von einem Controller) mit ID-Zuordnung und in einem zyklischen Script mit Namenszuordnung organisieren?

Ziel des Projekts: Ich will mittels Controller Analogstick (Eingabe Event) einen Motor(zyklischkontinuierliche Ausgabe) steuern.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@rob87: Wie verändert sich denn der Wert? Ich verstehe nicht wirklich was Du erreichen willst. In einem Wert zwei Werte zusammenfassen die dann mittels Punktoperator angefragt werden ist ein Objekt. Entweder ein unveränderliches, dann kann man `collections.namedtuple()` verwenden um den Datentyp dafür zu erstellen. Oder man schreibt eben eine Klasse mit den beiden Attributen.

Zum Code aus dem ersten Beitrag: Der wirft mehr fragen auf als er beantwortet. Zum Beispiel ist der syntaktisch nicht korrekt, kommt also nicht einmal am Compiler vorbei. Das ist schon mal kein gutes Zeichen für ein Beispiel wenn man das erst einmal ratend was denn gemeint sein könnte, repariert werden muss.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase).

Was ist denn der Geschmack mit dem Wert 1.0? SCNR :-) Man sollte sich für Deutsch oder Englisch entscheiden. In der Regel Englisch, weil die Schlüsselworte von Python und alle anderen Bibliotheken auch Englisch verwenden.

Kann es sein, dass Du eigentlich ein Wörterbuch suchst das IDs auf Attributnamen abbildet und die `setattr()`-Funktion?

Das ``if``/``else`` ist überflüssig wenn man einen Wahrheitswert zuweisen will der auf dem Ergebnis der ``if``-Bedingung beruht. Die Bedingung selbst wird doch schon zu einem Wahrheitswert ausgewertet.

Ungetestet:

Code: Alles auswählen

    event_code_to_attribute_name = {305: "key", 4: "poti"}

    ...

    for event in gamepad.read_loop():
        attribute_name = event_code_to_attribute_name.get(event.code)
        if attribute_name:
            setattr(hardware_object, attribute_name, event.value)

        led = hardware_object.key == 1
        servo_pwm = format_int2pwm(hardware_object.poti)
Wobei `hardware_object` ein ziemlich schlechter Name ist IMHO.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
rob87
User
Beiträge: 45
Registriert: Donnerstag 17. Oktober 2019, 14:24

Hallo, lang ists her doch das Projekt ist nicht tot.

Zur Nutzung von async bin ich jetzt von Python2.7 auf python3.8 gewechselt. Leider funktioniert die Klasseninitialisierung nicht mehr, bzw. der Compiler meckert
AttributeError: type object 'Stick' has no attribute 'X'
.

In 2.7 funktioniert folgender code (gekürzt)

Code: Alles auswählen

class Stickachse:
	def __init__(self, B0100 = 0.0, B100100 = 0.0, B1000 = 0.0):
		pass
	def input(self,val):
		global B0100,B100100,B1000
		self.B100100=val

class Stick:
	def __init__(self, X = Stickachse,Y = Stickachse, Button = 0.0):
		pass
	
linker_stick = Stick
rechter_stick = Stick	

for i in range(0,5):
	linker_stick.X.input(i)
	print(linker_stick.X.B100100)

Wo liegt mein fehler?
Da ich mit dem Code bereits einen Servo gesteuert habe bin ich relativ ratlos...
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@rob87: Nein der Code funktioniert nicht in Python 2.7. (Und schon wieder ``global``…)
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
rob87
User
Beiträge: 45
Registriert: Donnerstag 17. Oktober 2019, 14:24

__blackjack__ hat geschrieben: Freitag 17. September 2021, 20:55 @rob87: Nein der Code funktioniert nicht in Python 2.7. (Und schon wieder ``global``…)
Magst du mir vll. verraten wie ich die Klassen definieren muss damit der folgende Code Funktioniert?

Code: Alles auswählen

...
for i in range(0,5):
	linker_stick.X.input(i)
	print(linker_stick.X.B100100)
...
	
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@rob87,

Code: Alles auswählen

class A:
    pass
dies definiert eine Klasse mit dem Namen "A"

Code: Alles auswählen

a = A
jetzt sind "A" und "a" Namen für die selbe zuvor definierte Klasse

Code: Alles auswählen

a = A()
jetzt ist "a" eine Instanz der Klasse mit dem Namen "A".
Das sollte den Fehler in deinem Programm erklären. Es gibt da noch einige andere Merkwürdigkeiten, aber ich verstehe nicht ganz was du da eigentlich erreichen willst.
Benutzeravatar
sparrow
User
Beiträge: 4164
Registriert: Freitag 17. April 2009, 10:28

@rob87: Deine Codebeispiele und deine Erklärung helfen leider nicht zu verstehen, was du eigentlich machen möchtest.

Vielleicht Ist daher das, was du für die Lösung hälst, eigentlich nicht korrekt und du solltest mal das grundlegende Problem beschreiben.
rob87
User
Beiträge: 45
Registriert: Donnerstag 17. Oktober 2019, 14:24

sparrow hat geschrieben: Samstag 18. September 2021, 19:24 @rob87: Deine Codebeispiele und deine Erklärung helfen leider nicht zu verstehen, was du eigentlich machen möchtest.

Vielleicht Ist daher das, was du für die Lösung hälst, eigentlich nicht korrekt und du solltest mal das grundlegende Problem beschreiben.
Naja wie ich Eingang schon geschrieben habe bekommen ich ID und Value als aktuellen Zustand eines Xbox Controllers über Bluetooth (events).

Jetzt will ich im weiteren Programm aber nicht mit der ID aus einer List oder einem dict Arbeiten sondern mit Objektvariablen arbeiten.

So, der Controller hat 2 Sticks/Knüppel oder wie auch immer man es nennen möchte. Jeder Stick hat 2 (Stick)achsen (Horizintale Richtung - X /Vertikale Richtung - Y) und einen Button.
Diese Stickachse wird in 3 Bereiche unterteilt: Links mit mitte (B1000), Links bis rechts(B100100) und Mitte bis rechts (B0100)

Wenn ich also wissen will zu wie viel Prozent des Linken Sticks der Auschlag in X Richtung auf der rechten Seite ist, frage ich im Programm die Variable Controller.LS.X.B0100 ab.
Auch muss ich der Umrechnungsfunktion den Eventwert zuschieben.
Wenn das Programm anhand der ID erkennt (if /elif -Funktion), ah der Eventinput kommt von der X Achse Linker Stick also rufe ich folgende Funktion auf:
Controller.LS.X.input(Eventvalue) -> Diese Funktion berechnet nun die Stellung in einem Bereich von -1.0(ganz links) und 1.0(ganz rechts) und beschreibt die drei Variablen B1000; B100100; B0100 entsprechend.

@rogerb du hast recht, ich habe die Klammern vergessen.

Ich weiß halt nur nicht wie ich die Variable innerhalb einer Klassen definieren muss damit sie A: von außen zugreifbar sind und B: Beim initialisieren der Instanz einmalig auf 0.0 gesetzt werden?

Ich versuche es mal platischer (aber vermutlich noch falsch)

Code: Alles auswählen

class Finger():
	daumen #Variable anlegen
	def __init__(self):
		self.daumen = "Gerade"
		
	def kruemmen(self)
		self.daumen = "Krumm"
	def strecken(self)
		self.daumen = "Gerade"
	#weitere Fingerdefinition gespart
class Hand()
	rechts= Finger()
	links = Finger()

def main()
	haende = Hand()
	print(hand.rechts.daumen)
	hand.rechts.kruemmen()
	print(hand.rechts.daumen)
	print(hand.links.daumen)
Ausgabe sollte dann sein:
Gerade
Krumm
Gerade
wie sollte es richtig gecoded werden?
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@rob87,

wenn du die offensichtlichen Syntax-Fehler behebst, funktioniert es doch wie erwartet.
narpfel
User
Beiträge: 643
Registriert: Freitag 20. Oktober 2017, 16:10

@rob87: In Python müssen Attribute nicht irgendwie vorher „angelegt“ werden. Ein `daumen` auf Klassenebene hat keinen Effekt außer einen `NameError` auszulösen, wenn `daumen` vorher nicht (als globale Variable oder im gleichen `class`-Block) angelegt wurde. Das legt nicht ein Attribut für Instanzen der Klasse an.

Wenn ein Attribut beim Initialisieren angelegt werden soll, dann musst du es beim Initialisieren (also in `__init__`) anlegen bzw. an `self` binden. Damit es von außen zugreifbar ist, musst du gar nichts machen: In Python sind alle Attribute immer von außen zugreifbar.

Variablen, die im `class`-Block definiert werden, sind Klassenvariablen (was in anderen Sprachen auch „statisch“ genannt wird). Damit teilen sich dann alle Instanzen das gleiche Objekt, und das will man in 99% aller Fälle nicht.

Korrekt müsste es also in etwa so sein:

Code: Alles auswählen

class Finger:
    def __init__(self):
        self.zustand = "gerade"
    
    def krümmen(self):
        self.zustand = "krumm"
    
    def strecken(self):
        self.zustand = "gerade"


class Hand:
    def __init__(self):
        self.daumen = Finger()
        self.zeigefinger = Finger()


def main():
    linke_hand = Hand()
    print(linke_hand.daumen.zustand)
    linke_hand.zeigefinger.krümmen()
    print(linke_hand.daumen.zustand)
    print(linke_hand.zeigefinger.zustand)
In deinem Code macht die Aufteilung der Klassen keinen Sinn, das habe ich also etwas angepasst. Zumindest hat bei mir nicht jeder Finger einen Daumen, und meine Hände haben auch keine „linken“ und „rechten“ Finger.

Wichtigster Unterschied ist, dass die einzelnen `Finger` nicht Attribute der Klasse `Hand` sind, sondern Attribute von Instanzen der Klasse `Hand`.
rob87
User
Beiträge: 45
Registriert: Donnerstag 17. Oktober 2019, 14:24

narpfel hat geschrieben: Samstag 18. September 2021, 20:58 @rob87:
Korrekt müsste es also in etwa so sein:

Code: Alles auswählen

class Finger:
    def __init__(self):
        self.zustand = "gerade"
  ...

class Hand:
    def __init__(self):
        self.daumen = Finger()
        self.zeigefinger = Finger()
...
1000 Dank für dieses Beispiel! Mir war die Bedeutung des "self."- Operators noch nicht ganz klar. Jetzt kann ich meinen Code umsetzen. Echt spritze !!!
rob87
User
Beiträge: 45
Registriert: Donnerstag 17. Oktober 2019, 14:24

narpfel hat geschrieben: Samstag 18. September 2021, 20:58 @rob87:

Korrekt müsste es also in etwa so sein:

Code: Alles auswählen

class Finger:
    def __init__(self):
        self.zustand = "gerade"
    
    def krümmen(self):
        self.zustand = "krumm"


class Hand:
    def __init__(self):
        self.daumen = Finger()
        self.zeigefinger = Finger()


def main():
    linke_hand = Hand()
    print(linke_hand.daumen.zustand)
    linke_hand.zeigefinger.krümmen()
    print(linke_hand.daumen.zustand)
    print(linke_hand.zeigefinger.zustand)
Jetzt dachte ich, ich habe kapiert und dann doch nicht...
Eigentlich wollte ich beim überschreiten eines Schwellwerts einen Speicher setzen und beim erneuten Überstreiten rücksetzen

Dee Frage für mich ist wieso wird Zeile 38 nur ausgführt(bzw geprinted) und warum wird alt_val nich gespeichert?

Ausgabe:
OFF 0.77
OFF 0.78
ON 0.8300000000000001
OFF 0.91 <--!! hier sollte eigentlich weiterhin "ON" stehen
ON 0.9400000000000001
Wo ist der Fehler:

Code: Alles auswählen

import time
import random
class Flanke:
	def __init__(self):
		self.alt_val = 0.0
		
	def pos(self,val,schwelle= 0.75):
		if val > schwelle and self.alt_val<schwelle: # erstes mal die Schwelle überschritten
			return True
		else:
			return False
		self.alt_val = val # aktuellen wert speichern
		print(self.alt_val)
		
	def neg(self,val,schwelle= 0.75):
		if val<schwelle and self.alt_val>schwelle:
			return True
		else:
			return False
		self.alt_val = val
		
class Toggle:
	def __init__(self):
		self.zustand= False
		self.flanke = Flanke() # instanz Flanke inkl. speicher für -alt_val- erzugen
	
	def status(self,val, schaltschwelle = 0.5):
		if self.flanke.pos(val,schaltschwelle):
			self.zustand = not self.zustand # Zustand wechseln
		return self.zustand


		
def simu_input(input): #input zwischen 0.4 und 1.0 schwingen lassen
	input_richtung =1.0
	add=(random.randint(1, 10))*0.01
	
	if input+add >  1.0:
		input_richtung =-1.0
	elif input - add <  0.4:
		input_richtung =1.0

	return input + (add*input_richtung)
	

def main():

	schalter= Toggle() # bistabiler Schalter
	stick = 0.7 #eingabeelement von Hardware
	while True:
		if schalter.status(stick,0.8):
			str = 'ON %s'%(stick)
		else:
			str = 'OFF %s'%(stick)
		print(str)
		
		stick=simu_input(stick)
		time.sleep(0.2)

input_richtung=1.0	
main()

Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@rob87: Da ist ein unbenutztes `input_richtung` auf Modulebene. Und was ist denn bei Dir Zeile 38? Bei mir ist das ``if input+add > 1.0:`` in der `simu_input()`-Funktion.

In `Flanke.pos()` und `Flanke.neg()` steht jeweils toter Code nach dem ``if``/``else``. Denn dort steht in beiden Zweigen ein ``return``, womit der Code danach nie ausgeführt werden kann.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wie sollte denn in Flanke.pos deiner Meinung nach Self.alt_val gesetzt werden, wenn du aus beiden if-Zweigen darüber mit Return die Methode verlässt?
rob87
User
Beiträge: 45
Registriert: Donnerstag 17. Oktober 2019, 14:24

__blackjack__ hat geschrieben: Donnerstag 23. September 2021, 11:46 @rob87: Da ist ein unbenutztes `input_richtung` auf Modulebene. Und was ist denn bei Dir Zeile 38? Bei mir ist das ``if input+add > 1.0:`` in der `simu_input()`-Funktion.

Code: Alles auswählen

print(self.alt_val)
In `Flanke.pos()` und `Flanke.neg()` steht jeweils toter Code nach dem ``if``/``else``. Denn dort steht in beiden Zweigen ein ``return``, womit der Code danach nie ausgeführt werden kann.
Ok wird nach "return" kein Code mehr abgearbeitet? dann müsste man es so umschrieben:

Code: Alles auswählen

	def pos(self,val,schwelle= 0.75):
		if val > schwelle and self.alt_val<schwelle: # erstes mal die Schwelle überschritten
			ret_val= True
		else:
			ret_valFalse
		self.alt_val = val # aktuellen wert speichern
		print(self.alt_val)
		return ret_val

rob87
User
Beiträge: 45
Registriert: Donnerstag 17. Oktober 2019, 14:24

So funktionierts jetzt:

Code: Alles auswählen

import time
import random
class Flanke:
	def __init__(self):
		self.alt_val = 0.0
		
	def pos(self,val,schwelle= 0.75):
		if val >= schwelle and self.alt_val<schwelle:
			ret_val= True
		else:
			ret_val= False
		self.alt_val = val # aktuellen wert speichern
		return ret_val
		
	def neg(self,val,schwelle= 0.75):
		if val<=schwelle and self.alt_val>schwelle:
			ret_val= True
		else:
			ret_val= False
		self.alt_val = val
		return ret_val
		
class Toggle:
	def __init__(self):
		self.zustand= False
		self.flanke = Flanke()
	
	def status(self,val, schaltschwelle = 0.5):
		if self.flanke.pos(val,schaltschwelle):
			self.zustand = not self.zustand
		return self.zustand


		
def simu_input(input): #input zwischen 0.4 und 1.0 schwingen lassen
	input_richtung =1.0
	add=(random.randint(1, 10))*0.01
	
	if input+add >  0.88:
		input_richtung =-1.0
	elif input - add <  0.4:
		input_richtung =1.0

	return input + (add*input_richtung)
	

def main():

	schalter= Toggle() # bistabiler Schalter
	stick = 0.7 #eingabeelemnt von Hardware
	while True:
		if schalter.status(stick,0.8):
			str = 'ON %s'%(stick)
		else:
			str = 'OFF %s'%(stick)
		print(str)
		stick=simu_input(stick)
		time.sleep(0.2)

input_richtung=1.0	
main()

Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@rob87: eingerückt wird immer mit 4 Leerzeichen pro Ebene, keine Tabs. Wenn man eine if-Abfrage nur dazu benutzt, einer Variable True oder False zuzuweisen, dann kann man gleich die Bedinung der Variable zuweisen.
Benutze keine Abkürzungen, statt alt_val würde man alternative_value schreiben, oder sollte das alter_wert heißen? Die Mischung aus deutsch und englisch ist hier sehr verwirrend.
In simu_input ist der elif-Zeig unnötig, da darin input_richtung gar nicht verändert wird. Der Kommentar stimmt nicht damit überein, was die Funktion macht.
`str` ist der Name einer eingebauten Klasse und sollte nicht überschrieben werden.

Code: Alles auswählen

import time
import random

class Flanke:
    def __init__(self):
        self.alter_wert = 0.0
        
    def pos(self, wert, schwelle=0.75):
        resultat = wert >= schwelle and self.alter_wert < schwelle
        self.alter_wert = wert
        return resultat
        
    def neg(self, wert, schwelle=0.75):
        resultat = wert <= schwelle and self.alter_wert > schwelle
        self.alter_wert = wert
        return resultat
        
class Toggle:
    def __init__(self):
        self.zustand = False
        self.flanke = Flanke()
    
    def status(self, wert, schaltschwelle=0.5):
        if self.flanke.pos(wert, schaltschwelle):
            self.zustand = not self.zustand
        return self.zustand
        
def simuliere_eingabe():
    """ input zwischen 0.4 und 1.0 schwingen lassen """
    wert = 0.7
    richtung = 1.0
    while True:
        add = random.randint(1, 10) * 0.01
        if wert + add > 1.0:
            richtung = -1.0
        elif wert - add < 0.4:
            richtung = 1.0
        wert += add * richtung
        yield wert
    
def main():
    stick = simuliere_eingabe()
    schalter = Toggle() # bistabiler Schalter
    while True:
        wert = next(stick)
        if schalter.status(wert, 0.8):
            text = f'{wert:.2f} ON'
        else:
            text = f'{wert:.2f} OFF'
        print(text)
        time.sleep(0.2)

if __name__ == "__main__":
    main()
Antworten