try..except or type() or isinstance()?

Peter Otten __peter__ at web.de
Sat Aug 15 02:33:58 EDT 2020


Chris Angelico wrote:

> On Sat, Aug 15, 2020 at 3:36 PM Manfred Lotz <ml_news at posteo.de> wrote:
>>
>> I have an object which I could initialize providind an int or a str.
>>
>> I am not sure which of the following is best to use
>>  - try/except
>>  - if type(int)...
>>  - if isinstance(v, int)
>>
>> Here a minimal example
>>
>> def get_id(fromname):
>>     # do something with `fromname`
>>     return 0
>>
>> def get_name(fromid):
>>     # do something with `fromid`
>>     return "something"
>>
>> """ For O1, O2, O3: self.myid is int
>>                                         self.name is str
>> """
>> class O1:
>>     def __init__(self, val):
>>         try:
>>             self.myid = int(val)
>>             self.name = get_name(self.myid)
>>         except:
>>             self.myid = get_id(val)
>>             self.name = val
> 
> Don't use a bare "except" - use "except ValueError" instead. But
> otherwise, this is a perfectly reasonable way to say "anything that
> can be interpreted as an integer will be".
> 
>> class O2:
>>     def __init__(self, val):
>>         if type(val) == int:
>>             self.myid = val
>>             self.name = get_name(self.myid)
>>         else:
>>             self.myid = get_id(val)
>>             self.name = val
> 
> Nope, don't do this. It's strictly worse than O3.
> 
>> class O3:
>>     def __init__(self, val):
>>         if isinstance(val, int):
>>             self.myid = val
>>             self.name = get_name(self.myid)
>>         else:
>>             self.myid = get_id(val)
>>             self.name = val
> 
> This is a perfectly reasonable way to say "integers will be treated as
> IDs". Note that O1 and O3 are very different semantically; O1 will
> treat the string "7" as an ID, but O3 will treat it as a name.
> 
> Here's an even better way:
> 
> class O4:
>     def __init__(self, id):
>         self.myid = id
>         self.name = get_name(id)
>     @classmethod
>     def from_name(cls, name):
>         return cls(get_id(name))
> 
> This makes the ID the main way you'd do things, and a name lookup as
> an alternate constructor. Very good pattern, reliable, easy to use.

Yet another way: keyword arguments:

class O5:
    def __init__(self, *, id=None, name=None):
        if name is None:
            assert id is not None
            name = get_name(id)
        else:
            assert id is None
            id = get_id(name)
        self.id = id
        self.name = name



More information about the Python-list mailing list