Seite 1 von 1

Wie verschaltete Funktionen/Klassen?

Verfasst: Freitag 18. Oktober 2019, 14:25
von rob87
Hallo,

ich möchte ein Programm aus 3 Teilen Schreiben.
Teil 1: Input der List die Stellung von einem "Sensor"(Stick) von einem Controller aus
Teil 2: Funktion Hier wird der Zurückgegeben Wert ausgegeben (später soll hier per ServoBlaster an ein GPIO des Raspberry ein PWM Signal übergeben werden)
Teil 3: Main Hier findet die Verknüpfung von Teil 1 und 2 statt

Code: Alles auswählen

#Main
 
import Xbox  #Teil1
from funct import OutputVal #Teilfunktion aus Teil2

Cont = Xbox.Controller

OutputVal(Cont.LK.H())
OutputVal(Cont.MK.R())

Code: Alles auswählen

#Teil 1 Xbox Controller

class stick:
#stick X axis value scaled between -1.0 (left) and 1.0 (right) with deadzone tolerance correction
 def H():
  return 'Horizontale positon'
# Stick axis between middle position(0.0) and right (1.0) 
 def R():
 Wert=-0.1
 if Wert>0 and Wert<100
  print('PWM Wert ist gültig')
 else:
  print('0')

class Controller:
    LK = Stick()
    MK = Stick()

Code: Alles auswählen

#Teil2 Funktionen

def OutputVal(StrVal):
 if StrVal=='' OR StrVal=='0':
  print("Return Unglütlig oder leer")
 else:
  print(%StrVal)
Da ich Neuling in Python bin spuckt der Compiler duzende Fehler aus.
In Teil1
TypeError 'str' object is not callable
In Teil2
invalid syntax (das %-Zeichen)
Habt Ihr eine Erklärung?

Wie sollte man das Lösen?

Re: Wie verschaltete Funktionen/Klassen?

Verfasst: Freitag 18. Oktober 2019, 15:00
von Jankie
Da scheint einiges fehlerhaft zu sein. Ich würde dir DRINGENDST empfehlen das Python Grundlagen Tutorial einmal durchzuarbeiten und dir den PEP 8 Style Guide mal anzuschauen, da dir anscheinend noch viele Grundlagen fehlen (nicht Böse gemeint).

Re: Wie verschaltete Funktionen/Klassen?

Verfasst: Freitag 18. Oktober 2019, 16:13
von __blackjack__
@rob87: Als erstes mal sollte man das bisschen Code nicht unnötigerweise auf drei Module verteilen. Wobei `funct` auch ein sehr schlechter Modulname ist. Namen generell sollen dem Leser vermitteln was der Wert dahinter bedeutet.

Dann wäre es praktisch wenn Du den tatsächlichen Code zeigst inklusive kompletten Traceback. Dein Teil 1 sollte nämlich keinen Laufzeitfehler liefern. 1. Weil er gar nicht am Compiler vorbeikommt, 2. sehe ich auch nicht wie man *die* Ausnahme bekommen kann, selbst wenn man die beiden Syntaxfehler beheben würde.

Und auch bei der Zeiten Ausnahme, die zwar korrekt wäre, würde der Compiler *voher* bereits über einen anderen Syntaxfehler stolpern.

Und was ist Deine konkrete Frage zu dem Syntaxfehler bei ``print(%StrVal)``? Was denkst Du denn was das ``%`` da zu suchen hat?

Eingerückt wird in Python mit vier Leerzeichen pro Ebene.

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

Man sollte keine Abkürzungen verwenden, schon gar keine ein- oder zweibuchstabigen, sondern aussagekräftige, ausgeschriebene Namen verwenden. Ist ja nicht mehr BASIC auf 8-Bit-Rechnern wo nur ein bis zwei Zeichen pro Name erlaubt/signifikant waren. Ebenfalls wichtig ist Gross-/Kleinschreibung — wenn man eine Klasse `stick` nennt, kann man die nicht als `Stick` aufrufen um Objekte davon zu erstellen, das gibt einen `NameError`.

