0

Is there a way to simply my code below to not use a bunch of if-then-else?

if isinstance(t, int): v = 10
elif isinstance(t, bool): v = 20
elif isinstance(t, string): v = 30
...
JRR
  • 6,014
  • 6
  • 39
  • 59
  • 4
    `{int: 10, bool: 20, str: 30}[type(t)]`. – jonrsharpe Mar 22 '20 at 19:45
  • 3
    Those solutions will not work for derived types. They are equivalent to `type(t) == x`, not `isinstance(t, x)`. – Tom Karzes Mar 22 '20 at 19:46
  • wow. Does that work? (ha.) I would have thought you would have to use some Class names or such. That is clever. – AirSquid Mar 22 '20 at 19:47
  • @TomKarzes yes, that's true, it remains to be seen whether that meets the OP's needs anyway. – jonrsharpe Mar 22 '20 at 19:47
  • 1
    Dealing with derived types is exactly the reason why I didn't use dictionary mapping in the first case. – JRR Mar 22 '20 at 19:48
  • In what context does the value of `v` depend on the type of `t` in the first place? This is somewhat of an anti-pattern. – chepner Mar 22 '20 at 19:51
  • @chepner this is part of a code analyzer. – JRR Mar 22 '20 at 19:52
  • @JRR So you tried to achieve a `switch` statement but it has allready an answer here: https://stackoverflow.com/questions/60208/replacements-for-switch-statement-in-python – johannchopin Mar 22 '20 at 19:53
  • Everyone suggesting the standard switch-statement idioms: consider how you would deal with `t` whose type is a subclass of a given type. – chepner Mar 22 '20 at 19:55
  • 2
    BTW, the code you have in the question will never set `v = 20`, because `bool` is a subclass of `int`: `isinstance(True, int)` is itself `True`, so `v` is set to 10. – chepner Mar 22 '20 at 19:57

4 Answers4

0

You can use a dict that maps your types to your values, like this:

TYPE_TO_VALUE = {
  "int": 10,
  "bool": 20,
  "string": 30
}

and then assign your value like this v = TYPE_TO_VALUE[type(t).__name__]

hinosxz
  • 119
  • 5
0

I would go with list comprehension (this way you will cover the case for bool).

map_={int: 10, bool: 20, str: 30}
type_map=lambda x: [map_.get(el) for el in map_.keys() if isinstance(x, el)]
t=4
print(type_map(t))
# [10]
t=True
print(type_map(t))
# [10, 20]
t="txt"
print(type_map(t))
# [30]
t=5.3
print(type_map(t))
# []

Alternatively to cut it down to the relevant part (IMO):

map_={bool: 20, int: 10, str: 30} # notice order change
type_map=lambda x: ([map_.get(el) for el in map_.keys() if isinstance(x, el)]+[None])[0]
t=4
print(type_map(t))
# 10
t=True
print(type_map(t))
# 20
t="txt"
print(type_map(t))
# 30
t=6.7
print(type_map(t))
# None
Grzegorz Skibinski
  • 12,624
  • 2
  • 11
  • 34
0

One approach may be to do arithmetic with the boolean isinstance result.

def map_instance( t ):
    return isinstance( t, int ) * 10 + 
           isinstance( t , bool ) * 10 +  # bool returns 20 as it's int and bool.
           isinstance( t, str ) * 30  )

I think this can handle subclassing.

Tls Chris
  • 3,564
  • 1
  • 9
  • 24
0

two alternatives, I would choose first one (the function)

def get_v(my_object):
    my_types = {bool:20, int:10, str:30}
    for my_type, value in my_types.items():
        if isinstance(my_object, my_type): # first seen wins
            return value

class Spam(str):
    pass

s = Spam()
v = get_v(s)
print(v)


# ugly alternative
my_types = {'bool':20, 'int':10, 'str':30}
print(my_types.get(s.__class__.__bases__[0].__name__)) # in multiple inheritance it will take first one

If the dict is order-preserving (e.g. 3.7+) and bool is before int, first seen wins

buran
  • 13,682
  • 10
  • 36
  • 61
  • It would be possible to use the loop to accumulate the calculation. i.e. `result += isinstance( my_object, my_type) * value`. but I certainly prefer the loop – Tls Chris Mar 22 '20 at 20:36
  • @TlsChris, the problem with the approach to accumulate is (i) if you have multiple inheritance and (ii) it doesn't break out of the loop early, but calculate for all dict elements – buran Mar 22 '20 at 20:59