obiekt null w Pythonie?

Jak odnosić się do obiektu null w Pythonie?

 851
Author: undur_gongor, 2010-07-20

5 answers

W Pythonie obiekt 'null' to singleton None.

Najlepszym sposobem sprawdzenia rzeczy pod kątem "Noneness" jest użycie operatora tożsamości, is:

if foo is None:
    ...
 1176
Author: Ben James,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-20 11:54:27

Nie nazywa się null jak w innych językach, Ale None. Zawsze istnieje 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.

 56
Author: AndiDog,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-20 11:56:34

None, Python ' s null?

Nie ma null w Pythonie, zamiast tego jest None. Jak już wspomniano, najdokładniejszym sposobem sprawdzenia, czy coś zostało podane None jako wartość, jest użycie operatora tożsamości is, który sprawdza, czy dwie zmienne odnoszą się do tego samego obiektu.

>>> foo is None
True
>>> foo = 'bar' 
>>> foo is None
False

Podstawy

Istnieje i może być tylko jeden None

None jest jedyną instancją klasy NoneType i wszelkimi dalszymi próbami utworzenia instancji tej klasy zwróci ten sam obiekt, co czyni None singletonem. Nowi użytkownicy Pythona często widzą komunikaty o błędach, które wspominają NoneType i zastanawiają się, co to jest. To moja osobista opinia, że te wiadomości mogą po prostu wspomnieć None po imieniu, ponieważ, jak zobaczymy wkrótce, {32]} pozostawia niewiele miejsca na dwuznaczność. Więc jeśli widzisz jakieś TypeError wiadomość, która wspomina, że NoneType nie można zrobić tego lub nie można zrobić tamtego, po prostu wiedz, że to po prostu ten None został użyty w taki sposób, że nie mogę.

Ponadto, None jest wbudowaną stałą, gdy tylko uruchomisz Pythona, jest ona dostępna do użycia z każdego miejsca, czy to w module, klasie, czy funkcji. NoneType dla kontrastu nie jest, musisz najpierw uzyskać odniesienie do niego, pytając None o jego klasę.

>>> NoneType
NameError: name 'NoneType' is not defined
>>> type(None)
NoneType

Możesz sprawdzić unikalność None Za pomocą funkcji tożsamości Pythona id(). Zwraca unikalny numer przypisany do obiektu, każdy obiekt ma taki. Jeśli identyfikator dwóch zmiennych jest taki sam, to wskazują na fakt do tego samego obiektu.

>>> 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 nadpisać

W znacznie starszej wersji Pythona (przed 2.4) można było ponownie przypisać 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

Można więc bezpiecznie założyć, że wszystkie odniesienia None są takie same. Nie ma "zwyczaju" None.

Aby przetestować na None użyj operatora is

Pisząc kod możesz pokusić się o przetestowanie Noneness like this:

if value==None:
    pass

Lub testować na fałsz w ten sposób

if not value:
    pass

Musisz zrozumieć implikacje i dlaczego często dobrze jest być wyraźnym.

Przypadek 1: sprawdzanie, czy wartość jest None

Dlaczego to zrobić

value is None

Zamiast

value==None

Pierwsza jest równoważna:

id(value)==id(None)

Natomiast wyrażenie value==None jest w rzeczywistości stosowane w ten sposób

value.__eq__(None)

Jeśli wartość rzeczywiście jest None wtedy dostaniesz to, czego oczekiwałeś.

>>> nothing = function_that_does_nothing()
>>> nothing.__eq__(None)
True

W większości przypadków wynik będzie taki sam, ale metoda __eq__() otwiera drzwi, które unieważniają gwarancję dokładności, ponieważ może być nadpisana w klasie, aby zapewnić specjalne zachowanie.

Rozważ tę klasę.

>>> class Empty(object):
...     def __eq__(self, other):
...         return not other

Więc przymierzasz None i działa

>>> empty = Empty()
>>> empty==None
True

Ale wtedy działa również na pustym łańcuchu

>>> empty==''
True

A jednak

>>> ''==None
False
>>> empty is None
False

Przypadek 2: Użycie None jako boolean

Następujące 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" false", co oznacza, że jeśli zostanie rzucona na boolean, zwróci False, a jeśli zastosuje się operator not, zwróci True. Należy jednak pamiętać, że nie jest to właściwość unikalna dla None. Oprócz samej False, właściwość jest współdzielona przez puste listy, krotki, zestawy, dikty, łańcuchy, a także przez 0 i wszystkie obiekty z klas, które implementują metodę magiczną __bool__() do return 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
W ten sposób, podczas testowania zmiennych w następujący sposób, należy być dodatkowo świadomym tego, co włączasz lub wyłączasz z testu: [106]}
def some_function(value=None):
    if not value:
        value = init_value()

W powyższym przykładzie, czy chodziło Ci o wywołanie init_value(), gdy wartość jest ustawiona konkretnie na None, czy chodziło Ci o to, że wartość ustawiona na 0, pusty łańcuch lub pusta lista również powinny wywołać inicjalizację. Jak mówiłem, bądź uważny. Jak to często bywa w Pythonie explicit jest lepszy niż implicit .

None w praktyce

None używany jako wartość sygnału

