Es ist Halloween, also werde ich versuchen, es mit Python zu verstecken

Vorwort

Die dritte Halloween-Geschichte.


Angenommen, Sie haben diesen Code. Es ist ein allgemeiner Fizzbuzz-Code.

Fizzbuzz ohne Veränderung


def fizzbuzz(n):
    if n % 15 == 0:
        return 'fizzbuzz'
    if n % 3 == 0:
        return 'fizz'
    if n % 5 == 0:
        return 'buzz'
    
    return str(n)


for i in range(20):
    print(fizzbuzz(i+1))
** Ausführungsergebnis ** (Zum Öffnen klicken)
1
2
fizz
4
buzz
fizz
7
8
fizz
buzz
11
fizz
13
14
fizzbuzz
16
17
fizz
19
buzz

Allerdings sieht "i + 1" von "fizzbuzz (i + 1)" hässlich aus.

Soll ich nicht die Argumente lesen und raten, da Sie in der for-Anweisung stecken bleiben? [^ 1] Ich möchte, dass es genauso funktioniert, auch wenn ich es wie folgt nenne.

for _ in range(20):
    print(fizzbuzz())

Fordern Sie das eigentliche Argument heraus, um es zu verbergen.

[^ 1]: Ich glaube nicht.

Endprodukt dieses Artikels

Ich habe so etwas gemacht.

Endprodukt


#↓ Mach dir von hier aus keine Sorgen um das Recht
def fizzbuzz(n=(lambda mi: (__import__('sys').setprofile(mi.hook), mi)[-1])(type('MyInt', (object, ), {'__init__': (lambda self, n: setattr(self, '_n', n)), 'hook': (lambda self, frame, event, _: setattr(self, '_n', self._n + ((frame.f_code.co_name, event) == ('fizzbuzz', 'return')))), '__mod__': (lambda self, other: self._n % other), '__str__': (lambda self: str(self._n)),})(1))):
    if n % 15 == 0:
        return 'fizzbuzz'
    if n % 3 == 0:
        return 'fizz'
    if n % 5 == 0:
        return 'buzz'

    return str(n)


for _ in range(20):
    print(fizzbuzz())

** Ausführungsergebnis ** ⇒ Wandbox

Natürlich kann es normal ausgeführt werden, und das Ausführungsergebnis ist das gleiche wie vor der Änderung. Dieser Artikel erklärt, wie dieser Code vervollständigt wurde.

Machen wir es: Zunächst einmal ehrlich

Schritt 0: Bestätigen Sie die Richtlinie

Wenn Sie nur die tatsächlichen Argumente weglassen, gibt es viele Möglichkeiten, dies zu tun. In diesem Abschnitt möchte ich die Ziele klarstellen, indem ich jede einfache Lösung und ihre Schwierigkeiten (oder schwierige Gewohnheiten) aufschreibe.

Lösung mit Modulvariablen

default_n = 1

def fizzbuzz(n=None):
    global default_n
    if n is None:
        n = default_n
        default_n += 1

    if n % 15 == 0:
    ...

Es ist eine schlechte globale Deklaration aufgetreten, und die Implementierung der Funktion muss geändert werden. Das kommt gar nicht in Frage.

Lösung mit Verschluss

def make_fizzbuzz(n_start=1):
    default_n = n_start
    
    def _fizzbuzz(n=None):
        nonlocal default_n
        if n is None:
            n = default_n
            default_n += 1
            
        if n % 15 == 0:
        ...  
    
    return _fizzbuzz


fizzbuzz = make_fizzbuzz()

Es gibt einige Vorteile gegenüber der Methode mit der vorherigen Modulvariablen.

Die Implementierung der Fizzbuzz-Funktion muss jedoch erheblich geändert werden.

Lösung mit Klasse

class CountedUpInt:
    def __init__(self, start_n=1):
        self._n = start_n
        
    def __int__(self):
        n = self._n
        self._n += 1
        
        return n

    
def fizzbuzz(n=CountedUpInt()):
    n = int(n)

    if n % 15 == 0:
    ...

Eine einfache Lösung. Leicht zu verstehen [^ 2] und leicht zu reparieren.

[^ 2]: Sie müssen jedoch wissen, wie Sie mit den Standardargumenten von Python umgehen. Der Standardwert ** wird nur einmal ausgewertet, wenn die Funktion definiert ist. Nun, selbst mit dem Standardausdruck ** können Sie ähnlichen Code mithilfe von Klassenvariablen erstellen.