Wenn man zwei Vergleiche gegen ein gemeinsames Argument mit ``and`` verknüpft, bringt in der Regel ein verketteter Vergleich einen etwas kürzeren und leichter verständlichen Ausdruck. Wobei die Vergleichswerte nicht zum Kommentar passen. Da steht Werte von 0.0 bis 1.0, vergleich schliesst aber 0 selbst aus und erlaubt Werte bis 100 statt nur bis 1. Was denn nun? Falsche Kommentare/Dokumentation ist schlechter als gar keine.

Was ebenfalls falsch aussieht ist das die Funktion nichts zurück gibt. Auch das passt nicht zu Kommentar/Dokumentation und auch nicht dazu wie der nicht vorhandene Rückgabewert später verwendet wird.

Die Diskrepanz zwischen Kommentar/Dokumentation und Code bezüglich der Rückgabetypen ist auch schräg. Die ”Xbox”-Funktionen geben laut Kommentar/Dokumentation Zahlen zurück, der Code sieht aber so aus als wären es Zeichenketten‽

Methoden brauchen ein `self`-Argument. Und falls das nicht verwendet wird, ist das semantisch auch keine Methode und braucht einen Grund warum man das in eine Klasse steckt. Falls meinen keinen hat: Entweder die ”Methode” aus der Klasse herausnehmen oder als `staticmethod` dekorieren.

Da `stick` semantisch so wie's da steht keine Klasse ist sondern einfach nur zwei Funktionen, macht auch `Controller` keinen Sinn.

Selbst wenn es eine `Stick`-Klasse gäbe, würde man keine andere Klasse als Namensraum für zwei Exemplare davon missbrauchen. Diese Klasse dann in einem anderen Modul an einen kryptisch abgekürzten Namen zu binden, macht das ganze noch absurder.

Grunddatentypen haben in Namen nichts zu suchen und wie gesagt keine Abkürzungen: `StrVal` → `value`.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Von dem Beispiel bliebe dann ungefähr so etwas übrig:

Code: Alles auswählen

#!/usr/bin/env python3


def get_horizontal_position():
    """
    Get stick X axis value scaled between -1.0 (left) and 1.0 (right) with
    deadzone tolerance correction.
    """
    #
    # FIXME Docs say floating point values, code says string.
    #
    return "Horizontale positon"


def get_right_position():
    """
    Get stick axis between middle position (0.0) and right (1.0).
    """
    wert = -0.1
    #
    # FIXME Either the docs or this code ist wrong.
    #
    if 0 < wert < 100:
        print("PWM Wert ist gültig")
    else:
        print("0")
    #
    # FIXME I'm quite sure this function should return something and presumably
    #   numbers and not strings.
    #


def print_value(value):
    print("Return Unglütlig oder leer" if value in ["", "0"] else value)


def main():
    print_value(get_horizontal_position())
    print_value(get_right_position())


if __name__ == "__main__":
    main()

Re: Wie verschaltete Funktionen/Klassen?

Verfasst: Freitag 18. Oktober 2019, 20:24
von rob87
@Jankie: Danke das werde ich mir gleich anschauen
@__blackjack__: Auch dir ein großes danke ich werde deine Hinweise versuchen umzusetzen.
-> der Code ist natürlich nur ein kleiner Teil und soll die Systematik abklären.
Aber das ist tatsächlich alles was ich bis jetzt habe.
3 Modul weil der Code am ende sehr umfangreich wird. Alleine der Input Code hat in der Vorlage schon 250 Zeilen und wird von mir noch erweitert werden.

-> Das % habe ich aus einem Codeschnipsel aus einem Internet Tut
=> Was meinst du mit *die* Ausnahme (?)
Die Komplette Aufgabenstellung ist:
Ich will einen RC-Traktor mittels Raspberry Pi Zero W durch einen XBOX ONE S Controller Fernsteuern.

