[Python] Keine Ich habe nullutil.py erstellt, weil es durch Überprüfen und Verzweigen überfüllt war.

Wollten Sie schon immer der Aufgabe entkommen, für immer "Keine" Schecks und Bedingungen zu schreiben?

None in Python scheint in anderen Sprachen null und nil zu entsprechen, aber Python verfügt nicht über Operatoren oder Methoden, die die Handhabung von ** Null erleichtern, was in anderen Sprachen üblich ist ** ..

Da es keine Hilfe dafür gibt, habe ich beschlossen, ** Standard 3 Typen ** durch Schreiben von ** Funktionen ** zu ersetzen.

Inhalt

nullutil.py


# Null Coalesce
# This means:
#     lhs ?? rhs
def qq(lhs, rhs):
    return lhs if lhs is not None else rhs


# Safty Access
# This means:
#     instance?.member
#     instance?.member(*params)
def q_(instance, member, params=None):
    if instance is None:
        return None
    else:
        m = getattr(instance, member)
        if params is None:
            return m
        elif isinstance(params, dict):
            return m(**params)
        elif isinstance(params, list) or isinstance(params, tuple):
            return m(*params)
        else:
            return m(params)
# This means:
#     instance?[index]
def qL7(collection, index):
    return collection[index] if collection is not None else None


# Safety Evalate (do Syntax)
# This means:
#     params?.let{expression}
#     do
#         p0 <- params[0]
#         p1 <- params[1]
#         ...
#         return expression(p0, p1, ...)
def q_let(params, expression):
    if isinstance(params, dict):
        for param in params.values():
            if param is None:
                return None
        return expression(**params)
    elif isinstance(params, list) or isinstance(params, tuple):
        for param in params:
            if param is None:
                return None
        return expression(*params)
    else:
        return expression(params) if params is not None else None

Ich konnte nicht gut schreiben und verließ mich an vielen Stellen auf "Any", aber ich bereitete auch einen Stub vor.

Stub

nullutil.pyi


from typing import TypeVar, Hashable, Mapping, MutableMapping, Sequence, MutableSequence, Any, Union, Optional, Callable, AnyStr
from typing import overload


T = TypeVar('T')
U = TypeVar('U')
H = TypeVar('H', Hashable)

SeqT = Union[Sequence[T], MutableSequence[T]]
MapT = Union[Mapping[H, T], MutableMapping[H, T]]
C = Union[list, tuple, dict]


# Null Coalesce
# This means:
#     lhs ?? rhs
def qq(lhs: Optional[T], rhs: T) -> T: ...


# Safty Access
# This means:
#     instance?.member
#     instance?.member(*params)
def q_(instance: Optional[Any], member:AnyStr, params: Optional[Any]) -> Optional[Any]: ...
# This means:
#     instance?[index]
@overload
def qL7(collection: Optional[SeqT], index: int) -> Optional[T]: ...
@overload
def qL7(collection: Optional[MapT], index: H) -> Optional[T]: ...

# Safety Evalate (do Syntax)
# This means:
#     params?.let{expression}
#     do
#         p0 <- params[0]
#         p1 <- params[1]
#         ...
#         return expression(p0, p1, ...)
@overload
def q_let(params: Optional[T], expression: Callable[[T], U]) -> Optional[U]: ...
@overload
def q_let(params: Optional[C], expression: Callable[..., T]) -> Optional[T]: ...

Aufführen

Null-Koaleszenz-Operator

"** Ich möchte den Wert einer Variablen abrufen, aber wenn er Null ist, möchte ich einen Standardwert angeben **"

Der ** Null-Koaleszenzoperator ** beantwortet eine solche Anfrage.

In Sprachen, in denen der Null-Koaleszenzoperator verwendet werden kann, kann er wie folgt geschrieben werden.

Für Swift


foo = bar ?? default_value

Für Kotlin


foo = bar ?: default_value

Als Alternative dazu habe ich eine ** qq Funktion ** erstellt.

guide = 'Mirai Hirano'
researcher = 'Kako Nanami'
curator = None

# print(guide ?? 'John Doe')
print(qq(guide, 'John Doe'))

# print(researcher ?? 'John Doe')
print(qq(researcher, 'John Doe'))

# print(curator ?? 'John Doe')
print(qq(curator, 'John Doe'))
Mirai Hirano
Kako Nanami
John Doe

Sicherer Anrufbetreiber

Wenn Sie versuchen, ein Mitglied anzurufen, das möglicherweise Null (Nullable) ist, erhalten Sie eine Ausnahme, wenn es wirklich Null ist.

import numpy as np
import pandas as pd

np.random.seed(365)

score = np.clip(np.rint(np.random.normal(80., 15., 500)).astype(int), 0, 100)
mean = np.mean(score)
std = np.std(score)
mean_difference = score - mean
standard_score = mean_difference * (10. / std) + 50.