Selbst mit dieser Methode muss jedoch das Innere der Implementierung der Fizzbuzz-Funktion geändert werden. ** Das Ziel ist es, zu erkennen, dass Gott sich versteckt, indem Sie die Standardargumente entwickeln, ohne den Codeteil der Fizzbuzz-Funktion zu berühren. ** ** **

Lösung / Modifikation mit Verschluss

Wenn Sie Funktionen höherer Ordnung gut nutzen, müssen Sie die Implementierung der Fizzbuzz-Funktion nicht berühren. Die Lesbarkeit wird auch verbessert, wenn ein Dekorateur zusammen verwendet wird.

def countup_wrapper(n_start):
    default_n = n_start
    
    def _inner1(func):
        def _inner2(n=None):
            nonlocal default_n
            
            if n is None:
                n = default_n
                default_n += 1
                
            return func(n)
        return _inner2
    return _inner1


@countup_wrapper(1)
def fizzbuzz(n):
    ...

Ich habe es mir ausgedacht, nachdem ich die meisten Artikel fertig geschrieben hatte. ~~ ** Ist das in Ordnung? ** ~~ Da der Artikel, den ich geschrieben habe, verschwendet wird, möchte ich, dass Sie die nichtlokale Allergie als Schutzschild nehmen und ignorieren.

Es ist eine Bindung, die nur das Standardargument ändert.

Schritt 1: Erstellen Sie ein Objekt, das sich wie eine Zahl verhält

Solange Sie den Codeteil nicht berühren, muss der Standardwert wie ein numerischer Wert behandelt werden können. Um beispielsweise die Überschussberechnung zu realisieren, sollte die Klasse wie folgt organisiert werden. [^ 3]

[^ 3]: Wenn Sie das MyInt-Objekt in den richtigen Operanden der Restberechnung einfügen möchten, müssen Sie auch die Methode __rmod__ implementieren.

class MyInt:
    def __init__(self, n):
         self._n = n

    def __mod__(self, other):
         return self._n % other

    def __str__(self):
         return str(self._n)


print(16 % 6)             # => 4
print(MyInt(16) % 6)      # => 4

