Question about nested or

I am in the process of programming a simple org-mode diary which invokes emacs on a file in the form ddmmyyyy.org. It currently works. I changed a function which got which operation you wished (edit export del search quit) to error check the input. It is as follows:

def getOperation():
operation=input(“\nOperation: edit export del search quit\n\n”)
if operation.lower()==‘edit’ or operation.lower()==‘export’ or operation.lower()==‘del’ or operation.lower()==‘search’ or operation.lower()==‘quit’:
return(operation)
else:
maketitle()
getOperation()

when it returns “operation”, it hits:

if operation.lower()!=‘search’ and operation.lower()!=‘quit’ and operation.lower()!=‘’:
getdiary()
(which gets the date you wish to work with and invokes emacs)
the above works fine, my original code (which did not work (see following comments):

def getOperation():
operation=input(“\nOperation: edit export del search quit\n\n”)
if not operation.lower()==‘edit’ or operation.lower()==‘export’ or operation.lower()==‘del’ or operation.lower()==‘search’ or operation.lower()==‘quit’:
maketitle()
getOperation()
else:
return(operation)

when it returns “operation”, it hits:

if operation.lower()!=‘search’ and operation.lower()!=‘quit’ and operation.lower()!=‘’:
getdiary()

The top code works and is current. The lower variant, which adds nothing but the not and reverses the if / else
1.) only works if the operation is “edit”. All the other terms blank the screen and start the operation choice over ( the “else” part of the top code) AND only if the “edit” is the first try. If you enter “search”, “delete”, etc it blanks the screen and wants you to reenter your choice. If you enter “edit” on the first choice the diary functions. If you enter anything else and then “edit” on the second if you get an error like "NoneType does not have property “lower”.

Like I said, top code works, but “WT ______”. I am at a loss to explain the behavior.
Thanks
Swgeek

By Norm via Discussions on Python.org at 15Sep2022 22:43:

def getOperation():
operation=input(“\nOperation: edit export del search quit\n\n”)
if not operation.lower()==‘edit’ or operation.lower()==‘export’ or operation.lower()==‘del’ or operation.lower()==‘search’ or operation.lower()==‘quit’:

You’re encountering a precedence effect. When you combine multiple
operators (which can be anything: logic, arithmetic, etc) Python must
decide which values are applied to each operator.

Python’s full table is here:
https://docs.python.org/3/reference/expressions.html#operator-precedence

Example:

 x = 5 + 3 * 8

Python follows the normal arithmetic default operator precedence, which
binds multiplication (*) more tightly than addition (+). So this
means:

 x = 5 + ( 3 * 8 )

which you could write out that way in order to be unambiguous (to the
reader of the code) but you get this for free without the brackets.

If you wanted 5+3 multiplied by 8 you would need to write:

 x = ( 5 + 3 ) * 8

to force Python to do the addition before the multiplication, because
that is not the default precedence for these operators. Here, the
brackets are required.

The same effect applies with logic: the logical operators (and, or,
not) have default precedence: not then and then or, analogous to
arithmetic - (negation), * (multiplication, + (addition).

So putting in brackets to your if-statement condition to make things
clear, Python evaluates it like this:

 ( not operation.lower()=='edit' )
 or ( operation.lower()=='export' )
 or ( operation.lower()=='del' )
 or ( operation.lower()=='search' )
 or ( operation.lower()=='quit' )

whereas what I expect you wanted was:

 not (
   operation.lower()=='edit' )
   or ( operation.lower()=='export' )
   or ( operation.lower()=='del' )
   or ( operation.lower()=='search' )
   or ( operation.lower()=='quit' )
 )

which, as with arithmetic, you need to use brakcets to obtain:

 if not (
     operation.lower()=='edit' )
     or ( operation.lower()=='export' )
     or ( operation.lower()=='del' )
     or ( operation.lower()=='search' )
     or ( operation.lower()=='quit' )
 ):

BTW, I’m sure you’ve noticed that the more operations you supportthe
more cumbersome your code gets. It is more normal to write stuff like
this:

 op_lc = operation.lower()  # do this just once
 if op_lc == 'edit':
     ... do the edit operation ...
 elif op_lc == 'export':
     ... do the export operation ...
 elif ... for each other operation ...
 else:
     print("unhandled operation!")

Cheers,
Cameron Simpson cs@cskk.id.au

Please use three backticks before and after your code.

I’m finding I can’t follow your description of the code. I suspect that the not is only associated with the first item, but you might be expecting it to reverse the sense of the entire line. A small, runnable example would be helpful.

Not directly addressing the difference, but an improvement I would recommend is to modify how you do the comparison. Something like these comparisons would be more understandable and shorter (so less chances for typing mistakes).

commands = ('edit', 'export', 'del', 'search', 'quit')
if operation.lower() in commands:
    ...
# later
if operation.lower() not in ('search', 'quit', ''):
    ....