Klassen vs. Funktionen [geschlossen]

73

Funktionen sind selbst für jemanden ohne Programmiererfahrung, aber mit einem fairen mathematischen Hintergrund leicht zu verstehen. Auf der anderen Seite scheinen Klassen schwieriger zu verstehen zu sein.

Angenommen, ich möchte eine Klasse / Funktion erstellen, die das Alter einer Person anhand ihres Geburtstagsjahres und des aktuellen Jahres berechnet. Soll ich dafür eine Klasse oder eine Funktion erstellen? Oder hängt die Wahl vom Szenario ab?

PS Ich arbeite an Python, aber ich denke, die Frage ist allgemein.

multigoodverse
quelle
Klassen sind für größere Produkte. In einfachen Worten, denken Sie an die Schrauben und Muttern eines Autos als Objekte, während auch das Auto ein Objekt ist. Wenn Sie zum Spaß Beispielprogramme schreiben, bleiben Sie bei den Funktionen.
Saran-san
1
Ich schrieb meine Gedanken hier auf
Toby
1
Was ist mit diesem Ansatz: Beginnen Sie mit einer Funktion. Wenn Sie feststellen, dass Sie mehrere Funktionen schreiben, die immer dieselben Eingabeparameter benötigen, implementieren Sie diese Funktionen als Methoden einer Klasse.
Kevin Südmersen

Antworten:

134

Erstellen Sie eine Funktion. Funktionen machen bestimmte Dinge, Klassen sind bestimmte Dinge.

Klassen haben oft Methoden, die Funktionen sind, die einer bestimmten Klasse zugeordnet sind, und Dinge tun, die mit der Sache verbunden sind, die die Klasse ist - aber wenn Sie nur etwas tun möchten, ist eine Funktion alles, was Sie brauchen.

Im Wesentlichen ist eine Klasse eine Möglichkeit, Funktionen (als Methoden) und Daten (als Eigenschaften) in einer logischen Einheit zu gruppieren, die sich um eine bestimmte Art von Dingen dreht. Wenn Sie diese Gruppierung nicht benötigen, müssen Sie keine Klasse erstellen.

Bernstein
quelle
9
Das ist nicht genau richtig. Klassen ermöglichen auch den Versand nach Typen. Wenn Sie eine Gruppe oder Vorlage mit zusammenarbeitenden Funktionen haben, kann es sich durchaus um eine One-Shot-Ausführung handeln. Wenn Sie jedoch eine Klasse verwenden, haben Sie die Möglichkeit, verschiedene Funktionen in der Gruppe selektiv zu implementieren, um Spezialisierungen der Vorlage zu erhalten. Dies ist mit einfachen Funktionen nicht so einfach zu
bewerkstelligen
... Wenn Sie Ihre Logik erzwingen, können Sie die Klassenfunktionen (z. B. als vtable) als Teil des Status oder der Daten der Instanz einschließen. Die Funktionen selbst werden nicht in der Instanz gespeichert, sondern stattdessen indirekt referenziert. Unter dem Strich haben Sie möglicherweise keine Daten (außer einem Zeiger auf eine vtable), Sie haben möglicherweise nicht mehr als eine Instanz, aber eine Klasse könnte dennoch eine gute Idee sein, wenn Sie eine Familie verwandter Algorithmen implementieren müssen, die aus kollaborierende Funktionen.
Memeplex
1
Zusammenfassend: a classgruppiert Funktionen und Daten. Dies hat unter keinen Umständen Vorteile. Wir haben Funktionen, Datentypen und Module, um sie zu gruppieren. Selbst wenn Sie einen Ad-hoc-Funktionspolymorphismus benötigen: verschiedene Funktionsimplementierungen für verschiedene Datentypen, gibt es viel bessere Lösungen als OOP-Klassen; Betrachten Sie Typklassen im Haskell-Stil, die letztendlich sprachunabhängig sind.
Ton
20

Wie das, was Amber in ihrer Antwort sagt : Erstellen Sie eine Funktion. In der Tat, wenn Sie keinen Unterricht machen müssen, wenn Sie etwas haben wie:

class Person(object):
    def __init__(self, arg1, arg2):
        self.arg1 = arg1
        self.arg2 = arg2

    def compute(self, other):
        """ Example of bad class design, don't care about the result """
        return self.arg1 + self.arg2 % other

