2

Pythonic way of returning and checking method execution

I am currently using golang style of coding in python code, decided to move pythonic way

example:

import sys
from typing import Union, Tuple

def get_int_list(list_data: list) -> Tuple[Union[list, None], bool]:
    try:
        return [int(element) for element in list_data], True
    except ValueError:
        print("Failed to convert int elements list")
        return None, False

my_list = ["1", "2", "a"]

int_list, status = get_int_list(my_list)
if not status:
    sys.exit(1)
sys.exit(0)

I read in python docs pythonic way of doing is raising exceptions.

Can anyone provide me example for above method?

Vidya
  • 179
  • 1
  • 2
  • 12
  • 8
    You could smply remove the try/except. NOTE your current code is very dangerous simply because it hides every possible exception; if you _have_ to use try/except you should make the `except` filter for the __specific__ exception you are prepared to ignore, so other exceptions happen and give you proper errors. Believe me; code like yours has hidden horrible errors and created all sorts of debugging problems. – DisappointedByUnaccountableMod Jul 10 '21 at 21:44
  • I took it for example – Vidya Jul 10 '21 at 21:50
  • 2
    It’s not a good example. – DisappointedByUnaccountableMod Jul 10 '21 at 21:50
  • 1
    Do you _expect_ some elements on the list to be non-numeric? If not, just let the exception happen, do not handle it. Also, return an empty list instead of `None` if you still want to handle the exception. It is preferred that a function always returns a value of the same type. – DYZ Jul 10 '21 at 22:01
  • @barny, now it makes sense, I just added value error – Vidya Jul 10 '21 at 22:07
  • @DYZ, no in case, user passed, non numeric cases, I need to catch it and perform other actions – Vidya Jul 10 '21 at 22:08
  • 1
    @DYZ That might make it ambiguous whether the function succeeded (and the original argument was empty) or failed (with a non-empty list containing non-integers). I'd want to know more about the intended use-case before deciding between `None` and `[]` (though personally I would avoid the issue by raising an exception, not returning anything, in the event of an error; let the caller decide how to proceed if the argument wasn't valid). – chepner Jul 10 '21 at 22:09
  • If an empty list could be a valid result for valid input then you need to distinguish the case of invalid input and returning None might be one way to do that; an exception would be another way. – DisappointedByUnaccountableMod Jul 10 '21 at 22:10
  • Checking each of the values individually (i.e. NOT using a list comprehension) would allowing informing the user/caller that a specific value isn’t valid (and perhaps printing the value) which could be a lot more user-friendly for working out what went wrong than a very blunt ‘nah, doesn’t compute’ result from your function. – DisappointedByUnaccountableMod Jul 10 '21 at 22:13
  • @chepner There is the second part of the return value that tells whether the function succeeded or failed. – DYZ Jul 10 '21 at 22:15
  • 1
    Yes, and that's the part we're trying to get rid of. Return a useful value, or raise an exception. The "right" return type (`list[int]` with or without the possibility of a documented exception, `Optional[list]`, etc) is something we can't determine without knowing more about how the function is intended to be used. – chepner Jul 10 '21 at 22:26
  • Just remove the try except. – juanpa.arrivillaga Jul 10 '21 at 22:51

1 Answers1

2

Personally, I would simplify this greatly.

The annotations in this case are not doing much for you.

With int() you really only have two possible errors from in-memory data:

  1. ValueError - from trying to convert something that cannot be converted such as int('')

  2. TypeError - from trying to convert something other than a string or numeric type (like int(1.23)) such as int({'1':'2'}) or int(['1','2']) would be TypeErrors.

These are the only two exceptions you should handle in your function given its defined scope. If you try and broadly handle more than you are prepared to handle you risk masking many other exceptions that are better handled by Python, the OS, or the part of your program that called this function.

It is also more common in Python to return the item if successful and None if not. Be sure to explicitly test is None vs just testing truth of false of the return. A return of None or 0 or [] are all False but only None is None. (Although the way you are going is seen in Python, it is not super common IMHO.)

Simplified:

import sys

def get_int_list(list_data):
    try:
        return [int(element) for element in list_data]
    # limit 'except' to:
    # 1) What is a likely exception in THIS function directly from 'try' and 
    # 2) what you are prepared to handle
    # Other exceptions should be handled by caller, Python or OS
    except (ValueError, TypeError) as e:
        print("Failed to convert int elements list")
        print(e)
        # options now are: 
        #  1) return None 
        #  2) return []
        #  3) exit program here
        #  4) set a flag by returning a consistent data structure or object
        # which you choose is based on how the function is called
        return None

# you would handle Exceptions building the list HERE - not in the function  
my_list = ["1", "2", "3"]

nums=get_int_list(my_list)
if nums is None:
    # failure -- exit
    sys.exit(1)
#success    
print(nums) 
sys.exit(0)

There are other ways of course, such using a decorator or a User Defined Exception but these are used when you have many more possible errors to deal with.

dawg
  • 98,345
  • 23
  • 131
  • 206
  • In your example, you handled exceptions inside method, return status, other Option is Raise exception inside method and let caller method check the exception , In python which one is the good practice? – Vidya Jul 13 '21 at 02:02
  • Good question! It really depends on how you will react to the exception and the scope of the program. If the error is fixable or skip-able (such as a unknown character in a codec or user inputs a bad input or a KeyError in a mapping or overflow/underflow in math) these are usually not fatal errors and the programmer reacts in the exception to fix the issue. If the reaction is fatal anyway -- just let Python die for you. Inside or outside method just depends where the problem is potentially generated and could be fixed in the exception. – dawg Jul 13 '21 at 11:39
  • 1
    Got it, If it is fatal error, just terminate inside method itself, no need of handle this by caller method, In case error is fixable or skip-able just raise exception inside method and let caller method handles it or inside method only can handle depends on use case – Vidya Jul 13 '21 at 23:25