Reputation: 697
I have a function where sometimes a parameter can be none, and I would like to compare that with another object. However, if I am trying to call an object property, my script will throw an exception on None, even if both objects are None (see example below).
def do_animals_make_same_sound(first_animal, second_animal):
if first_animal.sound = second_animal.sound:
print('Yes they do!')
But if both animals are None, it throws an exception when instead I want it to print('Yes they do!'), but it seems I have to write a really ugly If statement:
def do_animals_make_same_sound(first_animal, second_animal):
if (first_animal is None and second_animal is None) or (first_animal is not None and first_animal.sound == second_animal.sound):
print('Yes they do!')
Is there a better way to do this?
Upvotes: 0
Views: 1615
Reputation: 16624
following code is clearer IMHO:
def do_animals_make_same_sound(first_animal, second_animal):
# early return if one of the two animals is missing, ensure both exist
if not (first_animal and second_animal):
return
if first_animal.sound == second_animal.sound:
print('Yes they do!')
Upvotes: 2
Reputation: 12391
If it's a general enough pattern, I'd use a decorator to catch the None case specifically and process it. That keeps the logic out of the function. But you need to define exactly what None means here... it's a little odd that you can pass None for both, but it's not legal to just pass None for one of them. In any case, a decorator is a great way to abstract out some common logic in a clean way...
def NoNone(f):
@functools.wraps(f)
def _no_none_func(*args, **kwargs):
if args[0] == None and args[1] == None:
print('Both are None')
return
return f(*args)
return _no_none_func
@NoNone
def do_animals_make_same_sound(first_animal, second_animal):
if first_animal.sound == second_animal.sound:
print('Yes they do!')
else:
print("No they don't!")
Upvotes: 0
Reputation: 107
I think first you have to understand what's the meaning if one of the objects is None. There are basically three scenarios:
One or both objects are None
One or both objects does not have sound
attribute
Both have sound
attribute
For #1, I'm assuming it should throw an error as there is really no comparison. What your code does is print "Yes they do" if both objects are None.
For #2, you can use what ShadowRanger suggests, If both objects have None
as sound
property, and your think it is a normal behavior, then use ShadowRanger's solution.
For #3, just do your normal comparison
def do_animals_make_same_sound(first_animal, second_animal):
if not first_animal or not second_animal:
print("One of the objects is None")
elif getattr(first_animal, 'sound', None) == getattr(second_animal, 'sound', None):
print("Yes, they do!")
Upvotes: 0
Reputation: 155418
It's not great, but one approach can be to use getattr
with a default, so None
(and anything else without the desired attribute) behaves as if it had the default as the value of its attribute. For example:
if first_animal.sound == second_animal.sound:
can become:
if getattr(first_animal, 'sound', None) == getattr(second_animal, 'sound', None):
I don't actually recommend this, as it silently ignores errors. In real code, I'd almost always let the AttributeError
propagate; there is no reasonable scenario in which I'd consider None
an acceptable stand-in for "something" where "something" has specific behaviors or attributes; if the caller is passing None
, that's almost certainly an error that should not be silently ignored.
Upvotes: 0