Refiriéndose al objeto nulo en Python

1329

¿Cómo me refiero al objeto nulo en Python?

0
1783

En Python, el objeto 'nulo' es el singleton None.

La mejor forma de comprobar la existencia de "Nada" es utilizar el operador de identidad is:

if foo is None:
    ...
10
  • 139
    Y la razón para elegir egg is Nonemás egg == None: Estos últimos pueden ser sobrecargados, y es probable que se rompa cuando se comparan objeto válido con la opción Ninguno (depende de cómo se implementa, pero no esperan que todos tomen comparaciones con ninguno en cuenta, ¿verdad?) , aunque issiempre funciona igual.
    user395760
    20/07/10 a las 12:25
  • 103
    ¿Por qué los diseñadores de Python simplemente no eligieron "nulo"? Solo tienes que ser diferente, ¿no? ;)
    Vidar
    26 de marzo de 2013 a las 9:44
  • 39
    @Vidar 'Ninguno' no es una falta de un objeto (como 'nulo' es, una referencia nula), es un objeto real. Nunca obtendrá un objeto 'Ninguno' pasando por un objeto que sea de un tipo diferente a 'NingunoTipo'. Tampoco es un concepto de lenguaje. No está integrado en el lenguaje Python, es parte de la biblioteca estándar de Python. ¡Ninguno! == (ejem) Nulo
    Rushyo
    12/06/2013 a las 12:17
  • 12
    @ naught101 No serviría de nada; ya que no existe el concepto de punteros. Si está utilizando la biblioteca ctypes, None se usará como sinónimo de la dirección cero, pero todavía no existe el concepto de lenguaje de una 'referencia nula'. En Python, siempre dejaba hacer referencia a algún tipo de objeto, incluso si ese objeto es Ninguno. Sospecho que esto simplifica enormemente el lenguaje.
    Rushyo
    2 de septiembre de 2014 a las 13:26
  • 6
    gran presentación que explica el 'error de mil millones de dólares' que es la referencia nula: infoq.com/presentations/… . Otras opciones en lugar de nulas son los tipos Opciones o Quizás (personalizado). 8 de abril de 2015 a las 23:56
188

None, ¿Python es nulo?

No hay nullen Python; en su lugar hay None. Como ya se dijo, la forma más precisa de probar que se ha dado algo Nonecomo valor es usar el isoperador de identidad, que prueba que dos variables se refieren al mismo objeto.

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

Los basicos

Hay y solo puede haber uno None

Nonees la única instancia de la clase NoneTypey cualquier intento posterior de instanciar esa clase devolverá el mismo objeto, lo que hace Noneun singleton. Los recién llegados a Python a menudo ven mensajes de error que mencionan NoneTypey se preguntan qué es. En mi opinión personal, estos mensajes podrían simplemente mencionarse Nonepor su nombre porque, como veremos en breve, Nonedejan poco espacio a la ambigüedad. Entonces, si ve algún TypeErrormensaje que menciona que NoneTypeno puede hacer esto o no puede hacer aquello, solo sepa que es simplemente el Noneque se estaba utilizando de una manera que no puede.

Además, Nonees una constante incorporada. Tan pronto como inicie Python, estará disponible para usar desde cualquier lugar, ya sea en módulo, clase o función. NoneTypepor el contrario, no lo es, primero debe obtener una referencia consultando Nonesu clase.

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

Puede comprobar Nonela unicidad con la función de identidad de Python id(). Devuelve el número único asignado a un objeto, cada objeto tiene uno. Si el id de dos variables es el mismo, entonces apuntan al mismo objeto.

>>> 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 no se puede sobrescribir

En versiones mucho más antiguas de Python (antes de la 2.4) era posible reasignar None, pero ya no. Ni siquiera como un atributo de clase o en los confines de una función.

# 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

Por lo tanto, es seguro asumir que todas las Nonereferencias son iguales. No hay ninguna "costumbre" None.

Para probar el Noneuso del isoperador

Al escribir código, es posible que tenga la tentación de probar la Noneness de esta manera:

if value==None:
    pass

O para probar una falsedad como esta

if not value:
    pass

Debe comprender las implicaciones y por qué a menudo es una buena idea ser explícito.

Caso 1: probar si un valor es None

Por qué

value is None

en vez de

value==None

?

El primero equivale a:

id(value)==id(None)

Mientras que la expresión value==Nonese aplica así

value.__eq__(None)

Si el valor es realmente None, obtendrá lo que esperaba.

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

En los casos más comunes, el resultado será el mismo, pero el __eq__()método abre una puerta que anula cualquier garantía de precisión, ya que puede anularse en una clase para proporcionar un comportamiento especial.

Considere esta clase.

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

Así que pruébalo Noney funciona

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

Pero luego también funciona en la cadena vacía.