__mod__ ist eine spezielle Methode, die die Restberechnung übernimmt, und a% b entsprichta .__ mod__ (b). ** Referenz **: [Python-Sprachreferenz »3. Datenmodell» 3.3.8. Numerische Typen emulieren](https://docs.python.org/ja/3/reference/datamodel.html#emulating- numerische Typen)

An dieser Stelle können Sie Code wie folgt schreiben: Da der Aufzählvorgang jedoch nicht implementiert wurde, wird natürlich wiederholt dieselbe Anzahl bestimmt.

def fizzbuzz(n=MyInt(1)):
    if n % 15 == 0:
    ...

Schritt 2: Überlegen Sie, wie Sie zum richtigen Zeitpunkt hochzählen können

Wenn Sie nicht an der Implementierung der Fizzbuzz-Funktion beteiligt sind, haben Sie keine andere Wahl, als den Aufruf / das Escape der Funktion zu verknüpfen. Lassen Sie uns hier den Hook mithilfe der Funktion sys.setprofile realisieren. ~~ Missbrauch ~~ ** Umleitung **. Ich denke.

class MyInt:
    ...
    def succ(self):
        self._n += 1
            
    ...
 

import sys

myInt = MyInt(1)
def _hook(frame, event, arg):
    # frame.f_code.co_name Funktionsname für das Ereignis
    # event                  'call'Oder'return'
    if (frame.f_code.co_name, event) == ('fizzbuzz', 'return'):
        myInt.succ()        

sys.setprofile(_hook)


#
def fizzbuzz(n=myInt):
    if n % 15 == 0:
    ...

** Referenz **: Python-Standardbibliothek »inspizieren - Typen und Mitglieder

Zu diesem Zeitpunkt wurde der oben erwähnte Zweck "ohne den Codeteil der Fizzbuzz-Funktion zu berühren" bereits erreicht.

Lieferbar α: Einfache (?) Implementierung

Lieferbar α


class MyInt:
    def __init__(self, n):
         self._n = n

    def succ(self):
        self._n += 1
            
    def __mod__(self, other):
         return self._n % other

    def __str__(self):
         return str(self._n)        
 

import sys

myInt = MyInt(1)
def _hook(frame, event, arg):
    if (frame.f_code.co_name, event) == ('fizzbuzz', 'return'):
        myInt.succ()        

sys.setprofile(_hook)


#
def fizzbuzz(n=myInt):
    if n % 15 == 0:
        return 'fizzbuzz'
    if n % 3 == 0:
        return 'fizz'
    if n % 5 == 0:
        return 'buzz'
    
    return str(n)


for _ in range(20):
    print(fizzbuzz())

** Ausführungsergebnis ** ⇒ Wandbox

Machen wir es: Reduzieren Sie die Anzahl der Zeilen

Es gibt jedoch zu viele andere Beschreibungen als die Fizzbuzz-Funktion im vorherigen Produkt α. Um zu betonen, dass Fizzbuzz der Hauptzweck ist, ** werde ich es mit so wenig Zeilen wie möglich schreiben ** [^ 4].

[^ 4]: Sag nicht, dass der Kunststil eng ist. Die Figur ist zu sternenklar und verletzt.

Schritt 1: Setzen Sie die Objekte zusammen

Um die spätere Beschreibung so einfach wie möglich zu gestalten, werde ich die Objekte einigermaßen zusammenfassen. Beispielsweise kann die Funktion _hook als Methode der MyInt-Klasse integriert werden.

class MyInt:
    ...    
    def hook(self, frame, event, arg):
        if (frame.f_code.co_name, event) == ('fizzbuzz', 'return'):
            self._n += 1

myInt = MyInt(1)
sys.setprofile(myInt.hook)

Die succ-Methode wurde zur Vereinfachung einer späteren Änderung entfernt.

Schritt 2: Einzeilige MyInt-Klasse

Schritt 2.1: Einzeilige Verarbeitung innerhalb der Methode

Implementieren Sie die Methode mit nur einer Ausdrucks- oder Rückgabeanweisung.

[^ 5]: In Python 3.8 wurde der Zuweisungsoperator (allgemein als Seiuchi-Operator bekannt) eingeführt, aber der Kontext, in dem er verwendet werden kann, scheint begrenzt zu sein. [^ 6]: Ich möchte zustandslose Verarbeitung schreiben, ohne sie so oft wie möglich zu verwenden. Ist das Gefühl zu verlieren, wenn ich setattr einfach nur für mich benutze?

class MyInt:
    def __init__(self, n):
        setattr(self, '_n', n)
    
    def hook(self, frame, event, arg):
        setattr(self, '_n',
            #Selbst, wenn die Bedingung wahr ist._n +Entspricht 1, selbst wenn falsch._n +Entspricht 0.
            self._n + ((frame.f_code.co_name, event) == ('fizzbuzz', 'return'))
        )
    ...

Schritt 2.2: Beseitigen Sie Funktionsdefinitionsanweisungen

Definieren Sie jede Methode mit einem Lambda-Ausdruck neu.

class MyInt:
    __init__ = (lambda self, n:
        setattr(self, '_n', n)
    )
    hook = (lambda self, frame, event, _:
        setattr(
            self, '_n',
            self._n + ((frame.f_code.co_name, event) == ('fizzbuzz', 'return'))
        )
    )
    __mod__ = (lambda self, other:
        self._n % other
    )
    __str__ = (lambda self: 
        str(self._n)
    )

Da eine Methode schließlich eine Klassenvariable ist, ist ihre Ersetzung überraschend einfach.

Schritt 2.3: Beseitigen Sie Klassendefinitionsanweisungen

Mit der Typfunktion können Sie ein Klassenobjekt in einem Ausdruck erstellen. Klassenattribute werden zusammen in einem Wörterbuch übergeben.

MyInt = type('MyInt', (object, ), {
    '__init__': (lambda self, n: 
        setattr(self, '_n', n)
    ), 
    'hook': (lambda self, frame, event, _:
        setattr(
            self, '_n',
            self._n + ((frame.f_code.co_name, event) == ('fizzbuzz', 'return'))
        )
    ), 
    '__mod__': (lambda self, other: 
        self._n % other
    ), 
    '__str__': (lambda self: 
        str(self._n)
    ),
})

Zu diesem Zeitpunkt ist die Klassendefinition einzeilig.

MyInt = type('MyInt', (object, ), {'__init__': (lambda self, n: setattr(self, '_n', n)), 'hook': (lambda self, frame, event, _: setattr(self, '_n', self._n + ((frame.f_code.co_name, event) == ('fizzbuzz', 'return')))), '__mod__': (lambda self, other: self._n % other), '__str__': (lambda self: str(self._n)),})

Schritt 3: Einzeiliges sys.setprofile (~)

Sie können die Funktion __import__ anstelle der import-Anweisung verwenden und schreiben:

__import__('sys').setprofile(MyInt(1).hook)

Tatsächlich ist dieser Code unvollständig. Die von mir erstellte MyInt-Instanz wurde gelöscht und kann nicht mehr wiederhergestellt werden [^ 7].

[^ 7]: Genau genommen ist es eine Lüge. Das Sammeln mit myInt = sys.getprofile () .__ self__ ist jedoch ärgerlich.

Es ist üblich, die Lambda-Formel wie folgt gut zu verwenden.

myInt = (lambda mi:
    #Erstellen Sie einen Taple und geben Sie das letzte Element zurück.
    (__import__('sys').setprofile(mi.hook), mi)[-1]
#Erstellen Sie ein Objekt nur einmal.
)(MyInt(1))

Schritt 4: Eine Zeile für das Ganze

Betten Sie einfach das vorherige Typobjekt direkt in den MyInt-Teil ein.

myInt = (lambda mi:
    (__import__('sys').setprofile(mi.hook), mi)[-1]
)(type('MyInt', (object, ), {'__init__': (lambda self, n: setattr(self, '_n', n)), 'hook': (lambda self, frame, event, _: setattr(self, '_n', self._n + ((frame.f_code.co_name, event) == ('fizzbuzz', 'return')))), '__mod__': (lambda self, other: self._n % other), '__str__': (lambda self: str(self._n)),})(1))

Artefakt β: Implementierung ohne zusätzliche Zeilen

Verwenden Sie das obige Objekt für den Standardwert myInt des Arguments n, um das oben erwähnte Endprodukt zu erhalten.

Lieferbares β(Endprodukt)


def fizzbuzz(n=(lambda mi: (__import__('sys').setprofile(mi.hook), mi)[-1])(type('MyInt', (object, ), {'__init__': (lambda self, n: setattr(self, '_n', n)), 'hook': (lambda self, frame, event, _: setattr(self, '_n', self._n + ((frame.f_code.co_name, event) == ('fizzbuzz', 'return')))), '__mod__': (lambda self, other: self._n % other), '__str__': (lambda self: str(self._n)),})(1))):
    if n % 15 == 0:
        return 'fizzbuzz'
    if n % 3 == 0:
        return 'fizz'
    if n % 5 == 0:
        return 'buzz'

    return str(n)


for _ in range(20):
    print(fizzbuzz())

** Ausführungsergebnis ** ⇒ Wandbox

Zusammenfassung

Wie oben erwähnt, konnte ich den Code ohne die tatsächlichen Argumente schreiben. Wenn Sie beim Schreiben von Code die offensichtlichen Argumente weglassen können ... Möchten Sie dies nicht auf irgendeine Weise tun?

** Ich glaube nicht. ** ** **

Erweiterungstipps

Listen Sie die Funktionen auf, die in Zukunft erweitert werden können.

  1. ** Anzahl zurücksetzen **
  2. ** Wenn Sie ein Argument explizit übergeben, lassen Sie den Standardwert folgen **
  3. ** Stellen Sie sicher, dass sich das Ändern des Funktionsnamens nicht auf den Standardwert auswirkt **

Bild der Erweiterung 1


print(fizzbuzz())   # => 1
print(fizzbuzz())   # => 2
print(fizzbuzz())   # => fizz

fizzbuzz.reset(10)

print(fizzbuzz())   # => buzz
print(fizzbuzz())   # => 11

Bild der Erweiterung 2


print(fizzbuzz())   # => 1
print(fizzbuzz())   # => 2
print(fizzbuzz())   # => fizz
print(fizzbuzz(10)) # => buzz
print(fizzbuzz())   # => 11

Die vorübergehende Implementierung von Erweiterung 3 wurde zu diesem Zeitpunkt abgeschlossen. Ich interessiere mich auch für 1 und 2, also werde ich bald darüber schreiben. Ich weiß nicht, ob es ein Artikel sein wird.

Recommended Posts

Es ist Halloween, also werde ich versuchen, es mit Python zu verstecken
Versuchen Sie, Facebook mit Python zu betreiben
Da es Weihnachten ist, werde ich versuchen, die Genealogie Jesu Christi mit Cabocha zu zeichnen
Es ist mühsam, "Kodierung: utf-8" in Python zu schreiben, also werde ich etwas mit Shellscript machen
Versuchen Sie, sich mit Python bei qiita anzumelden
3,14 π Tag, versuchen Sie also, in Python auszugeben
Probieren Sie es mit Word Cloud Japanese Python JupyterLab.
Versuchen Sie, das Mensch-Maschine-Diagramm mit Python zu lösen
Versuchen Sie, mit Python eine Lebenskurve zu zeichnen
Versuchen Sie, in Python einen "Entschlüsselungs" -Code zu erstellen
Versuchen Sie, Python-Dokumente automatisch mit Sphinx zu generieren
Versuchen Sie, mit Python eine Diedergruppe zu bilden
Versuchen Sie, Fische mit Python + OpenCV2.4 (unvollendet) zu erkennen.
Ich konnte AWS-Batch mit Python, Moto verspotten, also werde ich es verlassen
Versuchen Sie es mit Python.
Versuchen Sie, das Programmier-Herausforderungsbuch mit Python3 zu lösen
Lassen Sie uns ein Befehls-Standby-Tool mit Python erstellen
Installieren Sie Selenium auf Ihrem Mac und probieren Sie es mit Python aus
Probieren Sie die DB-Operation mit Python aus und visualisieren Sie sie mit d3
Es ist zu mühsam, Japanisch in Vims Python3 anzuzeigen.
[Memorandum] python + vscode + pipenv Es ist üblich, aber es war ein Durcheinander mit Warnungen, also ein Memorandum
Versuchen Sie, den Boden durch Rekursion herauszufordern
Versuchen Sie es mit GUI, PyQt in Python
Stellen Sie mit Python eine Verbindung zu BigQuery her
Immerhin ist es falsch, mit Python-Subprozess zu katzen.
Versuchen Sie, Python selbst zu verstehen
Wenn ich versuche, mit Heroku zu pushen, funktioniert es nicht
Probieren Sie die Python-Ausgabe mit Haxe 3.2 aus
Stellen Sie mit Python eine Verbindung zu Wikipedia her
[AWS] Versuchen Sie, die Python-Bibliothek mit SAM + Lambda (Python) zur Ebene hinzuzufügen.
Post to Slack mit Python 3
Versuchen Sie, ein Unterfenster mit PyQt5 und Python zu öffnen
Versuchen Sie, den Betrieb von Netzwerkgeräten mit Python zu automatisieren
Versuchen Sie, Python mit Try Jupyter auszuführen
Schalten Sie Python mit Alternativen auf 2.7 um
Schreiben Sie mit Python in csv
Versuchen Sie die Gesichtserkennung mit Python
Versuchen Sie, die verstümmelten Zeichen im angehängten Dateinamen mit Python zu entschlüsseln
Aktiviert, um PNG mit Pillow of Python in JPG zu konvertieren
Schreiben Sie Python nicht, wenn Sie es mit Python beschleunigen möchten
Personen, die mit Android-Programmen vertraut sind, versuchen die Multithread-Verarbeitung mit Python
Versuchen Sie, eine Python-Umgebung mit Visual Studio Code & WSL zu erstellen
Versuchen Sie, mit Python3 eine Zeichenfolge aus einem Bild zu extrahieren
Versuchen Sie, Google Map und Geography Map mit Python anzuzeigen
Versuchen Sie, den kürzesten Weg mit Python + NetworkX + Social Data zu lösen
Versuchen Sie, CloudWatch-Metriken mit der Python-Datenquelle re: dash abzurufen
Versuchen Sie, Ihrer IFC-Datei mit IfcOpenShell Python eine Wand hinzuzufügen
[Python] Versuchen Sie, Zeichen aus Bildern mit OpenCV und pyocr zu erkennen
Versuchen Sie es mit Python + Beautiful Soup
Python: So verwenden Sie Async mit
Link, um mit Python zu beginnen
[Python] Mit Python in eine CSV-Datei schreiben
Schön dich mit Python zu treffen
Versuchen Sie die Singularwertzerlegung mit Python
Ausgabe in eine CSV-Datei mit Python
Versuchen Sie, sich mit ONNX Runtime zu profilieren
Konvertieren Sie die Liste mit Python in DataFrame