pusty obiekt w Pythonie?

1190

Jak odwoływać się do obiektu zerowego w Pythonie?

Jaszczurka
źródło

Odpowiedzi:

1626

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:
    ...
Ben James
źródło
125
A powodem wyboru egg is Noneprzez egg == 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 gdy iszawsze działa tak samo.
94
dlaczego projektanci Pythona nie wybrali „null”. Musisz być inny, prawda! ;)
Vidar
35
@Vidar „Brak” nie jest brakiem obiektu (ponieważ „null” oznacza odwołanie zerowe), jest to rzeczywisty obiekt. Nigdy nie dostaniesz obiektu „Brak” dla obiektu innego niż „NoneType”. To także nie jest koncepcja języka. Nie jest wbudowana w język Python, jest częścią standardowej biblioteki Pythona. Brak! == (ahem) Null
Rushyo
11
@ naught101 To by nie służyło; ponieważ nie ma koncepcji wskaźników. Jeśli korzystasz z biblioteki ctypes, Żadna nie będzie używana jako synonim adresu zero, ale nadal nie istnieje koncepcja języka „zerowego odwołania”. W Pythonie zawsze pozostawiałeś odniesienie do jakiegoś obiektu, nawet jeśli ten obiekt to Brak. Podejrzewam, że to znacznie upraszcza język.
Rushyo
5
świetna prezentacja wyjaśniająca „błąd miliarda dolarów”, czyli zerowy ref: infoq.com/presentations/… . Inne opcje zamiast null to Opcje lub Może (niestandardowe) typy.
Michael Trouw,
133

NonePython jest zerowy?

W nullPythonie nie ma None. Jak już wspomniano, najdokładniejszym sposobem sprawdzenia, czy coś zostało podane Nonejako wartość, jest użycie isoperatora 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

Podstawy

Jest i może być tylko jeden None

Nonejest jedyną instancją klasy NoneTypei wszelkie dalsze próby utworzenia tej klasy zwrócą ten sam obiekt, co spowoduje Noneutworzenie singletonu. Nowi użytkownicy Pythona często widzą komunikaty o błędach, które wspominają NoneTypei zastanawiają się, co to jest. Moim osobistym zdaniem te wiadomości mogą po prostu wspomnieć Nonepo imieniu, ponieważ, jak zobaczymy wkrótce, Nonepozostawia niewiele miejsca na dwuznaczność. Więc jeśli zobaczysz jakieś TypeErrorprzesłanie, które wspomina, że NoneTypenie może tego zrobić lub nie może tego zrobić, po prostu wiedz, że to po prostu taki, Nonektóry był używany w sposób, którego nie może.

Ponadto Nonejest wbudowaną stałą, gdy tylko uruchamiasz Python, można go używać z dowolnego miejsca, czy to w module, klasie czy funkcji. NoneTypew przeciwieństwie do tego nie trzeba, najpierw trzeba by się do niego odwołać, sprawdzając Nonejego klasę.

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

Możesz sprawdzić Noneunikalność 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 Noneodniesienia są takie same. Nie ma „niestandardowego” None.

Aby przetestować Noneużycie isoperatora

Podczas 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.

Przypadek 1: sprawdzenie, czy wartość ma wartość 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==Nonejest 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 Nonei 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

Przypadek 2: Używanie Nonejako wartości logicznej

Nastę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

Nonejest „falsey”, co oznacza, że ​​jeśli zostanie ustawiony na wartość logiczną, zwróci Falsei jeśli zastosuje notoperator, zwróci True. Pamiętaj jednak, że nie jest to właściwość unikalna dla None. Oprócz Falsesiebie 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 praktyce

None używany jako wartość sygnału

Nonema 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ć Noneargumentom 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 Nonepodczas 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 Nonemają 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 Nonepochodzi, 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, Noneczy 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/exceptkonstrukcji:

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/exceptwzorcem, aby odróżnić Noneflagi i Nonedane, 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, Nonejak sygnał. Utworzenie wartownika w Pythonie jest banalne.

undefined = object()

Powyższy undefinedobiekt jest wyjątkowy i nie robi nic, co mogłoby zainteresować program, dlatego jest doskonałym zamiennikiem Nonejako 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ć undefinedpowyż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.

Michael Ekoka
źródło
Co z: Brak == rzeczą?
hkBst
@ hkBst Zgaduję, że twoje pytanie dotyczy tego, jak Python ocenia równość. Spójrz na ten stackoverflow.com/questions/3588776/...
Michael Ekoka
Ach, więc IIUC, które zwykle kończy się dzwonieniem .__ eq__? W takim przypadku wycofuję moją sugestię.
hkBst
@MichaelEkoka możesz udostępnić źródło tych imponujących i przydatnych informacji.
Anidhya Bhatnagar,
Standardowa praktyka. Możesz znaleźć pewne wskazówki na jego temat, przechodząc przez samouczek Python , ale czytanie kodu źródłowego popularnych projektów to moja rada numer jeden, aby szybko zanurzyć się w idiomatycznym Pythonie .
Michael Ekoka
67

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.

AndiDog
źródło
25
Zamierzam znaleźć sposób na złamanie Pythona poprzez ponowne utworzenie instancji None. Wtedy wszyscy mądrzy ludzie, którzy porównali się z nimi NoneType, zatriumfują!
Zenexer
28

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:

Paolo Rovelli
źródło
8
O ile mi wiadomo, (0 == fałsz) zwraca true W KAŻDYM języku programowania. Wynika to z faktu, że fałsz jest kodowany jako 0, a „prawda” jako wszystko, co NIE jest 0. Jednak nie należy twierdzić, że operator „jest” nie jest tym samym, co operator „==”. Jest to mniej więcej taka sama różnica między „==” i „równa się” w Javie. Operator „jest”, rzeczywiście sprawdza, czy DWA OBIEKTY są takie same (ta sama instancja i NIE ta sama treść)!
Paolo Rovelli,
12
Paolo, FYI, (0 == false) nie zwraca wartości true w każdym języku programowania. Przykładem szybkiego licznika jest Scala, gdzie (0 == false) zwraca false, i jestem pewien, że Scala nie jest jedynym językiem programowania, który zwraca false podczas porównywania liczby z wartością logiczną.
Rob Wilton
2
Ok, rozumiem! Powiedzmy, że w większości języków programowania.
Paolo Rovelli,
3
W JavaScript, powód 0 == falsezwraca truesię tak, ponieważ dwukrotnie równa operator wykonuje typu przymus. Jednak z operatorem triple-equals 0 === falsezwraca wartość false, ponieważ „liczba” i „wartość logiczna” są różnymi typami.
jkdev
1
@PaoloRovelli, w Lua, Ruby i Clojure, 0 jest traktowane jak truewarunkowo. W Rust and Go, 0i false są różne typy, które nie mogą być porównywane dla równości.
skuter-dynda
0

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


źródło
-1

W przypadku testowania wartości według prawdy „Brak” testuje bezpośrednio jako FAŁSZ, więc wystarczy najprostsze wyrażenie:

if not foo:
artejera
źródło
3
Nie zrobi tego. Że wyrażenie powróci Truedo dowolnej wartości falsy nie tylko None.
insert_name_here
To prawda, a proste „jeśli nie foo:” nie odpowiedziałoby poprawnie na pierwotne pytanie, jak stwierdzono. Jednak Python jest dynamicznie typowany i zachęca do zmniejszenia protokołów składniowych, więc wydaje mi się słuszne, aby rozważyć prostsze, podobne konstrukcje.
artejera