Jak odwoływać się do obiektu zerowego w Pythonie?
Jak odwoływać się do obiektu zerowego w Pythonie?
Odpowiedzi:
W Pythonie „zerowym” obiektem jest singleton None
.
Najlepszym sposobem sprawdzenia rzeczy pod kątem „Noneness” jest użycie operatora tożsamości is
:
if foo is None:
...
None
Python jest zerowy?W null
Pythonie nie ma None
. Jak już wspomniano, najdokładniejszym sposobem sprawdzenia, czy coś zostało podane None
jako wartość, jest użycie is
operatora tożsamości, który sprawdza, czy dwie zmienne odnoszą się do tego samego obiektu.
>>> foo is None
True
>>> foo = 'bar'
>>> foo is None
False
None
None
jest jedyną instancją klasy NoneType
i wszelkie dalsze próby utworzenia tej klasy zwrócą ten sam obiekt, co spowoduje None
utworzenie singletonu. Nowi użytkownicy Pythona często widzą komunikaty o błędach, które wspominają NoneType
i zastanawiają się, co to jest. Moim osobistym zdaniem te wiadomości mogą po prostu wspomnieć None
po imieniu, ponieważ, jak zobaczymy wkrótce, None
pozostawia niewiele miejsca na dwuznaczność. Więc jeśli zobaczysz jakieś TypeError
przesłanie, które wspomina, że NoneType
nie może tego zrobić lub nie może tego zrobić, po prostu wiedz, że to po prostu taki, None
który był używany w sposób, którego nie może.
Ponadto None
jest wbudowaną stałą, gdy tylko uruchamiasz Python, można go używać z dowolnego miejsca, czy to w module, klasie czy funkcji. NoneType
w przeciwieństwie do tego nie trzeba, najpierw trzeba by się do niego odwołać, sprawdzając None
jego klasę.
>>> NoneType
NameError: name 'NoneType' is not defined
>>> type(None)
NoneType
Możesz sprawdzić None
unikalność za pomocą funkcji tożsamości Pythona id()
. Zwraca unikalny numer przypisany do obiektu, każdy obiekt ma jeden. Jeśli identyfikator dwóch zmiennych jest taki sam, to w rzeczywistości wskazują na ten sam obiekt.
>>> NoneType = type(None)
>>> id(None)
10748000
>>> my_none = NoneType()
>>> id(my_none)
10748000
>>> another_none = NoneType()
>>> id(another_none)
10748000
>>> def function_that_does_nothing(): pass
>>> return_value = function_that_does_nothing()
>>> id(return_value)
10748000
None
nie można zastąpićW dużo starszej wersji Pythona (wcześniejszej niż 2.4) możliwe było ponowne przypisanie None
, ale już nie. Nawet jako atrybut klasy lub w ramach funkcji.
# In Python 2.7
>>> class SomeClass(object):
... def my_fnc(self):
... self.None = 'foo'
SyntaxError: cannot assign to None
>>> def my_fnc():
None = 'foo'
SyntaxError: cannot assign to None
# In Python 3.5
>>> class SomeClass:
... def my_fnc(self):
... self.None = 'foo'
SyntaxError: invalid syntax
>>> def my_fnc():
None = 'foo'
SyntaxError: cannot assign to keyword
Dlatego można bezpiecznie założyć, że wszystkie None
odniesienia są takie same. Nie ma „niestandardowego” None
.
None
użycie is
operatoraPodczas pisania kodu możesz ulec pokusie przetestowania Noneness w następujący sposób:
if value==None:
pass
Lub przetestować pod kątem fałszu w ten sposób
if not value:
pass
Musisz zrozumieć konsekwencje i dlaczego często dobrym pomysłem jest mówienie wprost.
None
czemu to robić
value is None
zamiast
value==None
Pierwszy jest równoważny z:
id(value)==id(None)
Podczas gdy wyrażenie value==None
jest w rzeczywistości stosowane w ten sposób
value.__eq__(None)
jeśli wartość jest naprawdę None
, otrzymasz to, czego się spodziewałeś.
>>> nothing = function_that_does_nothing()
>>> nothing.__eq__(None)
True
W najczęstszych przypadkach wynik będzie taki sam, ale __eq__()
metoda otwiera drzwi, które unieważniają jakąkolwiek gwarancję dokładności, ponieważ można ją zastąpić w klasie, aby zapewnić specjalne zachowanie.
Rozważ tę klasę.
>>> class Empty(object):
... def __eq__(self, other):
... return not other
Więc wypróbuj None
i działa
>>> empty = Empty()
>>> empty==None
True
Ale potem działa również na pusty ciąg
>>> empty==''
True
I jeszcze
>>> ''==None
False
>>> empty is None
False
None
jako wartości logicznejNastępne dwa testy
if value:
# do something
if not value:
# do something
są w rzeczywistości oceniane jako
if bool(value):
# do something
if not bool(value):
# do something
None
jest „falsey”, co oznacza, że jeśli zostanie ustawiony na wartość logiczną, zwróci False
i jeśli zastosuje not
operator, zwróci True
. Pamiętaj jednak, że nie jest to właściwość unikalna dla None
. Oprócz False
siebie właściwość jest wspólna dla pustych list, krotek, zestawów, dykt, ciągów, a także 0 i wszystkich obiektów z klas, które implementują __bool__()
magiczną metodę powrotu False
.
>>> bool(None)
False
>>> not None
True
>>> bool([])
False
>>> not []
True
>>> class MyFalsey(object):
... def __bool__(self):
... return False
>>> f = MyFalsey()
>>> bool(f)
False
>>> not f
True
Dlatego podczas testowania zmiennych w następujący sposób zwracaj szczególną uwagę na to, co włączasz lub wyłączasz z testu:
def some_function(value=None):
if not value:
value = init_value()
W powyższym przypadku, czy chciałeś zadzwonić, init_value()
gdy wartość jest ustawiona konkretnie na None
, czy miałeś na myśli, że wartość ustawiona na 0
, pusty ciąg znaków lub pusta lista powinna również inicjować inicjalizację. Tak jak powiedziałem, bądź ostrożny. Jak to często bywa w Pythonie, jawne jest lepsze niż niejawne .
None
w praktyceNone
używany jako wartość sygnałuNone
ma specjalny status w Pythonie. Jest to ulubiona wartość wyjściowa, ponieważ wiele algorytmów traktuje ją jako wartość wyjątkową. W takich scenariuszach można go użyć jako flagi, aby zasygnalizować, że warunek wymaga specjalnej obsługi (np. Ustawienia wartości domyślnej).
Możesz przypisać None
argumentom słowa kluczowego funkcji, a następnie jawnie ją przetestować.
def my_function(value, param=None):
if param is None:
# do something outrageous!
Możesz przywrócić go jako domyślny, gdy próbujesz dostać się do atrybutu obiektu, a następnie jawnie go przetestować przed zrobieniem czegoś specjalnego.
value = getattr(some_obj, 'some_attribute', None)
if value is None:
# do something spectacular!
Domyślnie get()
metoda słownika powraca None
podczas próby uzyskania dostępu do nieistniejącego klucza:
>>> some_dict = {}
>>> value = some_dict.get('foo')
>>> value is None
True
Jeśli spróbujesz uzyskać do niego dostęp za pomocą notacji indeksu dolnego KeyError
, zostanie podniesiony
>>> value = some_dict['foo']
KeyError: 'foo'
Podobnie jeśli spróbujesz rozbić nieistniejący element
>>> value = some_dict.pop('foo')
KeyError: 'foo'
które można ukryć za pomocą domyślnej wartości, która zwykle jest ustawiona na None
value = some_dict.pop('foo', None)
if value is None:
# booom!
None
używany zarówno jako flaga, jak i poprawna wartośćOpisane powyżej zastosowania None
mają zastosowanie, gdy nie jest uważane za prawidłową wartość, ale bardziej przypomina sygnał do zrobienia czegoś specjalnego. Są jednak sytuacje, w których czasem ważne jest, aby wiedzieć, skąd None
pochodzi, ponieważ nawet jeśli jest używany jako sygnał, może również stanowić część danych.
Kiedy pytasz obiekt o jego atrybut z getattr(some_obj, 'attribute_name', None)
powrotem None
, nie mówi ci, czy atrybut, do którego próbujesz uzyskać dostęp, został ustawiony, None
czy też był całkowicie nieobecny w obiekcie. Ta sama sytuacja podczas uzyskiwania dostępu do klucza ze słownika, np. some_dict.get('some_key')
Nie wiesz, czy go some_dict['some_key']
brakuje, czy po prostu jest ustawiony na None
. Jeśli potrzebujesz tych informacji, zwykłym sposobem radzenia sobie z nimi jest bezpośrednia próba uzyskania dostępu do atrybutu lub klucza z poziomu try/except
konstrukcji:
try:
# equivalent to getattr() without specifying a default
# value = getattr(some_obj, 'some_attribute')
value = some_obj.some_attribute
# now you handle `None` the data here
if value is None:
# do something here because the attribute was set to None
except AttributeError:
# we're now hanling the exceptional situation from here.
# We could assign None as a default value if required.
value = None
# In addition, since we now know that some_obj doesn't have the
# attribute 'some_attribute' we could do something about that.
log_something(some_obj)
Podobnie z dict:
try:
value = some_dict['some_key']
if value is None:
# do something here because 'some_key' is set to None
except KeyError:
# set a default
value = None
# and do something because 'some_key' was missing
# from the dict.
log_something(some_dict)
Powyższe dwa przykłady pokazują, jak obsługiwać przypadki obiektów i słowników, a co z funkcjami? To samo, ale w tym celu używamy argumentu słowa kluczowego podwójnej gwiazdki:
def my_function(**kwargs):
try:
value = kwargs['some_key']
if value is None:
# do something because 'some_key' is explicitly
# set to None
except KeyError:
# we assign the default
value = None
# and since it's not coming from the caller.
log_something('did not receive "some_key"')
None
używane tylko jako poprawna wartośćJeśli stwierdzisz, że Twój kod jest zaśmiecony powyższym try/except
wzorcem, aby odróżnić None
flagi i None
dane, użyj innej wartości testowej. Istnieje wzorzec, w którym wartość, która nie mieści się w zestawie prawidłowych wartości, jest wstawiana jako część danych w strukturze danych i służy do kontrolowania i testowania warunków specjalnych (np. Granic, stanu itp.). Taką wartość nazywa się wartownikiem i można ją wykorzystać tak, None
jak sygnał. Utworzenie wartownika w Pythonie jest banalne.
undefined = object()
Powyższy undefined
obiekt jest wyjątkowy i nie robi nic, co mogłoby zainteresować program, dlatego jest doskonałym zamiennikiem None
jako flaga. Obowiązują pewne zastrzeżenia, więcej o tym po kodzie.
Z funkcją
def my_function(value, param1=undefined, param2=undefined):
if param1 is undefined:
# we know nothing was passed to it, not even None
log_something('param1 was missing')
param1 = None
if param2 is undefined:
# we got nothing here either
log_something('param2 was missing')
param2 = None
Z dyktando
value = some_dict.get('some_key', undefined)
if value is None:
log_something("'some_key' was set to None")
if value is undefined:
# we know that the dict didn't have 'some_key'
log_something("'some_key' was not set at all")
value = None
Z przedmiotem
value = getattr(obj, 'some_attribute', undefined)
if value is None:
log_something("'obj.some_attribute' was set to None")
if value is undefined:
# we know that there's no obj.some_attribute
log_something("no 'some_attribute' set on obj")
value = None
Jak wspomniałem wcześniej, niestandardowi strażnicy mają pewne zastrzeżenia. Po pierwsze, nie są one słowami kluczowymi None
, więc Python ich nie chroni. Możesz zastąpić undefined
powyższe w dowolnym momencie, w dowolnym miejscu w module, który jest zdefiniowany, więc bądź ostrożny, jak je ujawnić i używać. Następnie instancja zwrócona przez object()
nie jest singletonem, jeśli wykonasz to wywołanie 10 razy, otrzymasz 10 różnych obiektów. Wreszcie użycie wartownika jest bardzo idiosynkratyczne. Wartownik jest specyficzny dla biblioteki, w której jest używany i jako taki, jego zakres powinien zasadniczo być ograniczony do wewnętrznych elementów biblioteki. Nie powinien „wyciec”. Kod zewnętrzny powinien wiedzieć o tym tylko, jeśli ich celem jest rozszerzenie lub uzupełnienie interfejsu API biblioteki.
Nazywa się nie zerowy jak w innych językach, ale None
. Zawsze jest tylko jedna instancja tego obiektu, więc możesz sprawdzić równoważność z x is None
(porównanie tożsamości) zamiast x == None
, jeśli chcesz.
None
. Wtedy wszyscy mądrzy ludzie, którzy porównali się z nimi NoneType
, zatriumfują!
W Pythonie, aby reprezentować brak wartości, możesz użyć wartości None ( types.NoneType.None ) dla obiektów i „” (lub len () == 0 ) dla łańcuchów. W związku z tym:
if yourObject is None: # if yourObject == None:
...
if yourString == "": # if yourString.len() == 0:
...
Jeśli chodzi o różnicę między „==” a „is”, wystarczające powinno być sprawdzenie tożsamości obiektu za pomocą „==”. Ponieważ jednak operacja „jest” jest zdefiniowana jako operacja tożsamości obiektu, prawdopodobnie bardziej poprawne jest użycie jej niż „==”. Nie jestem pewien, czy występuje nawet różnica prędkości.
W każdym razie możesz spojrzeć na:
0 == false
zwraca true
się tak, ponieważ dwukrotnie równa operator wykonuje typu przymus. Jednak z operatorem triple-equals 0 === false
zwraca wartość false
, ponieważ „liczba” i „wartość logiczna” są różnymi typami.
true
warunkowo. W Rust and Go, 0
i false
są różne typy, które nie mogą być porównywane dla równości.
Null to specjalny typ obiektu, taki jak:
>>>type(None)
<class 'NoneType'>
Możesz sprawdzić, czy obiekt należy do klasy „NoneType”:
>>>variable = None
>>>variable is None
True
Więcej informacji jest dostępnych w Python Docs
W przypadku testowania wartości według prawdy „Brak” testuje bezpośrednio jako FAŁSZ, więc wystarczy najprostsze wyrażenie:
if not foo:
True
do dowolnej wartości falsy nie tylko None
.
egg is None
przezegg == None
: Te ostatnie mogą być przeciążone, a może złamać przy porównywaniu ważny obiekt z None (w zależności od tego, jak to jest realizowane, ale nie należy się spodziewać, wszyscy wziąć Porównania z Żaden pod uwagę, prawda?) , podczas gdyis
zawsze działa tak samo.