Safe dereferencing in Python
Asked Answered
G

8

49

Groovy has a nice operator for safe dereferencing, which helps to avoid NullPointerExceptions:

variable?.method()

The method will only be called, if variable is not null.

Is there a way to do the same in Python? Or do I have to write if variable: variable.method()?

Getaway answered 31/10, 2010 at 21:0 Comment(12)
@deamon: first of all i don't know Groovy and i don't want to argue about what i don't know but this feature (after i read about it now) seem no useful to me (and i may be wrong and most of the time i'm) because even if i do something like this variable.?method() yes sure if variable is NULL it's not raising an Error but how do i know that the method was called , because it look to me like a silence error (actually i never needed something like this before) and if if i do something like this a = variable.?method() so after i should do if(a) so ... , did i miss something :)Fruin
I agree with singularity; I don't see how this could be all that useful. It looks like something you would sprinkle over your code to hide errors.Anachronous
@Seth Johnson: can you be more specific, did i say something wrong and if it's about my English is it that bad ? it's not my first language :)Fruin
I also agree with singularity. This has absolutely no real benefit and silently hides errors. I would much rather my program crash at that point then produce odd, hard to track down errors after the real problem occurred.Olav
@all this is really useful when dereferencing properties, so: addressbook?.people?.'tim_yates'?.age would return a value if none of the properties in the chain were null or null if one of them isWaldo
@tim_yates: And then what do you do if the expression evaluates to null? Ignore it? Wouldn't you want to know why any of those values was null, and log the problem? Or would you just show the user a message like "something went wrong somewhere, but we don't know what because we threw away the error", and then hope it doesn't happen again?Anachronous
@Anachronous Whatever you want, it's just better than building up a huge Javaesque chain of if( addressbook != null && addressbook.people != null && addressbook.people.tim_yates != null... etc... You still need to make the same decision, but it's a shorter, prettier journey to get thereWaldo
@tim_yates: No! That's just as wrong. With a proper design, you would have something like addressbook = GetAddressBookFromDatabase(db) and the method throws an exception if it cannot connect / cannot find the addressbook / etc. Then you catch the exception and handle it! And if addressbook == null when you try to use it, it's a bug in your code and it should cause an error, which you catch somewhere way up the callchain (since it should never happen, you don't have to catch it immediately).Anachronous
@Anachronous addressbook was just an example (maybe a badly thought out one), I was just trying to show where the null-safe operator is useful, not start a theological warWaldo
For the sake of comparison, ruby has a similar feature, the Object.try method. It solves the same problem, but the ruby approach is more methody and less operatory.Jett
i think the OP wants the feature more in DTO objects. so trying to output the age of the person doesn't have to be a speghetti of codeRoadbed
This may be added in a future version of Python python.org/dev/peps/pep-0505Vastha
B
20

EDIT 2021:

There is a new package that is sort of a hack featuring exactly this functionality in Python. Here is the repo: https://github.com/paaksing/nullsafe-python

from nullsafe import undefined, _

value = _(variable).method()
assert value is undefined
assert not value
assert value == None

Works with AttributeError and KeyError aswell

dic = {}
assert _(dic)["nah"] is undefined
assert _(dic).nah is undefined

The wrapped object typings will remain effective.


  1. No, there isn't.

  2. But to check for None, you don't write if x:, you write if x is None:. This is an important distinction - x evaluates to False for quite a few values that are propably perfectly valid (most notably 0-equivalent numbers and empty collections), whereas x is None only evaluates to True if the reference x points to the singleton object None.

  3. From personal experience, such an operator would be needed very rarely. Yes, None is sometimes used to indicate no value. But somehow - maybe because idiomatic code returns null objects where sensible or throws exceptions to indicate critical failure - I only get an AttributeError: 'NoneType' object has no attribute '...' twice a month.

  4. I would argue that this might be a misfeature. null has two meanings - "forgot to initialize" and "no data". The first is an error and should throw an exception. The second case usually requires more elaborate handling than "let's just not call this method". When I ask the database/ORM for a UserProfile, it's not there and I get null instead... do I want to silently skip the rest of the method? Or do I really want to (when in "library code") throw an approriate exception (so "the user (code)" knows the user isn't there and can react... or ignore it) or (when I'm coding a specific feature) show a sensible message ("That user doesn't exist, you can't add it to your friend list") to the user?

Brigittebriley answered 31/10, 2010 at 21:47 Comment(4)
Regarding #3 - There are many times you want to check if something is null. For example, any equality comparison on strings ends up being much cleaner to write my_string?.strip().lower() and much easier to read.Zaid
Another Regarding #3 - I'm running into this as well in a situation where I am creating reusable code for process safe and single-threaded cases. If the user (i.e., application) is a single process app, it may not want to access a Pipe as it doesn't care to communicate to another process. Would be nice to write self.pipe?.send(message)Encrimson
@Encrimson #3 already addresses this case by mentioning null objects. In the situation you mention, self.pipe would ideally not be None but instead should be an instance of e.g. DoNothingPipe, which implements a no-op send method.Sourdine
I guess but now you have to write extra code when the point was to be briefEncrimson
A
18

First of all, your options depend on what you'd like the expression to evaluate to if the variable is not dereferencable. I'll assume None is the appropriate result in these examples.

A common idiom for some circumstances in Python uses conditional expressions:

variable.method() if variable is not None else None

While this need may not be widespread, there are circumstances where it would be useful, especially when the references are nested and you would want something like this, where that idiom quickly gets cumbersome.

a?.b?.c?.d

Note that support for a ?. operator for safe deferencing is one of the main topics of PEP 505: None-aware operators \| Python.org. It's current status is "deferred".

Some discussion of it is at:

Alpestrine answered 16/4, 2020 at 18:31 Comment(1)
Additional discussion about PEP 505: discuss.python.org/t/pep-505-status/4612 --- similar functionality for the existing Python: github.com/Syeberman/weakget --- in the context of other languages: en.wikipedia.org/wiki/Safe_navigation_operatorBeanie
A
8

I've used this feature in Groovy, so I won't repeat the blub paradox of other posters.

In Groovy a statement like this

if(possiblyNull?.value){
    ...

Does this in Python

try:
    testVar = possiblyNull.value
except:
    testVar = None
if(testVar): 

It's definitely a cool feature in Groovy, and is helpful in removing syntactical noise. There are a few other bits of syntactical sugar, like the Elvis operator or *, but they do sacrifice legibility as the expense for quick fix symbols (in other words, they're no Pythonic).

Hope that helps :-)

