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))
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.
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.
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.
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.
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.
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. ** ** **
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.
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:
...
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 α
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
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.
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.
Implementieren Sie die Methode mit nur einer Ausdrucks- oder Rückgabeanweisung.
(Wert 1, Wert 2) [Bedingung]
ersetzt werden, aber hier möchte ich die Tatsache ausnutzen, dass True / False jeweils 1/0 sind. ..[^ 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'))
)
...
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.
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)),})
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))
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))
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
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. ** ** **
Listen Sie die Funktionen auf, die in Zukunft erweitert werden können.
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