>>> empty==''
True

Y todavía

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

Caso 2: Usar Nonecomo booleano

Las siguientes dos pruebas

if value:
    # Do something

if not value:
    # Do something

son de hecho evaluados como

if bool(value):
    # Do something

if not bool(value):
    # Do something

Nonees un "falsey", lo que significa que si se convierte en un booleano, volverá Falsey si se aplica el notoperador, volverá True. Sin embargo, tenga en cuenta que no es una propiedad exclusiva de None. Además de Falsesí misma, la propiedad es compartida por listas vacías, tuplas, conjuntos, dictados, cadenas, así como 0, y todos los objetos de las clases que implementan el __bool__()método mágico para regresar 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

Por lo tanto, cuando pruebe las variables de la siguiente manera, tenga más en cuenta lo que está incluyendo o excluyendo de la prueba:

def some_function(value=None):
    if not value:
        value = init_value()

En lo anterior, ¿quiso llamar init_value()cuando el valor se establece específicamente en None, o quiso decir que un valor establecido en 0, o la cadena vacía, o una lista vacía también debería activar la inicialización? Como dije, sea consciente. Como suele ser el caso, en Python lo explícito es mejor que lo implícito .

None en la práctica

None utilizado como valor de señal

Nonetiene un estado especial en Python. Es un valor de referencia favorito porque muchos algoritmos lo tratan como un valor excepcional. En tales escenarios, se puede usar como una bandera para señalar que una condición requiere un manejo especial (como la configuración de un valor predeterminado).

Puede asignar Noneargumentos a las palabras clave de una función y luego probarlos explícitamente.

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

Puede devolverlo como predeterminado cuando intente acceder al atributo de un objeto y luego probarlo explícitamente antes de hacer algo especial.

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

De forma predeterminada, el get()método de un diccionario regresa Noneal intentar acceder a una clave no existente:

>>> some_dict = {}
>>> value = some_dict.get('foo')
>>> value is None
True

Si intentara acceder a él utilizando la notación de subíndice KeyError, se generaría un

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

Del mismo modo, si intenta hacer estallar un artículo que no existe

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

que puede suprimir con un valor predeterminado que generalmente se establece en None

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

None utilizado como indicador y valor válido

Los usos descritos anteriormente de se Noneaplican cuando no se considera un valor válido, sino más bien como una señal para hacer algo especial. Sin embargo, hay situaciones en las que a veces es importante saber de dónde Noneviene porque, aunque se usa como señal, también podría ser parte de los datos.

Cuando consulta un objeto por su atributo con la getattr(some_obj, 'attribute_name', None)devolución, Noneno le dice si el atributo al que estaba tratando de acceder estaba configurado Noneo si estaba completamente ausente del objeto. La misma situación cuando se accede a una clave desde un diccionario, por ejemplo some_dict.get('some_key'), no sabe si some_dict['some_key']falta o si simplemente está configurada None. Si necesita esa información, la forma habitual de manejar esto es intentar acceder directamente al atributo o clave desde dentro de una try/exceptconstrucción:

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 handling 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)

De manera similar con 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)

Los dos ejemplos anteriores muestran cómo manejar casos de objetos y diccionarios. ¿Qué pasa con las funciones? Lo mismo, pero usamos el argumento de palabra clave de doble asterisco para ese fin:

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 utilizado solo como un valor válido

Si encuentra que su código está lleno del try/exceptpatrón anterior simplemente para diferenciar entre Noneindicadores y Nonedatos, entonces use otro valor de prueba. Existe un patrón en el que un valor que cae fuera del conjunto de valores válidos se inserta como parte de los datos en una estructura de datos y se usa para controlar y probar condiciones especiales (por ejemplo, límites, estado, etc.). Este valor se llama centinela y se puede utilizar de la misma forma que Nonese utiliza como señal. Es trivial crear un centinela en Python.

undefined = object()

El undefinedobjeto anterior es único y no hace mucho de nada que pueda ser de interés para un programa, por lo que es un excelente reemplazo Nonecomo indicador. Se aplican algunas advertencias, más sobre eso después del código.

Con función

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

Con 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

Con un objeto

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

Como mencioné anteriormente, los centinelas personalizados vienen con algunas advertencias. Primero, no son palabras clave como None, por lo que Python no las protege. Puede sobrescribir lo undefinedanterior en cualquier momento, en cualquier lugar del módulo que esté definido, así que tenga cuidado de cómo los expone y usa. A continuación, la instancia devuelta por object()no es un singleton. Si haces esa llamada 10 veces, obtienes 10 objetos diferentes. Finalmente, el uso de un centinela es altamente idiosincrásico. Un centinela es específico de la biblioteca en la que se usa y, como tal, su alcance generalmente debería limitarse a las partes internas de la biblioteca. No debería "filtrarse". El código externo solo debe tener conocimiento de él, si su propósito es extender o complementar la API de la biblioteca.