Advocacy answered 10/12, 2010 at 15:59 Comment(1)
Very likely, it isn't implemented like that, but like: testv = possiblyNull.value; if not tesv: return None; return testv.valueAtaxia
F
4

Let's say you're parsing xml, and you're currently writing this:

element = root.find("name")
name = "unnamed" if element is None else name.text

Python 3.8 introduced an assignment expression (the walrus operator) and can now write this pretty elegantly:

name = "unnamed" if (name := root.find("name")) is None else name.text
Forcefeed answered 25/9, 2022 at 8:4 Comment(0)
M
2

An idiom I have seen and used is callable(func) and func(a, b, c) in place of a plain function call (where the return value is not used). If you are trying to use the return value, however, this idiom will yield False if the function is not callable, which may not be what you want. In this case you can use the ternary operator to supply a default value. For example, if the function would return a list that you would iterate over, you could use an empty list as a default value with func(a, b, c) if callable(func) else [].

Misogamy answered 31/10, 2010 at 23:24 Comment(0)
M
0

Answering your question: yeah, you need to write if-esle clause, but use the pythonic way:

variable.method() if variable else None
Milano answered 3/6, 2023 at 13:26 Comment(0)
V
0

This might be very hacky, but for my purposes this has cut out a bit of code:

from typing import Callable
def ch(func: Callable, _default = None):
   try:
      return func()
   except (AttributeError, KeyError):
     return _default

Use it by wrapping your call in a lambda:

val = ch(lambda: obj['a']['b'].c[0].d(), 'default value')
Vacancy answered 15/3 at 7:6 Comment(0)
I
-1

Python has no such thing. This is because there is no null pointer in Python. Well, there is the special None value, which is often used in situations, where it represents "no value". But that is just a convention. None is a value/object like all others. And since there is no null, there is no operator to deal with it.

Idaidae answered 31/10, 2010 at 21:3 Comment(7)
I agree, but None is essentially the same in this case. Instead of a NullPointerException I would get an AttributeError, but I could not call a method on None which would be present in usual object such as a list.Getaway
The point is, that None is not "unusual" at all. It is an object with a proper class, has methods, etc. There is nothing in the language which would make it special in any way (besides, that it is being used to represent the "nothing interesting here" value). So, what would the hypothetical ?. operator test for? For None specifically? What about other libraries which use other markers (e.g., NOT_FOUND in PEAK)?Idaidae
None is great for marking optional function arguments and various other things, but using it (or something like NOT_FOUND) as a "not found" result or other special return value should be avoided. Using exceptions for these kinds of exceptional conditions is better because it tends to simplify code using those functions (instead of checking every return value, you can have an except clause, or just let the exception bubble up).Anachronous
@adw: One could argue, that exceptions should be used just for exceptional situations, and a value not being present in a dict, say, might or might not be exceptional depending on the circumstances, but let's not get into that. I was merely pointing out, that there actually are such uses for objects different from None and there is nothing in the language except convention, which would favour None as target for a special ?. operator.Idaidae
this is mainly an opinion piece. DB nulls map to none, uninitialized objects map to none, throws exception same way and usage as null, acts like null, quacks like null, is null.Abelmosk
What the op means is if there is an operator like Groovy's ?.. This question is valid regardless of the null nature.Jeanmariejeanna
The question is: what is ?. supposed to do? In a language with dedicated null value, x?.y is roughly equivalent to x.y if x is not the_magic_null_value else the_magic_null_value (in pythonesque syntax). But if the language has no magic_null_value (and whether None qualifies is up to debate as can be seen in the PEP discussing this feature) -- what should the ?. actually do?Idaidae

© 2022 - 2024 — McMap. All rights reserved.