Die Namens Formatierung werden ich mir zu herzen nehmen. Abkürzungen würde ich jedoch weiterverwenden sonst würde in der Main folgendes stehen für rund 15 Pins stehen:

Code: Alles auswählen

servoansteuerung_fuer_stellung_0bis100prozent(gpio_pin5,Controller_weiß.linker_knüppel.position_mitte_bis_rechts())
#PWM 1-2ms entspechend der Stellung des linken Controllerknüppels zwischen der mittleren und der rechten Position
Ein wenig lang mMn.

-> Der Controller hat 13 Eingabeelemente deren aktueller Zustand auf 2-6 Verschiedene Arten ausgewertet werden soll
-> Aktuell sind 4 Funktionen geplante PWN für 0-100%, PWM für -100%bis100%, toggle und ONOFF

Wie würde man die Struktur dafür aufbauen?

Re: Wie verschaltete Funktionen/Klassen?

Verfasst: Freitag 18. Oktober 2019, 21:36
von __blackjack__
@rob87: Ich würde trotzdem nicht auf ”Vorrat” schon Module erstellen. Man kann das später noch aufteilen, wenn es tatsächlich zu gross wird.

Und wenn man ein Programm auf Module aufteilt, sollte man die auch sofort in ein Package stecken, damit auf oberster Ebene immer nur *ein* Name in Konkurrenz zu allen anderen auf dem System vorhandenen Modulen/Paketen existiert.

Das `xbox`-Modul ist doch bereits fertig‽ Wobei die Dokumentation dazu sagt man soll das Programm welches das verwendet entweder als Benutzer `root` laufen lassen oder den Benutzer `pi` zur Gruppe `root` hinzufügen — letzteres ist ein absolutes no-go, und das erstere kann/sollte man durch entsprechende Rechtevergabe auch umgehen können.

Und was sollte das ``%`` dort bewirken laut Tutorial aus dem Internet? Was denkst Du denn was das bewirken soll? Man tippt doch nicht wahllos irgenwelche Sonderzeichen ein ohne zu wissen warum?

Mit *die* Ausnahme meinte ich die Ausnahme von der Du behauptet hast die würde bei dem gezeigten Code kommen: ``TypeError 'str' object is not callable``. Das sehe ich so gar nicht. Es macht halt keinen Sinn wenn Du irgendwelchen Code zeigst, bei dem die Ausnahme gar nicht kommt/kommen kann. Sollen wir dann raten wie Dein Code *tatsächlich* aussieht? Das geht ja schlecht.

Keine Abkürzungen! Das bedeutet nicht das Du in jeden Namen einen Roman packen musst, nur so viel dass der Leser eine Chance hat zu verstehen was da passiert. Und das beinhaltet auch Dich selbst als Leser, denn was gerade noch eine klare sinnvolle Abkürzung war, ist in einem halben Jahr auch beim Autoren selbst nicht mehr frisch im Kopf und erschwert das lesen/verstehen. Dein Roman würde eher so aussehen:

Code: Alles auswählen

set_servo(gpio_pin5, min(0, joystick.leftX()))
Was hier noch schlecht ist, ist der Name `gpio_pin5`. Für den Leser ist hier zwar interessant das es eine Pin-Nummer ist, aber nicht das es die 5 ist, sondern was die 5 *bedeutet*. Kann man hier noch aus dem Funktionsnamen erraten, aber `SERVO_PIN` als Konstante ist sinnvoller. Interessant wäre hier auch noch die Information was der Servo steuert, denn dann kann man aus der Zeile herauslesen was mit dem linken Joystick in X-Richtung bewirkt wird.

Aus dem Original kann man dagegen so gut wie *nichts* rauslesen, das ist einfach nur kryptischer Code:

Code: Alles auswählen

OutputVal(Cont.LK.H())
Beziehungsweise mit `gpiozero.Servo` würde das so aussehen:

Code: Alles auswählen

