Frage

Ich versuche, ein Einfrieren Dekorateur für Python zu schreiben.

Die Idee ist wie folgt:

(Als Reaktion auf die beiden Kommentare)

Ich könnte falsch sein, aber ich denke, es gibt zwei hauptsächliche Verwendung von Testfall.

  • Eine davon ist die testgetriebene Entwicklung: Idealerweise schreiben Entwickler Fall, bevor Sie den Code zu schreiben. Es hilft in der Regel die Definition der Architektur, weil diese Disziplin Kräfte, die die realen Schnittstellen vor der Entwicklung zu definieren. Man kann sogar, dass die Person in einigen Fällen überlegen, wer Depeschen Job zwischen dev wird den Testfall schriftlich und verwenden Sie es effizient die Spezifikation illustrieren er im Sinn hat. Ich habe keine Erfahrungen mit der Anwendung von Testfall so.

  • Die zweite ist die Idee, dass alle Projekt mit einem anständigen Größe und ein mehr Programmierer aus gebrochen Code leiden. Etwas, das zur Arbeit kann von einem Wechsel kaputt gehen das sah wie ein unschuldiges Refactoring. Obwohl gute Architektur, kann lose Kopplung zwischen Komponente Hilfe gegen dieses Phänomen zu bekämpfen; Sie schlafen besser in der Nacht, wenn Sie etwas Testfall, um sicherzustellen, geschrieben dass nichts Ihr Programm das Verhalten brechen.

JEDOCH Niemand kann den Aufwand für writting Testfälle verweigern. In dem erster Fall kann man argumentieren, dass der Testfall tatsächlich Führungs Entwicklung und wird daher nicht als Kopf berücksichtigt werden.

Ehrlich gesagt, ich bin ein ziemlich junger Programmierer und wenn ich Sie zu diesem Thema mein Wort ist nicht wirklich wertvoll ... Wie auch immer, ich denke, dass mosts Unternehmen / Projekte, die nicht arbeiten wie das, und das Komponententests sind vor allem in der zweiten verwendet Fall ...

Mit anderen Worten, statt dafür zu sorgen, dass das Programm korrekt funktioniert, will es prüfen, ob es wird die gleiche Arbeit in der Zukunft.

Dies muss ohne die Kosten für das Schreiben von Tests erfüllt werden, durch dieses Einfrieren Dekorateur verwendet wird.

Angenommen, Sie haben eine Funktion

def pow(n,k):
    if n == 0:  return 1
    else:       return n * pow(n,k-1)

Es ist ganz schön, und Sie wollen es als eine optimierte Version neu zu schreiben. Es ist Teil eines großen Projektes. Sie wollen es das gleiche Ergebnis zurück geben, für ein paar Wert. Anstatt den Schmerz der Testfälle durchlaufen, könnte man einige verwenden Art Freeze Dekorateur.

Etwas, dass das erste Mal, wenn der Dekorateur ausgeführt wird, der Dekorateur die Funktion mit den definierten args laufen gelassen (unter 0 und 7) und speichert das Ergebnis in einer Karte (f -> args -> Ergebnis)

@freeze(2,0)
@freeze(1,3)
@freeze(3,5)
@freeze(0,0)
def pow(n,k):
    if n == 0:  return 1
    else:       return n * pow(n,k-1)

Das nächste Mal das Programm ausgeführt wird, lädt der Dekorateur diese Karte und Scheck dass das Ergebnis dieser Funktion für diese Argumente als nicht geändert.

Ich schrieb schon schnell den Dekorateur (siehe unten), aber schaden ein paar Probleme zu was ich brauche Ihre Beratung ...

from __future__ import with_statement
from collections import defaultdict
from types import GeneratorType
import cPickle

def __id_from_function(f):
    return ".".join([f.__module__, f.__name__])

def generator_firsts(g, N=100):
    try:
        if N==0:
            return []
        else:
            return  [g.next()] + generator_firsts(g, N-1)
    except StopIteration :
        return []

def __post_process(v):
    specialized_postprocess = [
        (GeneratorType, generator_firsts),
        (Exception,     str),
    ]
    try:
        val_mro = v.__class__.mro()
        for ( ancestor, specialized ) in specialized_postprocess:
            if ancestor in val_mro:
                return specialized(v)
        raise ""
    except:
        print "Cannot accept this as a value"
        return None

def __eval_function(f):
    def aux(args, kargs):
        try:
            return ( True, __post_process( f(*args, **kargs) ) )
        except Exception, e:
            return ( False, __post_process(e) )
    return aux

def __compare_behavior(f, past_records):
    for (args, kargs, result) in past_records:
        assert __eval_function(f)(args,kargs) == result

def __record_behavior(f, past_records, args, kargs):
    registered_args = [ (a, k) for (a, k, r) in past_records ]
    if (args, kargs) not  in registered_args:
        res = __eval_function(f)(args, kargs)
        past_records.append( (args, kargs, res) )

def __open_frz():
    try:
        with open(".frz", "r") as __open_frz:
            return cPickle.load(__open_frz)
    except:
        return defaultdict(list)

def __save_frz(past_records):
    with open(".frz", "w") as __open_frz:
        return cPickle.dump(past_records, __open_frz)


