-2

Edit: For the people marking it as duplicate with logical operators, I found the link Python Logical Operators and the answers there to be rather vague and had to deep dive into it before finding the "or" answer. So while the issue here is that Python throws None, False and 0 together, great, but here we want to make a distinction between the earlier two and an actual integer or float value. That post really doesn't help my question here except for explaining some of the basic behaviour and doesn't address the "how can we handle this specific situation in the most pythonic way". //

I've ran into some unexpected behaviour with converting integers using the int(... or ...). I didn't notice it before, because I usually use something like:

int(value or 0)

Which always worked fine, but when I change the or 0 to another value and we enter 0, I get something I didn't expect. The example is int, but same goes for float.

In [88]: int(None or 1)
Out[88]: 1

In [89]: int(2 or 1)
Out[89]: 2

In [90]: int(0 or 1)
Out[90]: 1

In [91]: int(0)
Out[91]: 0

Edit: I would expect [90] to do the same as [91], but obviously it doesn't. My question here is: why does it do that behaviour and how can I handle this in a Pythonic way without having to resort to try excepts.

Is this because it evaluates like a boolean and 0 therefore returns the or value? What is common practice to handle this? Because this is a very common thing in our program and to use try except will create some ugly, not very readable code... Using python 2.7 here.

This is my current workaround to get my expected behaviour:

try:
    value = int(value)
except (TypeError, ValueError) as e:
    value = 1

Kind regards,

Carst

Edit 2: as the underlying mechanism is clear now, the help I'm really looking for is about how to handle this situation. As stated in the comments, I mostly do this to handle Nones. Another way to handle this would be:

if value is None:
    value = 1
else:
    value = int(value)

What I'm trying to understand really, is how to handle Nones in a way that does not transform the code into an endless parade of if/else statements or write my own function that does it (because to me None handling seems something that should be common?).

Community
  • 1
  • 1
Carst
  • 1,614
  • 3
  • 17
  • 28
  • 2
    What behaviour are you actually expecting? I'm not sure where your confusion is. – Daniel Roseman Jan 29 '15 at 23:10
  • Hi Daniel, I've added some explanation, but basically I had expected int(0 or 1) to deliver 0 and not 1. – Carst Jan 29 '15 at 23:40
  • 1
    Congratulations on the deep dive, maybe you learned something. You shouldn't form expectations about how something works without reading up on it. And you haven't really explained the "situation" you're trying to handle, so nobody can really give you an answer until you clarify. – Mark Ransom Jan 29 '15 at 23:47
  • 1
    Do you really need to distinguish any value that evaluates as `False`, or just `None`? – Mark Ransom Jan 29 '15 at 23:50
  • Mark, the original question already contained "Is this because it evaluates like a boolean and 0 therefore returns the or value", which makes pointing to an answer stating that not that much of an added value, especially as it isn't an answer to the actual problem described here but just a description of the underlying issue. Sorry if I sound a bit slighted here, but it's because I ended up with "you don't read" duplicate downvotes while no one addresses the actual question I had :( – Carst Jan 29 '15 at 23:52
  • The first value inside the function's parentheses is evaluated as a boolean because of the `or`. The result of the `or` is **then** used as the function's argument. What you're writing is not `int(0) or int(1)`, it's `if 0: int(0) else: int(1)`, or maybe more clearly `arg = 0 or 1; int(arg)` – jscs Jan 29 '15 at 23:56
  • It's 99% None handling (False should not occur in the context of the program here) – Carst Jan 29 '15 at 23:56
  • Where are you getting your `value` from? It's often better to have your earlier code raise an exception for you to catch, rather than returning `None` or some other "error" value that you have to check for in your later code. – Blckknght Jan 29 '15 at 23:57
  • 1
    Related: [Checking whether a variable is an integer or not](http://stackoverflow.com/q/3501382), (although it's not clear where your "value" is coming from) [Check if input is a number given that input always returns string](http://stackoverflow.com/q/5424716), [Python check if a string represents an int without using try except](http://stackoverflow.com/q/1265665) – jscs Jan 30 '15 at 00:00
  • Thanks Josh! So the common approach then is to write a function I guess. – Carst Jan 30 '15 at 00:05

1 Answers1

5

The way I read your question, you're looking for the most pythonic way to handle types that fail to typecast successfully to int().

For me, I would generally catch the exception exactly as you have in your question. It can get a bit verbose/repetitive, so if you are doing it a lot, you can always have a utility typecast function that abstracts away your try/catch blocks. Something such as:

def safely_int(value, default=0):
    try:
        return int(value)
    except (TypeError, ValueError):
        return default

This does a nice job of catching all weird/unexpected cases (generally speaking, types/values that can't be converted to integers).

I know you say you don't want to use exception handling, but I honestly think that's the best solution here as it will catch cases you weren't expecting. And feeding code unexpected inputs is a great way to get buggy/unexpected behavior unless you're careful.

bmhkim
  • 754
  • 5
  • 16