Suppose you have a function Car.find_owner_from_plate_number(plate_number) that will raise an Exception if plate is unknown and return an Owner object if plate number exists.
Now, you do not need Owner information in your script, just to know if plate number exists (ie no exception raised)
owner = Car.find_owner_from_plate_number('ABC123')
_ = Car.find_owner_from_plate_number('ABC123')
Car.fund_owner_from_plate_number('ABC123')
With first, IDE will complain that owner is not used afterwards
Second is ok since _ is a global variable, but will assign memory in line with Owner's size
Third should also do the job, cherry on the cake without consuming memory if I'm correct.
What's the best way / more pythonic between 2nd and 3rd way? I ask because I often see 2nd way but I would be tempted to say 3rd is best.
CodePudding user response:
As a rule, if you don't care about the returned value of a function, then don't assign it to anything, and it will vanish once you're done with it. This applies not only in python, but also in Java and other garbage-collected languages. In C/C you need to be more careful with memory management to make sure you're not leaving any memory leaks, but they do allow this behavior, and if you didn't explicitly use malloc()
it's usually fine.
As for whether it's good design for find_owner_from_plate_number()
to be built this way: it's okay but not great. The optimal design (more idiomatic in Java, in my experience, less so in python since exceptions are more integrated into control flow in general) would probably be
- if plate is present, return an
Owner
- if plate is not present, return
None
(or the language's equivalentnull
value, if not writing python)
and save exception-throwing for actual error behavior that isn't expected to happen during normal use (invalid license plate format, could not connect to database, etc.). In general, try to avoid using thrown exceptions for control flow, if you can avoid it.
That said, I've seen both patterns in use at various points, and using exception behavior in this way isn't unheard of.
CodePudding user response:
TLDR: If you do not need the return value, do not assign it at all. If you need some parts of the return value, assign the rest to _
.
Python explicitly has an "expression statement" that solely exists to evaluate an expression and ignore its return value. Since this is the simplest way to ignore return values, it should be the preferred approach whenever applicable.
Car.find_owner('ABC123')
When the return value should be ignored only partially, an "assignment statement" is required to assign the parts of interest. In this case, the name _
is idiomatic to mean "unused" for the parts that must be assigned to something but are not of interest; it is often used as *_
¹ to ignore arbitrary many items.
# we only want the owner and ignore the model
owner, _ = Car.find_owner_and_model('ABC123')
# ignore everything but the first item
owner, *_ = Car.find_owner_model_make_and_other_stuff('ABC123')
Note that _
is still a fully functional variable which keeps its assigned object alive until the end of the scope or deletion. This is not usually an issue when used in short-lived functions, but may require explicit cleanup when used for large objects at global scope.
¹ The *
is commonly known as the "star" or "splat" operator. It denotes iterable packing/unpacking.