column_dict = {'Noten': score, 'Unterschied zum Durchschnitt': mean_difference, 'Abweichungswert': standard_score,}
column_list = ['Noten', 'Unterschied zum Durchschnitt', 'Abweichungswert',]
score_df = pd.DataFrame(column_dict)[column_list]
none_df = None

display(score_df.sort_values('Noten'))
display(none_df.sort_values('Noten'))
Noten Unterschied zum Durchschnitt Abweichungswert
249 34 -45.632 16.784097
82 36 -43.632 18.239913
89 36 -43.632 18.239913
372 41 -38.632 21.879453
112 42 -37.632 22.607361
... ... ... ...
197 100 20.368 64.826033
43 100 20.368 64.826033
337 100 20.368 64.826033
334 100 20.368 64.826033
280 100 20.368 64.826033
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-50-badfe23fbcf4> in <module>
      1 display(score_df.sort_values('Noten'))
----> 2 display(none_df.sort_values('Noten'))

AttributeError: 'NoneType' object has no attribute 'sort_values'

"Ich möchte ein Mitglied einer ** Nullable-Instanz aufrufen. Wenn es Null ist, kann der Rückgabewert Null sein **"

Der ** Safe Call Operator ** beantwortet eine solche Anfrage.

In Sprachen, in denen sichere Anrufbetreiber verfügbar sind, können Sie schreiben:

Für Swift


foo?.bar()

Für Kotlin


foo?.bar()

Als Alternative dazu habe ich eine ** q_ Funktion ** erstellt.

# display(score_df?.sortvalues('Noten'))
display(q_(score_df,'sort_values','Noten'))

# display(none_df?.sortvalues('Noten'))
display(q_(none_df,'sort_values','Noten'))
Noten Unterschied zum Durchschnitt Abweichungswert
249 34 -45.632 16.784097
82 36 -43.632 18.239913
89 36 -43.632 18.239913
372 41 -38.632 21.879453
112 42 -37.632 22.607361
... ... ... ...
197 100 20.368 64.826033
43 100 20.368 64.826033
337 100 20.368 64.826033
334 100 20.368 64.826033
280 100 20.368 64.826033
None

** Wenn Sie mehrere Argumente angeben **, geben Sie diese in einer Liste, einem Taple oder einem Wörterbuch an **.

# score_df?.sort_values(by='Abweichungswert', ascending=False)
q_(score_df, 'sort_values', {'by': 'Abweichungswert', 'ascending': False})

Wenn Sie ** Feld ** anstelle von Methode aufrufen, ** lassen Sie ** das dritte Argument weg.

# score_df?.index
q_(score_df, 'index')

Wenn das dritte Argument für die Methode weggelassen wird, wird einfach ein Funktionsobjekt zurückgegeben, das aufgerufen werden kann. Wenn Sie also eine Methode ** ohne Argument ** aufrufen, wird eine leere Liste, ein Tapple oder ein Wörterbuch ** angegeben.

# standard_score?.min()
q_(standard_score, 'min', ())

#Da None nicht aufrufbar ist, kann die folgende Notation Ausnahmen verursachen.
# q_(standard_score, 'min')()

Je nach Sprache existiert auch die Notation ? []. Ich habe auch eine "qL7" -Funktion erstellt, um auf Elemente durch Indizes für Listen, Wörterbücher und Numpy-Tensoren zuzugreifen. ~~ Ich habe den Funktionsnamen der allgemeinen Notation ähnlich gemacht, aber ich habe das Gefühl, dass er bald an seine Grenzen stößt. ~~

# standard_score?[5]
qL7(standard_score, 5)

Sichere Formelbewertung

Ausdrücke, die einen Nullable-Wert als Argument verwenden, können eine Ausnahme auslösen, wenn sie wirklich Null sind.

import numpy as np

sequence = np.arange(0, 10)
none_array = None

print(sequence * 2)
print(none_array * 2)
[ 0  2  4  6  8 10 12 14 16 18]
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-82-44094c5f4f90> in <module>
      1 print(sequence * 2)
----> 2 print(none_array * 2)

TypeError: unsupported operand type(s) for *: 'NoneType' and 'int'

"Ich möchte einen Ausdruck auswerten, der einen ** Nullable-Wert als Argument verwendet. Dieser Ausdruck erwartet Nicht-Null. Wenn er also Null ist, kann der Rückgabewert Null ** sein."

Das ** System zur Bewertung sicherer Ausdrücke ** beantwortet solche Anforderungen.

Im Fall von Swift verfügt eine Nullable-Instanz über eine ** map-Methode **, die sicher ausgewertet werden kann, indem sie geschlossen wird.

Für Swift


foo.map { $0 * 2 }

Im Fall von Kotlin wird dies durch sicheres Aufrufen der ** let -Methode ** der Non-Null-Instanz realisiert.

