Alternatives for checking if something is not None in Python I recently bumped into dry-python/returns, which seems like a very useful project. In the project description, the authors describe a snippet which can be written with their Maybe container in a better way (according to the authors):
The original code is:
if user is not None:
     balance = user.get_balance()
     if balance is not None:
         credit = balance.credit_amount()
         if credit is not None and credit &gt; 0:
             discount_program = choose_discount(credit)

And the improved version is:
discount_program: Maybe['DiscountProgram'] = Maybe.from_optional(
    user,
).bind_optional(  
    lambda real_user: real_user.get_balance(),
).bind_optional(
    lambda balance: balance.credit_amount(),
).bind_optional(
    lambda credit: choose_discount(credit) if credit &gt; 0 else None,
)

This is a legitimate solution. However, I would write the first code as:
if user:
   balance = user.get_balance()
   if balance:
      credit = balance.credit_amount()
      if credit &gt; 0:
          discount_program = choose_discount(credit)

Being a long time pylint user that the style that is recommended.
Honestly, I've never given it much thought, and now that I saw the first
code, I started asking myself if I should give it some thought.
The most dangerous place IMHO, in my version is:
if credit &gt; 0:

Since None will raise here a TypeError. However, the original line can be written as:
if credit and credit &gt; 0:
   discount_program = choose_discount(credit)

My favorite style for re-writing this block would be very LISPy. I use getattr with a default value which is callable (a lambda which returns None). This very similar to what the example in return does, but without a 3rd party library:
choose_discount(
   getattr(
      getattr(user, 'get_balance', lambda: None)(),
      'credit_amount', lambda: 0)())

Which I think would not be like by many because it's very unusual. Also, for this to work properly, I would write choose_discount as:
def  choose_discount(credit):
    if credit:
        return &quot;Your discount choosing ...&quot;
    else:
        return &quot;No discount&quot;

If allowed to use a newer version of Python, I would probably use the walrus operator here:
if (credit := getattr(getattr(user, 'get_balance', lambda: None)(), 'credit_amount', lambda: 0)()) &gt; 0:
    discount_program = choose_discount(credit)

This style strongly assume that credit_amount always returns a type which can be numerically compared ... Hence, the caveat I mentioned earlier, with credit being a None is implicitly handled.
I have a few questions regarding this.

Can you point out possible caveats in my style?
Could I be missing out something terrible, and I should look for is not None
explicitly?
Is the functional style legitimate here or should I absolutely avoid it?
I thought the readme on dry-python/return did a good job of explaining the usage of the Maybe type.
The key to understanding Maybe is that it represents computations which might &quot;go wrong&quot; by not returning a value.
The bind method (bind_optional) in this library allows you to write code without having to worry about the result of the computation at any intermediate step, it handles the case where one of the computations returns None.
In the code you use to demonstrate the conditionals, you still have to handle the cases of None via an if statement, the use of if not None vs if Some isn't relevant. As a hypothetical consider a computation with many level of optional results from computation, you'd still have to implement the same level of nesting.
In the example from the documentation
user: Optional[User]

# Type hint here is optional, it only helps the reader here:
discount_program: Maybe['DiscountProgram'] = Maybe.from_optional(
    user,
).bind_optional(  # This won't be called if `user is None`
    lambda real_user: real_user.get_balance(),
).bind_optional(  # This won't be called if `real_user.get_balance()` is None
    lambda balance: balance.credit_amount(),
).bind_optional(  # And so on!
    lambda credit: choose_discount(credit) if credit &gt; 0 else None,
)

the comments in the code say the same. You can imagine a library using a symbolic function name &gt;&gt;= with infix operators allowing the above code to be rewritten as

credit = user &gt;&gt;= (lambda real_user: real_user.get_balance())
              &gt;&gt;= (lambda balance: balance.credit_amount()) 
              &gt;&gt;= (lambda credit: choose_discount(credit) if credit &gt; 0 else None)


IMO, you approach to utilizing getattr is more akin to programming in a &quot;duck typing&quot; style than functional.
I used the haskell wiki's description of the Maybe monad if you want to dig in further.