Hier haben Sie nur eine Funktion, die in einer Klasse gekapselt ist. Dies macht den Code nur weniger lesbar und weniger effizient. Tatsächlich kann die Funktion computeeinfach so geschrieben werden:

def compute(arg1, arg2, other):
     return arg1 + arg2 % other

Sie sollten Klassen nur verwenden, wenn Sie mehr als eine Funktion haben und wenn das Beibehalten eines internen Status (mit Attributen) sinnvoll ist. Wenn Sie andernfalls Funktionen neu gruppieren möchten, erstellen Sie einfach ein Modul in einer neuen .pyDatei.

Sie könnten dieses Video (Youtube, ca. 30 Minuten) ansehen , das meinen Standpunkt erklärt. Jack Diederich zeigt, warum Klassen in diesem Fall böse sind und warum es so ein schlechtes Design ist, besonders in Sachen API.
Es ist ein ziemlich langes Video, aber es ist ein Muss.

Maxime Lorant
quelle
4
Wenn Sie 100 Funktionen benötigen, schreiben Sie 100 Funktionen. Ich sehe überhaupt keine Notwendigkeit oder keinen Nutzen für den Unterricht. Es sei denn, Sie stecken im OOP-Denken fest.
Ton
Vielen Dank für die Veröffentlichung des Videos: Folgendes ist mir im Kopf geblieben: Erstellen Sie keine Klasse, die nur zwei Methoden hat und eine davon ist init
Kevin Südmersen
8

Das hängt vom Szenario ab. Wenn Sie nur das Alter einer Person berechnen möchten, verwenden Sie eine Funktion, da Sie ein einzelnes bestimmtes Verhalten implementieren möchten .

Wenn Sie jedoch ein Objekt erstellen möchten, das das Geburtsdatum einer Person (und möglicherweise andere Daten) enthält, es ändern kann, kann die Berechnung des Alters eine von vielen Operationen sein, die sich auf die Person beziehen, und es wäre sinnvoll, dies zu tun Verwenden Sie stattdessen eine Klasse.

Klassen bieten eine Möglichkeit, einige Daten und verwandte Vorgänge zusammenzuführen. Wenn Sie nur eine Operation für die Daten ausführen und dann eine Funktion verwenden und die Daten als Argument übergeben, erhalten Sie ein äquivalentes Verhalten mit weniger komplexem Code.

Beachten Sie, dass eine Klasse der Art:

class A(object):
    def __init__(self, ...):
        #initialize
    def a_single_method(self, ...):
        #do stuff

ist nicht wirklich eine Klasse, es ist nur eine (komplizierte) Funktion. Eine legitime Klasse sollte immer mindestens zwei Methoden haben (ohne zu zählen __init__).

Bakuriu
quelle
Ich hatte bisher nicht gedacht, dass Klassen um nur eine Methode unnötig kompliziert sind (ich stimme zu, dass dies der Fall ist, ich bin beim Codieren nur manchmal auf Auto-OOPilot eingestellt), sondern habe gerade Pylint entdeckt, das es als Warnung kennzeichnet, wenn Sie weniger haben als zwei (öffentliche) Methoden in Ihrer Klasse. Das macht vollkommen Sinn, wenn ich tatsächlich aufhöre, darüber nachzudenken.
Dwanderson
Wenn Sie in Ihrem Beispiel einen Personendatensatz möchten, aus dem Sie Werte wie Alter ändern und berechnen können, würde ich es vorziehen, einen Personendatensatz (in Python mit dem Namen Tupel) zu verwenden und alle Funktionen zu schreiben, die Sie benötigen.
Ton
7

Klassen (oder vielmehr ihre Instanzen) dienen zur Darstellung von Dingen. Klassen werden verwendet, um die Operationen zu definieren, die von einer bestimmten Klasse von Objekten (ihren Instanzen) unterstützt werden. Wenn Ihre Anwendung den Überblick behalten muss, Personhandelt es sich wahrscheinlich um eine Klasse. Die Instanzen dieser Klasse repräsentieren bestimmte Personen, die Sie verfolgen.

Funktionen dienen zur Berechnung von Dingen. Sie empfangen Eingaben und erzeugen eine Ausgabe und / oder haben Auswirkungen.