servo.value = min(0, joystick.leftX())
Auch hier wäre es sinnvoll den Namen `servo` so zu erweitern das man ablesen kann was der macht.

Re: Wie verschaltete Funktionen/Klassen?

Verfasst: Samstag 19. Oktober 2019, 20:00
von rob87
Hi,

danke für deine Anregungen. Zum Thema ROOT: Das dingen bekommen keinen Internetzugang und erhält auhc sonst keinen Netzwerkzugriff. Das Phyton Script wird in den Autostart gepackt und soll dann bei 'Strom an' einfachloslaufen.

Zu '%' in Batchprogrammierung ist das das Symbol um auf eine Variable zuzugreifen. also schien es mir erstmal nicht "sinnlos"

gpio_pin5 könntan man mittels Dict noch sinnvoller benennen. da gebe ich dir recht. Wobei ich Beschreibung eher in den Kommentar packte und so Hardware und Software näher bringen.

Das Xbox Moduk ist fertig und für Programmierer leicht zu verstehen. Wenn ich jetzt das allerdings meine Vereinkollegen (teilw. 60+ mit ohne Eng. Kenntnisse) zeige... Zudem gefällt mir nicht das vieles mehr als 2x mal wiederholt wird. Im Job habe ich die Regel alles was du mehr als 2x (gleich) programmierst kommt in eine eigene Funktion.
Zugegeben mein Beispiel war ein übertriebener Roman.

Dennoch wollte ich mich gedanklich in jemanden versetzen der Sonst Servokabel A in Buchse6 Steck und sich freut wenn der Servo sich bewegt wenn er Stick6 betätigt.
Dewegen soll der __MAIN__ - Teil ähnlich einfach gehalten werden, wie das verdrahten von Standard RC-Fernbedinungen, mit dem Unterschied das man mehr Funktionen zur Verfügung hat.

Außerdem sollte der Code so allgemein sein, das ihn hernehmen kann und einfach für einen Bagger / Kran etc. verwenden kann.

Danke im Voraus.

Aktuell sieht mein noch nicht veränderter Code aus:

Code: Alles auswählen

class Val:
    def V():
        return 'Vertikale Position'
    def H():
        return 'Horizontale positon'
    def R():
        return 'Mitte bis rechts'

class Controller:
    LK = Val()
    MK = Val()

Cont = Controller
print(Cont.LK.H())
Compiler:
Message File Name Line Position
Traceback
<module> G:\Privat\RC-Truck\Xbo_controller Steuerng\python_scripte_Lix\Main.py
Line 1
TypeError: 'Val' object is not callable

Re: Wie verschaltete Funktionen/Klassen?

Verfasst: Samstag 19. Oktober 2019, 20:32
von rob87
Namensoptimierung:

Code: Alles auswählen

class analogstick:
    def vertikal_pos():
        return 'Vertikale Position'
    def horizontal_pos():
        return 'Horizontale positon'
    def rechte_pos():
        return 'Mitte bis rechts'

class Controller:
    linker_stick = analogstick()
    mittlererstick = analogstick()

Cont = Controller
print(Cont.mittlererstick.rechte_pos())

Re: Wie verschaltete Funktionen/Klassen?

Verfasst: Samstag 19. Oktober 2019, 20:39
von Sirius3
@rob87: die Fehlermeldung passt immer noch nicht zum Code. Das was Du da zusammenschreibst ist auch kein Python, es sieht jetzt nur mehr nach irgendwas mit Pythonsyntax aus. Am besten arbeitest Du erst mal gründlich ein Python-Tutorial durch, vor allem zu Klassendefinitionen.

`Cont` macht keinen Sinn, weil es nur ein anderer Name für Controller ist.

Re: Wie verschaltete Funktionen/Klassen?