Für Kotlin


foo?.let { it * 2 }

Als Alternative dazu habe ich eine ** q_let Funktion ** erstellt.

# print(sequence?.let { it * 2 } )
print(q_let(sequence, lambda it: it * 2))

# print(none_array?.let { it * 2 } )
print(q_let(none_array, lambda it: it * 2))
[ 0  2  4  6  8 10 12 14 16 18]
None

Natürlich kann der Lambda-Ausdrucksteil durch eine ** definierte Funktion ** ersetzt werden.

np.random.seed(365)

n01 = np.random.randn(10)

# n01?.let { np.mean(it) }
q_let(n01, np.mean)

Wie Sie vielleicht bemerkt haben, ist die Funktion "q_let" eine Alternative zu der Funktion "q_".

# score_df?.sort_values('Abweichungswert', ascending=False)
# <=> score_df?.let { it.sort_values('Abweichungswert', ascending=False) }
q_let(score_df, lambda it: it.sort_values('Abweichungswert', ascending=False))

Wenn das Positionsargument und das Namensargument nicht zusammen in einer Liste oder einem Wörterbuch angegeben werden können / es schwierig ist, kann stattdessen die Funktion q_let verwendet werden. In diesem Fall ist die Kette jedoch sehr schwer zu schreiben, so dass es in diesem Fall einfacher ist, die Funktion "q_" zu verwenden.

Wenn es mehrere nullfähige Variablen gibt, werden map und let verschachtelt und es wird schwierig. Haskell scheint in der Lage zu sein, dies einfach mit der Notation do zu schreiben. Da die Funktion q_let schließlich eine Funktion ist, habe ich es möglich gemacht, eine Sammlung von Anfang an als Argument ** zu verwenden.

import math

r = 5
pi = math.pi

# r?.let { x -> pi?.let { y -> x**2 * y } }
q_let([r, pi,], lambda x, y: x**2 * y)

Schwächen

Erstens wird die Anzahl der Zeichen zwangsläufig zunehmen, da sie von einer Funktion zwangsweise implementiert wird. Der ?? Operator besteht aus 2 Zeichen, aber qq (,) besteht aus 5 Zeichen. ? . usw. sind aufgrund unnötiger Zitate noch miserabler.

Eine andere Sache ist, dass sie im Gegensatz zum ** Operator nicht eingelegt werden kann **, sodass die ** Kette schrecklich aussieht **.

Unten sehen Sie ein Beispiel für eine Kette in Swift.

foo?.bar()?.baz?.qux() ?? default_value

Es ist sehr erfrischend, aber wenn ich versuche, dasselbe mit der Funktion zu schreiben, die ich dieses Mal erstellt habe, wird es so.

qq(q_(q_(q_(foo,'bar',()),'baz'),'qux',()), default_value)

Es ist keine Kette mehr, sondern eine verschachtelte **, und ich habe keine Ahnung, was sie umgibt. Zu schrecklich. Wenn du hier her kommst

if foo is None:
    ret = default_value
else:
    temp = foo.bar()
    if temp is None:
        ret = default_value
    else:
        temp = temp.baz
        if temp is None:
            ret = default_value
        else:
            temp = temp.qux()
            ret = temp if temp is not None else default_value

Sieht immer noch besser aus.

qq(
    q_(
        q_(
            q_(
                foo, 'bar', ()
            ), 'baz'
        ), 'qux', ()
    ), default_value
)

Wenn Sie es so schreiben, ist es etwas einfacher zu sehen **.

Recommended Posts

[Python] Keine Ich habe nullutil.py erstellt, weil es durch Überprüfen und Verzweigen überfüllt war.
Ich habe versucht, Google Translate aus Python zu verwenden, und es war einfach zu einfach
Pythons lru_cache war langsam (ich habe es untersucht, weil ich es falsch verstanden habe)
Ich habe versucht, LINE BOT mit Python und Heroku zu machen
Ich fragte mich, ob Python 3.4 schneller war, aber es war langsamer
Python: Kann in Lambda wiederholt werden
[Python] Ich habe eine Funktion erstellt, die AES entschlüsselt und entschlüsselt, indem ich sie einfach mit pycrypto geworfen habe.
Ich habe einen Server mit Python-Socket und SSL erstellt und versucht, über den Browser darauf zuzugreifen
[Python] Ich habe das Spiel von pip installiert und versucht zu spielen
[Ich habe es mit Python gemacht] Tool für die Stapelausgabe von XML-Daten
Ich habe Chatbot mit LINE Messaging API und Python erstellt
Ich dachte, ich hätte die pyc-Datei kürzlich nicht gesehen, aber sie wurde von python3 in pycache isoliert
Ich habe es gemacht, weil ich JSON-Daten möchte, die in Demos und Prototypen frei verwendet werden können