Klassen und Funktionen sind keine wirklichen Alternativen, da sie nicht für die gleichen Dinge sind. Es ist nicht wirklich sinnvoll, eine Klasse zu bilden, um "das Alter einer Person anhand ihres Geburtstagsjahres und des aktuellen Jahres zu berechnen". Sie können oder nicht Klassen zu einem der Begriffe darstellen Person, Age, Yearund / oder Birthday. Aber selbst wenn Agees sich um eine Klasse handelt, sollte man nicht daran denken, das Alter einer Person zu berechnen. Vielmehr führt die Berechnung des Alters einer Person zu einer Instanz der AgeKlasse.

Wenn Sie Personen in Ihrer Anwendung modellieren und eine PersonKlasse haben, kann es sinnvoll sein, die Altersberechnung als Methode der PersonKlasse zu verwenden. Eine Methode ist im Grunde eine Funktion, die als Teil einer Klasse definiert ist. Auf diese Weise definieren Sie "die Operationen, die von einer bestimmten Klasse von Objekten unterstützt werden", wie bereits erwähnt.

Sie können also für Ihre Personenklasse eine Methode zur Berechnung des Alters der Person erstellen (wahrscheinlich wird das Geburtstagsjahr aus dem Personenobjekt abgerufen und das aktuelle Jahr als Parameter empfangen). Die Berechnung wird jedoch weiterhin von einer Funktion durchgeführt (nur eine Funktion, die zufällig eine Methode für eine Klasse ist).

Oder Sie können einfach eine eigenständige Funktion erstellen, die Argumente empfängt (entweder ein Personenobjekt, aus dem ein Geburtsjahr abgerufen werden soll, oder einfach das Geburtsjahr selbst). Wie Sie bemerken, ist dies viel einfacher, wenn Sie noch keine Klasse haben, zu der diese Methode natürlich gehört! Sie sollten niemals eine Klasse erstellen, nur um eine Operation durchzuführen. Wenn das alles ist, was die Klasse zu bieten hat, sollte die Operation nur eine eigenständige Funktion sein.

Ben
quelle
1
Klassen können komplexe Berechnungen basierend auf einer Gruppe von zusammenarbeitenden Funktionen modellieren. Wenn Sie eine Vorlage für einen Algorithmus haben, können Sie die Platzhalter als Methoden darstellen. Durch Subtypisierung können Sie einige der Methoden spezialisieren, um eine erweiterbare Implementierungsfamilie zu erhalten. Denken Sie daran, dass polymorphe Klasseninstanzen immer mindestens einige Daten enthalten: eine vtable. Es ist also nicht wahr, dass Klassen für Konzepte und Funktionen für Berechnungen sind.
Memeplex
1
Sie können Personals definieren namedtupleund schreiben, welche Funktionen Sie benötigen.
Ton
7

Ich weiß, dass es ein kontroverses Thema ist, und wahrscheinlich werde ich jetzt verbrannt. aber hier sind meine Gedanken.

Für mich selbst dachte ich, dass es am besten ist, den Unterricht so lange wie möglich zu vermeiden. Wenn ich einen komplexen Datentyp benötige, verwende ich einfache Struktur (C / C ++), Diktat (Python), JSON (js) oder ähnliches, dh keinen Konstruktor, keine Klassenmethoden, keine Überladung von Operatoren, keine Vererbung usw. Bei Verwendung von class, Sie können sich von OOP selbst (Welches Designmuster, was sollte privat sein, bla bla) mitreißen lassen und sich nicht mehr auf das Wesentliche konzentrieren, das Sie zuerst codieren wollten.

Wenn Ihr Projekt groß und chaotisch wird, macht OOP Sinn, da eine Art Systemarchitektur mit Hubschrauberansicht erforderlich ist. "Funktion gegen Klasse" hängt auch von der vor Ihnen liegenden Aufgabe ab.

Funktion

  • Zweck: Daten verarbeiten, Daten bearbeiten, Ergebnismengen erstellen.
  • Verwendungszweck: Codieren Sie immer eine Funktion, wenn Sie dies tun möchten: "y = f (x)"

struct / dict / json / etc (anstelle von class)

  • Zweck: attr./param. speichern, attr./param. pflegen, attr./param. wiederverwenden, attr./param verwenden. später.
  • Verwendungszweck: Wenn Sie sich mit einer Reihe von Attributen / Parametern befassen (vorzugsweise nicht veränderbar)
  • verschiedene Sprachen dasselbe: struct (C / C ++), JSON (js), dict (Python) usw.
  • Ziehen Sie immer einfache struct / dict / json / etc komplizierten Klassen vor (halten Sie es einfach!)