def freeze_behavior(*args, **kvargs):
    def freeze_decorator(f):
        past_records = __open_frz()
        f_id = __id_from_function(f)
        f_past_records = past_records[f_id]
        __compare_behavior(f, f_past_records)
        __record_behavior(f, f_past_records, args, kvargs)
        __save_frz(past_records)
        return f
    return freeze_decorator
  • Dumping und die Ergebnisse vergleichen, ist für alle Arten nicht trivial. Im Moment denke ich über die Verwendung einer Funktion (ich nenne es nachbearbeiten hier), um dieses Problem zu lösen. Grundsätzlich anstelle des Speicherns res I speichern Nachverarbeitung (res) und I vergleichen Nachverarbeitung (RES1) == Nachverarbeitung (RES2) anstelle von RES1 RES2 zu vergleichen. Es ist wichtig, dem Anwender zu lassen die vordefinierte nachbearbeiten Funktion überlasten. Meine erste Frage ist: Haben Sie eine Art und Weise kennen zu überprüfen, ob ein Objekt dumpable ist oder nicht?

  • einen Schlüssel für die Funktion eingerichtet zu definieren, ist ein Schmerz. In den folgenden Schnipsel Ich bin mit dem Funktionsmodul und seinen Namen. ** Können Sie sich eine intelligentere Art und Weise, das zu tun. **

  • Die Schnipsel unten ist eine Art von Arbeit, sondern öffnen und schließen Sie die Datei beim Testen und bei der Aufnahme. Dies ist nur ein dummer Prototyp ... aber haben Sie eine schöne Art und Weise kennen, die Datei zu öffnen, verarbeiten die Dekorateur für alle Funktion, schließen Sie die Datei ...

  • Ich beabsichtige, einige Funktionalitäten dazu hinzuzufügen. So fügen Sie die possibity definieren ein iterable eine Reihe von Argumenten, Rekord Argumente aus dem wirklichen Gebrauch durchsuchen usw. Warum würden Sie von einem solchen Dekorateur erwarten?

  • In general, würden Sie eine solche Funktion zu verwenden, die Beschränkung zu wissen ... Vor allem wenn man versucht, es mit POO zu benutzen?

War es hilfreich?

Lösung

"In der Regel würden Sie eine solche Funktion zu verwenden, die Beschränkung zu wissen ...?"

Ehrlich gesagt -. Nie

Es gibt keine Umstände, unter denen ich auf diese Weise „einzufrieren“ Ergebnisse einer Funktion würde.

Der Anwendungsfall erscheint auf zwei falschen Ideen basieren: (1), dass Unit-Tests sind entweder schwer oder komplex oder teuer; und (2) könnte es einfacher sein, den Code zu schreiben, „einzufrieren“, um die Ergebnisse und irgendwie die gefrorenen Ergebnisse für Refactoring verwenden. Das ist nicht hilfreich. Tatsächlich macht die sehr reale Möglichkeit des Einfrierens falsche Antworten dies eine schlechte Idee.

Als erstes auf „Konsistenz vs. Korrektheit“. Dies ist einfacher, mit einer einfachen Zuordnung zu erhalten als mit einem komplexen Satz von Dekorateure.

Tun Sie dies stattdessen ein Einfrieren Dekorateur zu schreiben.

print "frozen_f=", dict( (i,f(i)) for i in range(100) )

Das Dictionary-Objekt, das als ein gefrorenes Ergebnismenge wird perfekt funktioniert erstellt wird. Kein Dekorateur. Keine Komplexität zu sprechen.

Zweitens auf "Unit-Tests".

Der Punkt eines Unit-Test ist nicht "einzufrieren" einige zufällige Ergebnisse. Der Punkt eines Unit-Test ist echte Ergebnisse mit den Ergebnissen einer andere (einfachere, offensichtlich, schlecht-performing Art und Weise), entwickelt zu vergleichen. Normalerweise Unit-Tests vergleichen Hand erarbeiteten Ergebnisse. Andere Zeiten Unit-Tests verwenden offensichtlich, aber schrecklich langsam Algorithmen ein paar wichtigen Ergebnisse zu erzielen.

Der Punkt Testdaten um zu haben, ist nicht, dass es ein „eingefroren“ Ergebnis ist. Der Punkt, der Testdaten ist, dass es ein unabhängiges Ergebnis. Geschehen anders - manchmal von verschiedenen Menschen -. Das bestätigt, dass die Funktion arbeitet

Es tut uns leid. Dies scheint mir eine schlechte Idee zu sein; es sieht aus wie es die Absicht der Unit-Tests untergräbt.


"JEDOCH niemand den Overhead writting Testfälle leugnen kann"

Eigentlich würde leugnen viele Leute die „Overhead“. Es ist nicht „über Kopf“ im Sinne der verschwendeten Zeit und Mühe. Für einige von uns sind Unittests unerlässlich. Ohne sie kann der Code arbeiten, aber nur durch Zufall. Mit ihnen haben wir reichlich Beweise dafür, dass es tatsächlich funktioniert; und die spezifischen Fälle, in denen es funktioniert.

Andere Tipps

Suchen Sie Invarianten oder senden Bedingungen zu implementieren?

Sie das Ergebnis explizit angeben, sollte dies wil die meisten Probleme entfernen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top