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.
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.
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]: ...
"** 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
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)
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)
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 **.