Klasse (wenn es sich um einen neuen Datentyp handelt)

  • Eine einfache Perspektive: ist eine Struktur (C), ein Diktat (Python), ein JSON (JS) usw. mit angehängten Methoden.
  • Die Methode sollte nur in Kombination mit den in der Klasse gespeicherten Daten / Parametern sinnvoll sein.
  • Mein Rat: Codieren Sie niemals komplexe Dinge in Klassenmethoden (rufen Sie stattdessen eine externe Funktion auf).
  • Warnung: Verwenden Sie Klassen nicht als gefälschten Namespace für Funktionen! (das passiert sehr oft!)
  • andere Anwendungsfälle: Wenn Sie viel Operatorüberladung durchführen möchten, verwenden Sie Klassen (z. B. Ihre eigene Matrix- / Vektormultiplikationsklasse).
  • Fragen Sie sich: Ist es wirklich ein neuer „Datentyp“? (Ja => Klasse | Nein => können Sie die Verwendung einer Klasse vermeiden)

Array / Vektor / Liste (um viele Daten zu speichern)

  • Zweck: Speichern Sie viele homogene Daten desselben Datentyps, z. B. Zeitreihen
  • Tipp Nr. 1: Verwenden Sie einfach das, was Ihre Programmiersprache bereits hat. erfinden Sie es nicht neu
  • Hinweis Nr. 2: Wenn Sie wirklich Ihre "Klasse mysupercooldatacontainer" wollen, überladen Sie eine vorhandene Array / vector / list / etc-Klasse (z. B. "Klasse mycontainer: public std :: vector ...").

Aufzählung (Aufzählungsklasse)

  • Ich erwähne es nur
  • Hinweis Nr. 1: Verwenden Sie Enum plus Switch-Case anstelle von überkomplizierten OOP-Entwurfsmustern
  • Hinweis Nr. 2: Verwenden Sie Finite-State-Maschinen
ulf
quelle
außer , dass Namespacing ist wichtig. Durch funktionale Schließungen können Namespaces die meiste Zeit privatisiert werden, jedoch nicht immer. Die Hauptanwendung von Klassen ist die Unterstützung von kollektiven (namespace-zentrierten) Vorlagen, Erweiterungen und DRY-gesteuertem Polymorphismus.
Cowbert
3

Bevor Sie Ihre Frage beantworten:

Wenn Sie keine PersonKlasse haben, müssen Sie zunächst überlegen, ob Sie eine PersonKlasse erstellen möchten . Planen Sie, das Konzept eines Personsehr oft wiederzuverwenden ? In diesem Fall sollten Sie eine PersonKlasse erstellen . (Sie haben Zugriff auf diese Daten in Form einer übergebenen Variablen und es ist Ihnen egal, ob Sie chaotisch oder schlampig sind.)

Zur Beantwortung Ihrer Frage:

Sie haben Zugriff auf ihr Geburtsjahr. In diesem Fall haben Sie wahrscheinlich eine PersonKlasse mit einem someperson.birthdateFeld. In diesem Fall müssen Sie sich fragen, ob someperson.ageein Wert wiederverwendbar ist.

Die Antwort ist ja. Das Alter ist uns oft wichtiger als das Geburtsdatum. Wenn das Geburtsdatum also ein Feld ist, sollte das Alter definitiv ein abgeleitetes Feld sein. (Ein Fall, in dem wir dies nicht tun würden: Wenn wir Werte wie someperson.chanceIsFemaleoder someperson.positionToDisplayInGridandere irrelevante Werte berechnen würden, würden wir die PersonKlasse nicht erweitern . Sie fragen sich nur: "Würde sich ein anderes Programm um die Felder kümmern, mit denen ich die Klasse erweitern möchte? ? "Die Antwort auf diese Frage bestimmt, ob Sie die ursprüngliche Klasse erweitern oder eine Funktion (oder eine eigene Klasse wie PersonAnalysisDataoder so) erstellen.)

Ninjagecko
quelle
Angenommen, Sie möchten einen Personentyp. Warum sollten Sie eine "Klasse" gegen nur einen Datensatztyp oder ein definiertes benanntes Tupel verwenden? OOP basiert auf Klassen, die Leute sind daran gewöhnt, aber stilistisch bevorzugen viele einfachere Typen.
Ton
2

Erstelle niemals Klassen. Zumindest die OOP-Klassen in Python werden diskutiert.

Betrachten Sie diese vereinfachende Klasse:

