A quick and pythonic way to find out if a string is a palindrome

[Edit: as someone remarked, I misused the concept of a palindrome, now I have edited using the correct functions. I also made some optimizations in the first and third examples in which the for statement reaches half the line]

I encoded three different versions for a method that checks if a string is a palindrome. This method is implemented as extensions to the class "str"

Methods also convert the string to lowercase and remove all punctual and whitespace. Which one is better (faster, pythons)?

Here are the methods:

1) This is the first solution I was thinking about:

def palindrom(self): lowerself = re.sub("[ ,.;:?!]", "", self.lower()) n = len(lowerself) for i in range(n//2): if lowerself[i] != lowerself[n-(i+1)]: return False return True 

I think this is faster because there are no conversions or line reversals, and the for statement breaks into the first separate element, but I don't think this is an elegant and pythonic way to do this

2) In the second version, I am doing a conversion with a solution based here on stackoverflow (using the extended string slicing [:: - 1])

 # more compact def pythonicPalindrom(self): lowerself = re.sub("[ ,.;:?!]", "", self.lower()) lowerReversed = lowerself[::-1] if lowerself == lowerReversed: return True else: return False 

But I think that slicing and comparing strings makes this solution slower.

3) The third solution I was thinking of is using an iterator:

 # with iterator def iteratorPalindrom(self): lowerself = re.sub("[ ,.;:?!]", "", self.lower()) iteratorReverse = reversed(lowerself) for char in lowerself[0:len(lowerself)//2]: if next(iteratorReverse) != char: return False return True 

which I find more elegant from the first solution and more effective from the second solution

+5
source share
6 answers

So, I decided to just timeit and find which one was the fastest. Note that the final function is a cleaner version of your own pythonicPalindrome . It is defined as follows:

 def palindrome(s, o): return re.sub("[ ,.;:?!]", "", s.lower()) == re.sub("[ ,.;:?!]", "", o.lower())[::-1] 

Methodology

I performed 10 different tests for each function. In each test run, the function was called 10,000 times, with arguments self="aabccccccbaa", other="aabccccccbaa" . Results can be found below.

  palindrom iteratorPalindrome pythonicPalindrome palindrome 1 0.131656638 0.108762937 0.071676536 0.072031984 2 0.140950052 0.109713793 0.073781851 0.071860462 3 0.126966087 0.109586756 0.072349792 0.073776719 4 0.125113136 0.108729573 0.094633969 0.071474645 5 0.130878159 0.108602964 0.075770395 0.072455015 6 0.133569472 0.110276694 0.072811747 0.071764222 7 0.128642812 0.111065438 0.072170571 0.072285204 8 0.124896702 0.110218949 0.071898959 0.071841214 9 0.123841905 0.109278358 0.077430437 0.071747112 10 0.124083576 0.108184210 0.080211147 0.077391086 AVG 0.129059854 0.109441967 0.076273540 0.072662766 STDDEV 0.005387429 0.000901370 0.007030835 0.001781309 

It looks like the cleaner version of your pythonicPalindrome slightly faster, but both functions are clearly superior to the alternatives.

+5
source

It seems like you want to know the runtime of your code blocks and compare them.

You can use the timeit module.

Here is a quick way:

 import timeit start = timeit.default_timer() #Your code here stop = timeit.default_timer() print stop - start 

More details:

Option 1

Option 2

+1
source

You can also use this one-liner that does not use re, but instead itertools:

 def isPalindrom(self): return all(i==j for i, j in itertools.zip_longest((i.lower() for i in self if i not in " ,.;:?!"), (j.lower() for j in self[::-1] if j not in " ,.;:?!"))) 

Or, explained in more detail:

 def isPalindrom(self): #using generators to not use memory stripped_self = (i.lower() for i in self if i not in " ,.;:?!") reversed_stripped_self = (j.lower() for j in self[::-1] if j not in " ,.;:?!") return all(self_char==reversed_char for self_char, reversed_char in itertools.zip_longest(stripped_self, reversed_stripped_self)) 
+1
source

Recall that filter works with strings:

 >>> st="One string, with punc. That also needs lowercase!" >>> filter(lambda c: c not in " ,.;:?!", st.lower()) 'onestringwithpuncthatalsoneedslowercase' 

Thus, your test can be a single liner, which is obvious in the function:

 >>> str '!esacrewol sdeen osla tahT .cnup htiw ,gnirts enO' >>> filter(lambda c: c not in " ,.;:?!", st.lower())==filter(lambda c: c not in " ,.;:?!", str.lower()[::-1]) True 

Or, if you are going to use a regular expression, just change the result to idiomatic str[::-1] :

 >>> "123"[::-1] '321' >>> re.sub(r'[ ,.;:?!]', '', st.lower())==re.sub(r'[ ,.;:?!]', '', str.lower())[::-1] True 

The fastest might be using string.tranlate to remove characters:

 >>> import string >>> string.translate(st, None, " ,.;:?!") 'OnestringwithpuncThatalsoneedslowercase' >>> string.translate(st, None, " ,.;:?!")==string.translate(str, None, " ,.;:?!")[::-1] True 
+1
source

When we pass a word, it checks to see if it can be undone, if it can be undone, it prints "This is a palindrome." or "This is not a palindrome"

 def reverse(word): x = '' for i in range(len(word)): x += word[len(word)-1-i] return x word = input('give me a word:\n') x = reverse(word) if x == word: print('This is a Palindrome') else: print('This is NOT a Palindrome') 
0
source

Why not use a more pythonic way!

 def palindrome_checker(string): string = string.lower() return string == string[::-1] # returns a boolean 
0
source

Source: https://habr.com/ru/post/1240005/


All Articles