Verfasst: Samstag 19. Oktober 2019, 23:35
von __blackjack__
@rob87: Auch bei Rechnern die keinen Internetzugang haben sollte man nicht einfach als `root` arbeiten. Insbesondere wenn das System ja die Werkzeuge hat das auch sicherer zu regeln. Nur um Joysticks/Controller auszulesen braucht man keine Rechte um jede Datei auf dem gesamten System verändern zu können. Eine Lösung wäre eine eigene Gruppe für Eingabegeräte in die man den Benutzer stecken kann. Standard auf dem Rechner an dem ich hier gerade tippe ist das die Eingabegeräte”dateien” bereits einer Gruppe mit dem Namen `input` gehören. Hier würde es also reichen den Benutzer *dieser* Gruppe hinzuzufügen.

Das "%"-Zeichen hat in einigen Programmiersprachen eine Bedeutung bei Variablennamen. Bei Batch-Skripen um Variablen zu kennzeichnen (wobei man da noch eines am Ende braucht (z.b. `%path%`)), in Perl kennzeichnen sie das man eine Variable als Hash verwenden will (`dict` in Python), in vielen BASIC-Dialekten wird die Variable durch ein angehängtes "%" als Ganzzahltyp deklariert. Da in dem Beispiel von Dir aber noch viele andere Variablen vorkommen und das "%" nur vor *einem* Namen steht, muss man sich doch gerade wenn genau *das* dann als Syntaxfehler abgelehnt wird, doch überlegen welchen Sinn das ausgerechnet an der Stelle haben soll. Zudem hat man zu dem Zeitpunkt wo man anfängt *so* ein Projekt anzugehen, doch mal die Grundlagen durchgearbeitet, und da kommt das nirgends vor als Präfix oder unärer Operator. Ist ja auch kein Wunder, weil das syntaktisch kein korrektes Python ist.

Wieso ein `dict` für eine sinnvollere Benennung von `gpio_pin5`? Einfach eine Konstante reicht doch. Falls man da struktieren möchte, könnte man noch Gruppen von Zahlen mit `enum.IntEnum` zusammenfassen.

Das `xbox`-Modul ist in der Tat nicht so schön geschrieben, aber wenn man es nur *verwenden* will, und deshalb auch nicht reinschauen muss, ist das ja nicht ganz so schlimm. Aber das hat offensichtlich kein Python-Programmierer geschrieben. Darum wahrscheinlich auch die eigentlich einfach zu beseitigenden Wiederholungen.

Ich verstehe aber sowieso nicht so ganz warum dieser komische Weg verwendet wurd die Text-Logausgaben von diesem Treiberprogramm/daemon zu parsen, denn das Programm stellt ja wohl ganz normal die Joystick- und XPad-Schnittstelle zur Verfügung die man beispielsweise mit PyGame verwenden kann. Und da dann eben nicht nur die Geräte die `xboxdrv` unterstützt, sondern auch alle anderen Geräte die der Linuxkernel unterstützt. Ich frage mich auch was `xboxdrv` besser macht als der Kernel wenn man ihn einfach nur als Treiber verwendet. Also ich meine *heute*.

Wenn ich das neu schreiben würde, dann so, dass es gut mit `gpiozero` funktioniert. Also beispielsweise eine Klasse die jeweils Joystickachsen und Buttons kapselt und zum Beispiel das `value`-Property anbietet und ein `ValuesMixin` verwendet um ein `values`-Property zur Verfügung zu stellen.

Die `Servo`-Klasse erwartet Werte zwischen -1.0 und 1.0, und so eine Joystickachse liefert Werte zwischen -1.0 und 1.0. Um also mit einer Joystickachse einen Servo 1:1 zu steuern würde ich das so implementieren, dass man das so schreiben kann:

Code: Alles auswählen

#!/usr/bin/env python3
from signal import pause

from gpiozero import LED, Servo

from xbox_zero import Controller

LED_PIN = 17
SERVO_PIN = 5


def main():
    controller = Controller()

    led = LED(LED_PIN)
    led.source = controller.button_a

    servo = Servo(SERVO_PIN)
    servo.source = controller.left_joystick.x_axis

    pause()


if __name__ == "__main__":
    main()