class Person(object):
    def __init__(self, id, name, city, account_balance):
        self.id = id
        self.name = name
        self.city = city
        self.account_balance = account_balance

    def adjust_balance(self, offset):
        self.account_balance += offset


if __name__ == "__main__":
    p = Person(123, "bob", "boston", 100.0)
    p.adjust_balance(50.0)
    print("done!: {}".format(p.__dict__))

gegen diese NamedTuple-Version:

from collections import namedtuple

Person = namedtuple("Person", ["id", "name", "city", "account_balance"])


def adjust_balance(person, offset):
    return person._replace(account_balance=person.account_balance + offset)


if __name__ == "__main__":
    p = Person(123, "bob", "boston", 100.0)
    p = adjust_balance(p, 50.0)
    print("done!: {}".format(p))

Der Namedtuple-Ansatz ist besser, weil:

  • Namedtuples haben eine präzisere Syntax und Standardverwendung.
  • Um den vorhandenen Code zu verstehen, sind Namedtuples grundsätzlich mühelos zu verstehen. Klassen sind komplexer. Und Klassen können für Menschen sehr komplex werden.
  • Namedtuples sind unveränderlich. Das Verwalten des veränderlichen Status erhöht die Komplexität.
  • Klasse inheritancefügt Komplexität hinzu und verbirgt Komplexität.

Ich sehe keinen einzigen Vorteil bei der Verwendung von OOP-Klassen. Wenn Sie an OOP gewöhnt sind oder mit Code arbeiten müssen, für den Klassen wie Django erforderlich sind.

Übrigens haben die meisten anderen Sprachen einige Datensatztypen wie namedtuples. Scala hat zum Beispiel Fallklassen. Diese Logik gilt dort gleichermaßen.

Lehm
quelle
5
Downvoting. Der namedtupleAnsatz, den Sie befürworten, scheint keine Methodenkapselung oder Verstecken von Daten zu bieten (es sei denn, ich vermisse etwas). Dies sind zwei Hauptvorteile eines echten OO-Ansatzes: Sie können Daten zusammen mit den Funktionen, die darauf einwirken, verpacken und den privaten Status in einem Objekt beibehalten. Wenn Sie das nicht tun, erfinden Sie OOP nur schlecht neu, und es ist schwieriger, mit dem Code zu arbeiten, nicht einfacher.
Marnen Laibow-Koser
1

Ich werde mich in diesem Fall von der Herde lösen und einen alternativen Standpunkt vertreten:

Erstelle niemals Klassen.

Die Abhängigkeit von Klassen führt in erheblichem Maße dazu, dass Codierer aufgeblähten und langsamen Code erstellen. Klassen, die herumgereicht werden (da sie Objekte sind), benötigen viel mehr Rechenleistung als das Aufrufen einer Funktion und das Übergeben von ein oder zwei Zeichenfolgen. Richtige Namenskonventionen für Funktionen können so ziemlich alles, was das Erstellen einer Klasse kann, und das mit nur einem Bruchteil des Overheads und einer besseren Lesbarkeit des Codes.

Das heißt aber nicht, dass Sie nicht lernen sollten, Klassen zu verstehen. Wenn Sie mit anderen programmieren, werden diese ständig verwendet, und Sie müssen wissen, wie Sie diese Klassen unter einen Hut bringen. Wenn Sie Ihren Code so schreiben, dass er sich auf Funktionen stützt, ist der Code kleiner, schneller und besser lesbar. Ich habe riesige Sites gesehen, die nur mit Funktionen geschrieben wurden, die schnell und schnell waren, und ich habe winzige Sites gesehen, die nur minimale Funktionen hatten, die stark auf Klassen beruhten und ständig kaputt gingen. (Wenn Sie Klassen haben, die Klassen erweitern, die Klassen als Teil ihrer Klassen enthalten, wissen Sie, dass Sie den Anschein einer einfachen Wartbarkeit verloren haben.)

Wenn es darauf ankommt, können alle Daten, die Sie übergeben möchten, problemlos von den vorhandenen Datentypen verarbeitet werden.

Klassen wurden als mentale Krücke erstellt und bieten keine wirklichen zusätzlichen Funktionen, und der übermäßig komplizierte Code, den sie tendenziell erzeugen, besiegt auf lange Sicht den Punkt dieser Krücke.

Liljoshu
quelle
Kommentare sind nicht für eine ausführliche Diskussion gedacht. Dieses Gespräch wurde in den Chat verschoben .
Meagar