None ma specjalny status w Pythonie. Jest to ulubiona Wartość bazowa, ponieważ wiele algorytmów traktuje ją jako wyjątkową wartość. W takich scenariuszach może być używany jako znacznik, aby zasygnalizować, że warunek wymaga specjalnej obsługi (np. ustawienia wartości domyślnej).

Możesz przypisać None do argumentów kluczowych funkcji, a następnie jawnie przetestować to.

def my_function(value, param=None):
    if param is None:
        # do something outrageous!

Możesz zwrócić go jako domyślny, gdy próbujesz dostać się do atrybutu obiektu, a następnie jawnie przetestować go przed wykonaniem czegoś specjalnego.

value = getattr(some_obj, 'some_attribute', None)
if value is None:
    # do something spectacular!

Domyślnie metoda słownika get() zwraca 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 dolnej a KeyError zostanie podniesione

>>> value = some_dict['foo']
KeyError: 'foo'

Podobnie jeśli spróbujesz pop nieistniejącego pozycja

>>> value = some_dict.pop('foo')
KeyError: 'foo'

Które można wyłączyć za pomocą domyślnej wartości, która jest zwykle ustawiona na None

value = some_dict.pop('foo', None)
if value is None:
    # booom!

None używane jako flaga i poprawna wartość

Opisane powyżej zastosowania None mają zastosowanie, gdy nie jest uważana za prawidłową wartość, ale raczej jako sygnał do zrobienia czegoś specjalnego. Są jednak sytuacje, w których czasami ważne jest, aby wiedzieć, skąd None pochodzi, ponieważ nawet jeśli jest używany jako sygnał, może być również częścią danych.

Kiedy zapytujesz obiekt o jego atrybut za pomocą getattr(some_obj, 'attribute_name', None) odzyskanie None nie powie Ci, czy atrybut, do którego próbowałeś uzyskać dostęp, był ustawiony na None lub czy był całkowicie nieobecny w obiekcie. Ta sama sytuacja, gdy uzyskujesz dostęp do klucza ze słownika, takiego jak some_dict.get('some_key'), nie wiesz, czy brakuje some_dict['some_key'], czy jest on po prostu ustawiony na None. Jeśli potrzebujesz tych informacji, standardowym sposobem obsługi tego problemu jest bezpośrednia próba uzyskania dostępu do atrybutu lub klucza z poziomu try/except konstruktor:

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łownika, a co z funkcjami? To samo, ale w tym celu używamy argumentu słowa kluczowego double asterisks:

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 ważna wartość

Jeśli okaże się, że Twój kod jest zaśmiecony powyższym wzorcem try/except, aby odróżnić znaczniki None od danych None, po prostu użyj innej wartości testowej. Jest wzorzec, w którym wartość wykraczająca poza zestaw ważnych wartości jest wstawiana jako część danych w strukturze danych i jest używana do kontrolowania i testowania specjalnych warunków (np. granic, stanu, itp.). Taka wartość nazywa się sentinel i może być używana w sposób None jako sygnał. Tworzenie Sentinela w Pythonie jest trywialne.

undefined = object()

Obiekt undefined powyżej jest unikalny i nie robi zbyt wiele z niczego, co może być interesujące dla programu, jest więc doskonałym zastępuje None jako flagę. 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 dict

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 obiektem

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 custom sentinels mają pewne zastrzeżenia. Po pierwsze, nie są to słowa kluczowe jak None, więc python ich nie chroni. Możesz nadpisać undefined powyżej w dowolnym momencie, w dowolnym miejscu zdefiniowanego modułu, więc uważaj, jak je ujawniasz i używasz. Następnie instancja zwracane przez object() nie jest singletonem, jeśli wykonasz to połączenie 10 razy, otrzymasz 10 różnych obiektów. W końcu użycie Sentinela jest wysoce idiosynkratyczne. Sentinel jest specyficzny dla biblioteki, w której jest używany i jako taki jego zakres powinien być ogólnie ograniczony do wewnętrznych bibliotek. Nie powinno się "wyciekać". Zewnętrzny kod powinien być o tym świadomy tylko wtedy, gdy jego celem jest rozszerzenie lub uzupełnienie API biblioteki.

 37
Author: Michael Ekoka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-31 03:32:36

W Pythonie, aby reprezentować brak wartości, możesz użyć typów None value (.NoneType.None ) dla obiektów i "" (lub len() == 0) na sznurki. Dlatego:

if yourObject is None:  # if yourObject == None:
    ...

if yourString == "":  # if yourString.len() == 0:
    ...

Jeśli chodzi o różnicę między " = = "I" is", testowanie tożsamości obiektu za pomocą "= = " powinno być wystarczające. Jednakże, ponieważ operacja " is "jest zdefiniowana jako operacja tożsamości obiektu, prawdopodobnie bardziej poprawne jest jej użycie niż"==". Nie wiem, czy istnieje nawet różnica prędkości.

W każdym razie możesz rzucić okiem na:

 26
Author: Paolo Rovelli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-03 11:11:50

Per testowanie wartości prawdy , 'None' bezpośrednio testuje jako FALSE, więc najprostsze wyrażenie wystarczy:

if not foo:
 1
Author: artejera,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-24 23:15:42