How do I refer to the null object in Python?
Practical Data Science using PythonPython does not have a null object. But the most closely related similar object is none. In this article, we will see how how None behaves in Python. Checking the type of Null and None we see that there is no Null Type and the None object is of type NoneType.
You can use the fillna() function to fill the null values in the dataset. The accuracy value comes out to be 77.98% which is a reduction over the previous case. This will not happen in general, in this case, it means that the mean has not filled the null value properly.
None
, Python's null?There's no null
in Python; instead there's None
. As stated already, the most accurate way to test that something has been given None
as a value is to use the is
identity operator, which tests that two variables refer to the same object.
>>> foo is None True >>> foo = 'bar' >>> foo is None False
None
None
is the sole instance of the class NoneType
and any further attempts at instantiating that class will return the same object, which makes None
a singleton. Newcomers to Python often see error messages that mention NoneType
and wonder what it is. It's my personal opinion that these messages could simply just mention None
by name because, as we'll see shortly, None
leaves little room to ambiguity. So if you see some TypeError
message that mentions that NoneType
can't do this or can't do that, just know that it's simply the one None
that was being used in a way that it can't.
Also, None
is a built-in constant. As soon as you start Python, it's available to use from everywhere, whether in module, class, or function. NoneType
by contrast is not, you'd need to get a reference to it first by querying None
for its class.
>>> NoneType NameError: name 'NoneType' is not defined >>> type(None) NoneType
You can check None
's uniqueness with Python's identity function id()
. It returns the unique number assigned to an object, each object has one. If the id of two variables is the same, then they point in fact to the same object.
>>> 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
cannot be overwrittenIn much older versions of Python (before 2.4) it was possible to reassign None
, but not any more. Not even as a class attribute or in the confines of a function.
# 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
It's therefore safe to assume that all None
references are the same. There isn't any "custom" None
.
None
use the is
operatorWhen writing code you might be tempted to test for Noneness like this:
if value==None: pass
Or to test for falsehood like this
if not value: pass
You need to understand the implications and why it's often a good idea to be explicit.
None
Why do
value is None
rather than
value==None
?
The first is equivalent to:
id(value)==id(None)
Whereas the expression value==None
is in fact applied like this
value.__eq__(None)
If the value really is None
then you'll get what you expected.
>>> nothing = function_that_does_nothing() >>> nothing.__eq__(None) True
In most common cases the outcome will be the same, but the __eq__()
method opens a door that voids any guarantee of accuracy, since it can be overridden in a class to provide special behavior.
Consider this class.
>>> class Empty(object): ... def __eq__(self, other): ... return not other
So you try it on None
and it works
>>> empty = Empty() >>> empty==None True
But then it also works on the empty string
>>> empty=='' True
And yet
>>> ''==None False >>> empty is None False
None
as a booleanThe following two tests
if value: # Do something if not value: # Do something
are in fact evaluated as
if bool(value): # Do something if not bool(value): # Do something
None
is a "falsey", meaning that if cast to a boolean it will return False
and if applied the not
operator it will return True
. Note however that it's not a property unique to None
. In addition to False
itself, the property is shared by empty lists, tuples, sets, dicts, strings, as well as 0, and all objects from classes that implement the __bool__()
magic method to 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
So when testing for variables in the following way, be extra aware of what you're including or excluding from the test:
def some_function(value=None): if not value: value = init_value()
In the above, did you mean to call init_value()
when the value is set specifically to None
, or did you mean that a value set to 0
, or the empty string, or an empty list should also trigger the initialization? Like I said, be mindful. As it's often the case, in Python explicit is better than implicit.
None
in practiceNone
used as a signal valueNone
has a special status in Python. It's a favorite baseline value because many algorithms treat it as an exceptional value. In such scenarios it can be used as a flag to signal that a condition requires some special handling (such as the setting of a default value).
You can assign None
to the keyword arguments of a function and then explicitly test for it.
def my_function(value, param=None): if param is None: # Do something outrageous!
You can return it as the default when trying to get to an object's attribute and then explicitly test for it before doing something special.
value = getattr(some_obj, 'some_attribute', None) if value is None: # do something spectacular!
By default a dictionary's get()
method returns None
when trying to access a non-existing key:
>>> some_dict = {} >>> value = some_dict.get('foo') >>> value is None True
If you were to try to access it by using the subscript notation a KeyError
would be raised
>>> value = some_dict['foo'] KeyError: 'foo'
Likewise if you attempt to pop a non-existing item
>>> value = some_dict.pop('foo') KeyError: 'foo'
which you can suppress with a default value that is usually set to None
value = some_dict.pop('foo', None) if value is None: # Booom!
None
used as both a flag and valid valueThe above described uses of None
apply when it is not considered a valid value, but more like a signal to do something special. There are situations however where it sometimes matters to know where None
came from because even though it's used as a signal it could also be part of the data.
When you query an object for its attribute with getattr(some_obj, 'attribute_name', None)
getting back None
doesn't tell you if the attribute you were trying to access was set to None
or if it was altogether absent from the object. The same situation when accessing a key from a dictionary, like some_dict.get('some_key')
, you don't know if some_dict['some_key']
is missing or if it's just set to None
. If you need that information, the usual way to handle this is to directly attempt accessing the attribute or key from within a try/except
construct:
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)
Similarly with 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)
The above two examples show how to handle object and dictionary cases. What about functions? The same thing, but we use the double asterisks keyword argument to that end:
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
used only as a valid valueIf you find that your code is littered with the above try/except
pattern simply to differentiate between None
flags and None
data, then just use another test value. There's a pattern where a value that falls outside the set of valid values is inserted as part of the data in a data structure and is used to control and test special conditions (e.g. boundaries, state, etc.). Such a value is called a sentinel and it can be used the way None
is used as a signal. It's trivial to create a sentinel in Python.
undefined = object()
The undefined
object above is unique and doesn't do much of anything that might be of interest to a program, it's thus an excellent replacement for None
as a flag. Some caveats apply, more about that after the code.
With function
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
With 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
With an object
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
As I mentioned earlier, custom sentinels come with some caveats. First, they're not keywords like None
, so Python doesn't protect them. You can overwrite your undefined
above at any time, anywhere in the module it's defined, so be careful how you expose and use them. Next, the instance returned by object()
is not a singleton. If you make that call 10 times you get 10 different objects. Finally, usage of a sentinel is highly idiosyncratic. A sentinel is specific to the library it's used in and as such its scope should generally be limited to the library's internals. It shouldn't "leak" out. External code should only become aware of it, if their purpose is to extend or supplement the library's API.
In Python, the 'null' object is the singleton None
.
The best way to check things for "Noneness" is to use the identity operator, is
:
if foo is None: ...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With