8
  • ¿Qué pasa con: Ninguna == cosa?
    hkBst
    1 de nov. De 2018 a las 9:56
  • @hkBst Supongo que tu pregunta es sobre cómo Python evalúa la igualdad. Eche un vistazo a este stackoverflow.com/questions/3588776/… 1 de nov. De 2018 a las 17:02
  • Ah, entonces IIUC que normalmente terminará llamando a la cosa .__ eq__? En ese caso, me retracto de mi sugerencia.
    hkBst
    2/11/18 a las 9:56
  • @MichaelEkoka puede compartir la fuente de esta información útil e impresionante. 25 de diciembre de 2018 a las 6:46
  • Práctica estándar. Puede encontrar algunos indicios al seguir el tutorial de Python , pero leer el código fuente de los proyectos populares es mi consejo número uno para sumergirse rápidamente en el lenguaje idiomático de Python . 14/04/19 a las 5:53
70

No se llama nulo como en otros idiomas, pero None. Siempre hay solo una instancia de este objeto, por lo que puede verificar la equivalencia con x is None(comparación de identidad) en lugar de x == None, si lo desea.

1
  • 27
    Voy a encontrar una manera de romper Python volviendo a crear instancias None. ¡Entonces todas esas personas inteligentes con las que se compararon NoneTypetriunfarán!
    Zenexer
    27 de agosto de 2013 a las 3:40
29

En Python, para representar la ausencia de un valor, puede usar el valor None ( types.NoneType.None ) para objetos y "" (o len () == 0 ) para cadenas. Por lo tanto:

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

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

Con respecto a la diferencia entre "==" y "es", probar la identidad del objeto usando "==" debería ser suficiente. Sin embargo, dado que la operación "es" se define como la operación de identidad del objeto, probablemente sea más correcto usarla, en lugar de "==". No estoy seguro de si hay una diferencia de velocidad.

De todos modos, puedes echarle un vistazo a:

7
  • 8
    Hasta donde yo sé, (0 == falso) devuelve verdadero EN CADA lenguaje de programación. Eso es porque falso está codificado como 0 y "verdadero" como todo lo que NO es 0. Sin embargo, tenga en cuenta que el operador "es" no es el mismo que el "==". Esta es más o menos la misma diferencia entre "==" y "igual" en Java. El operador "es", de hecho, comprueba si DOS OBJETOS son iguales (la misma instancia y NO el mismo contenido). 13 dic 13 a las 14:43
  • 13
    Paolo, FYI, (0 == falso) no devuelve verdadero en todos los lenguajes de programación. Un ejemplo de contador rápido sería Scala, donde (0 == falso) devuelve falso, y estoy seguro de que Scala no es el único lenguaje de programación que devuelve falso al comparar un número con un booleano. 1 de julio de 2014 a las 14:40
  • 4
    En JavaScript, la razón por la que 0 == falseregresa truees porque el operador doble igual escribe coerción. Sin embargo, con el operador triple-igual, 0 === falsedevuelve false, porque 'número' y 'booleano' son tipos diferentes.
    jkdev
    18 de mayo de 2016 a las 20:41
  • 1
    @PaoloRovelli, en Lua, Ruby y Clojure, 0 se trata como trueen condicionales. En Rust and Go, 0y false son diferentes tipos que no se pueden comparar por igualdad. 15 de agosto de 2017 a las 0:23
  • 1
    @jkdev Sí, de hecho. Eso es exactamente lo que quería señalar con la diferencia "==" e "igual" de Java. Su ejemplo con JavaScript "==" y "===" es realmente mejor. :) 19 de mayo a las 13:44
0

Use una fcuerda para resolver esto.

year=None
year_val= 'null' if year is None else  str(year)
print(f'{year_val}')

null
-1

Null es un tipo de objeto especial como:

>>>type(None)
<class 'NoneType'>

Puede verificar si un objeto está en la clase 'NoneType':

>>>variable = None
>>>variable is None
True

Más información está disponible en Python Docs

1
  • Pero en realidad no se llama "nulo" (?). 20/06/20 a las 14:51
-2

Según la prueba del valor de Verdad , 'Ninguno' prueba directamente como FALSO, por lo que la expresión más simple será suficiente:

if not foo:
2
  • 5
    No, no lo hará. Esa expresión volverá Truepara cualquier valor Falsy, no sólo None. 24 de septiembre de 2017 a las 23:32
  • Verdadero, y el simple "si no foo:" no respondería correctamente a la pregunta original, como se dijo. Sin embargo, python se escribe dinámicamente e invita a reducir los protocolos de sintaxis, por lo que me parece válido considerar construcciones similares más simples. 6 oct 2017 a las 3:46