Python: How to detect a particular number inside a long serial number
Question: I have been working on this project, a small Python and Tkinter project as I'm
a beginner and I almost finished it if it weren't for this little issue I have
with it that I detected after doing a few tests. The program should say
whether a serial number that I entered in an input is a "devil number" or not
depending on whether the number does have the number "666" in it or not. in
the positive case, there should be the number "666" in it and it should away
from other 6s, which means there shouldn't be something like this "666". If
the number "666" is repeated several times inside the serial number (without
being stuck together "666666") it can be considered a devil number too.

The issue I have is that when I test numbers that only have one "666" within
them and that at the same time end with that number (666), those numbers are
not considered as devil numbers while they should be. I can't seem to solve
this problem.

To realise this project, I used Python and Tkinter. The code is as follows:

    
    
    """*************************************************************************"""
    """ Author: CHIHAB        Version: 2.0        Email: chihab2007@gmail.com   """
    """ Purpose: Practice     Level: Beginner     2016/2017                     """
    """*************************************************************************"""
    ############################ I M P O R T S#####################################
    from tkinter import*
    from types import *
    ############################ T K I N T E R ####################################
    main = Tk()
    
    e = Entry(main, bg="darkblue", fg="white")
    e.pack(fill=X)
    
    l = Label(main, bg="blue", fg="yellow")
    l.pack(fill=X)
    ############################ F U N C T I O N S ################################
    def devil(x): #TEST ENTERED VALUE FOR DEVIL NUMBER
        c = 0
        i = 0
        l = list(x)
        while i < len(l):                   #This block of code means that as long as the index i 
            if l[i] == "6":                 #   is below the length of the list to which we have 
                c = c+1                     #       converted the entry, the program is allowed to keep 
                print("this is c :", c)     #           reading through the list's characters.
            else:
                c = 0
            if i <= (len(l)-2) and c == 3 and l[i+1] != "6":
                return True
            i = i+1
        return False
    def printo():   #GET VALUE ENTRY AND SHOW IT IN LABEL
        x = e.get()
        if x != "":
            if x.isnumeric() == True:   #SHOW ENTERED VALUE IF INTEGER
                y = devil(x)
                if y == True:
                    print("The number you entered is a devil number.")                
                    l.config(text="The number you entered is a devil number.", bg="blue")
                else:
                    print("The number you entered is NOT a devil number.")                
                    l.config(text="The number you entered is NOT a devil number.", bg="blue")
                #print(x)
                e.delete(0, END)
            else:   #SHOW ERROR IF NOT INTEGER
                l.config(text="please enter an integer in the entry.", bg="red")
                print("please enter an integer in the entry.")
                e.delete(0, END)
        else:   #SHOW ERROR IF EMPTY
            l.config(text="please enter something in the entry.", bg="red")
            print("please enter something in the entry.")
    
    ############################ T K I N T E R ####################################
    b = Button(main, text="Go", bg="lightblue", command=printo)
    b.pack(fill=X)
    
    main.mainloop()
    

Here you go, guyz. I hope my code is neat enough and that you would be able to
help me which I have no doubt about. Thank you.


Answer: If you mean that `666`, found anywhere in the number should be a match, then
it's very simple:

    
    
    if '666' in '1234666321':
        print("It's a devil's number")
    

However, you say that `666` must be a "lone" `666`, i.e. exactly three `6`
side by side, no more, no less. Neither two, nor four. Five `6`'s are right
out. In that case, I would use [tobias_k's
regex](http://stackoverflow.com/a/38769806/344286).

Though, if you had a passionate hatred for regex, you _could_ do it using
`string.partition`:

    
    
    def has_devils_number(num):
        start, mid, end = num.partition('666')
        if not mid:
            return False
        else:
            if end == '666':
                return False
            elif start.endswith('6') or end.startswith('6'):
                return has_devils_number(start) or has_devils_number(end)
            return True
    

Here's what the performance looks like:

    
    
    >>> x = '''
    ... import re
    ... numbas = ['666', '6', '123666', '12366', '66123', '666123', '666666', '6666', '6'*9, '66661236666']
    ... 
    ... def devil(x):
    ...     return re.search(r"(?:^|[^6])(666)(?:[^6]|$)", x) is not None
    ... '''
    >>> import timeit
    >>> timeit.timeit('[devil(num) for num in numbas]', setup=x)
    13.822128501953557
    
    >>> x = '''
    ... numbas = ['666', '6', '123666', '12366', '66123', '666123', '666666', '6666', '6'*9, '6666123
    666']
    ... def has_devils_number(num):
    ...     start, mid, end = num.partition('666')
    ...     if not mid:
    ...         return False
    ...     else:
    ...         if end == '666':
    ...             return False
    ...         elif start.endswith('6') or end.startswith('6'):
    ...             return has_devils_number(start) or has_devils_number(end)
    ...         return True
    ... '''
    >>> timeit.timeit('[has_devils_number(num) for num in numbas]', setup=x)
    9.843224229989573
    

I'm as surprised as you are.

