Assuming that my list has only str
and None
, and I want to check assign lowered strings into another variable but if it's uppered or None, it should be rendered as None.
The code to checking for None
and str.isupper()
works:
for i in [None,'foo',None,'FOO',None,'bar']:
x = None if i is None or i.isupper() else i
print x
but the negated condition didn't work:
for i in [None,'foo',None,'FOO',None,'bar']:
x = i if i is not None or i.isupper() else None
print x
It returns AttributeError
:
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
AttributeError: 'NoneType' object has no attribute 'isupper'
You are testing if i is not None or i.isupper()
. This doesn't work when i
is None
:
(i is not None) or (i.isupper())
is evaluated lazily; first the left is evaluated, and only if False
is the second argument evaluated. The first is only False
if i
is None
, so the right-hand expression is only ever evaluated as None.isupper()
.
Use and
instead, and negate the isupper()
test:
x = i if i is not None and not i.isupper() else None
The above expression is the proper logical inversion of i is None or i.isupper()
.
Alternatively use not (..)
around your original expression:
x = i if not (i is None or i.isupper()) else None
Because you are using a regular for
loop, there is no need to use a conditional expression here; the following, assigning to the existing loop variable i
, suffices:
for i in [None,'foo',None,'FOO',None,'bar']:
if i and i.isupper():
i = None
print i