code: def __ngrams(s, n=3):
      
      return list(zip(*[s[i:] for i in range(n)]))
docs: Raw n-grams from a sequence
  
          If the sequence is a string, it will return char-level n-grams.
          If the sequence is a list of words, it will return word-level n-grams.
  
          Note: it treats space (' ') and punctuation like any other character.
  
              >>> ngrams('This is not a test!')
              [('T', 'h', 'i'), ('h', 'i', 's'), ('i', 's', ' '), ('s', ' ', 'i'),
              (' ', 'i', 's'), ('i', 's', ' '), ('s', ' ', 'n'), (' ', 'n', 'o'),
              ('n', 'o', 't'), ('o', 't', ' '), ('t', ' ', 'a'), (' ', 'a', ' '),
              ('a', ' ', 't'), (' ', 't', 'e'), ('t', 'e', 's'), ('e', 's', 't'),
              ('s', 't', '!')]
              >>> ngrams(["This", "is", "not", "a", "test!"])
              [('This', 'is', 'not'), ('is', 'not', 'a'), ('not', 'a', 'test!')]
  
          Args:
              s: a string or a list of strings
              n: an int for the n in n-gram
  
          Returns:
              list: tuples of char-level or word-level n-grams