Skip to main content
6 of 7
added 591 characters in body
C.Nivs
  • 3.1k
  • 14
  • 32

You don't need the loop. Once i > 0, the if statement will always be False:

x = 'abba'
x[0::1] == x[::-1]
True

x[1::1] == x[::-1]
False

x[1::1]
'bba'

So drop the iteration. Besides, the immediate return won't allow for iteration anyways, a second iteration will never be reached:

def is_palindrome(x):
    x = x.lower()

    if x[::-1] == x:
        return f'{x} is a palindrome'
    else:
        return f'{x} is not a palindrome'

You don't need the parentheses with return, and I'd use f-strings instead of % formatting if you're on python 3.5+.

If you are adding the possibilities of spaces/punctuation, you could change x to only include alphabet chars:

def is_palindrome(x):
    # this will produce only alphabet lowercase chars
    # and join them into a string, though I'm not sure if
    # this is a use case for you
    x = ''.join(filter(str.isalpha, x.lower()))

    if x[::-1] == x:
        return f'{x} is a palindrome'
    else:
        return f'{x} is not a palindrome'

To show what that join statement does:

# maybe this should be a palindrome if you ignore the
# non-alpha chars including spaces
x = "ABC1234CB a."

y = ''.join(filter(str.isalpha, x.lower()))
'abccba'

Edit

To address concerns in the comments, if you wanted to offer options into what kind of filtering you want to provide, you could use a dictionary to act as a mapping:

from functools import partial

def filter_input(x, lower=False, filter_type='nofilter'):
    """
    Parameter 'filter_type' defaults to pass-through, but you can
    provide options such as 'alphanum', 'nospace' (to just get rid of 
    spaces), and 'alpha'

    lower is a bool that checks if you want the string to be 
    case-insensitive
    """
    filters = {
        'alpha': partial(filter, str.isalpha),
        'alphanum': partial(filter, str.isalnum),
        'nospace': partial(filter, lambda char: not char.isspace()),
        'nofilter': partial(map, lambda char: char) # this is just a pass-through 
    }

    # raise this exception just so the use is more clear to the user
    # what is expected
    try:
        f = filters[filter_type]
    except KeyError as e:
        raise ValueError(
            f"Invalid filter_type, choose one of {'\n'.join(filters)}"
        ) from e
    
    return ''.join(filter_type(
        input_str.lower() if lower else input_str
    ))


def is_palindrome(x):
    """
    Take an input string and return if it is a palindrome
    """

    # you can just check the first half of the string
    # against the last half reversed, rather than the entire string 
    midpoint = len(x) // 2
    if len(x) % 2: # even length
        a = b = midpoint
    else:
        a, b = midpoint + 1, midpoint
 
    return x[:a] == x[b::-1]

# which can be used like
mystr = 'abc123 321 BCA'

is_palindrome(mystr)
False

# won't lower-case and strips out non-alpha chars
is_palindrome(filter_input(mystr, filter_type='alpha'))
False

will lower-case and strips out spaces
is_palindrome(filter_input(mystr, lower=True, filter_type='nospaces'))
True

partial will bind arguments to a function and return a new callable. As a small example:

def f(a):
    return a

g = partial(f, 1)
f(2)
2

g()
1

Which is helpful for taking a function that takes many arguments and returning one that takes fewer arguments.

Credit to Toby Speight for returning bool type, @Baldrickk for midpoint slice, and @Peilonrayz for concern over input filtering.

C.Nivs
  • 3.1k
  • 14
  • 32