3

I just learned that I can check if a substring is inside a string using:

substring in string

It looks to me that a string is just a special kind of tuple where its elements are chars. So I wonder if there's a straightforward way to search a slice of a tuple inside a tuple. The elements in the tuple can be of any type.

tupleslice in tuple

Now my related second question:

>>> tu = 12 ,23, 34,56
>>> tu[:2] in tu
False

I gather that I get False because (12, 23) is not an element of tu. But then, why substring in string works?. Is there syntactic sugar hidden behind scenes?.

Moinuddin Quadri
  • 46,825
  • 13
  • 96
  • 126
Yuta73
  • 188
  • 10

5 Answers5

5

string is not a type of tuple. Infact both belongs to different class. How in statement will be evaluated is based on the __contains__() magic function defined within there respective class.

Read How do you set up the contains method in python, may be you will find it useful. To know about magic functions in Python, read: A Guide to Python's Magic Methods

Community
  • 1
  • 1
Moinuddin Quadri
  • 46,825
  • 13
  • 96
  • 126
3

A string is not just a special kind of tuple. They have many similar properties, in particular, both are iterators, but they are distinct types and each defines the behavior of the in operator differently. See the docs on this here: https://docs.python.org/3/reference/expressions.html#in

To solve your problem of finding whether one tuple is a sub-sequence of another tuple, writing an algorithm like in your answer would be the way to go. Try something like this:

def contains(inner, outer):
  inner_len = len(inner)
  for i, _ in enumerate(outer):
    outer_substring = outer[i:i+inner_len]
    if outer_substring == inner:
      return True
  return False
Cameron Lee
  • 973
  • 9
  • 11
  • Uau!. Talk about some real real python coding!. Thanks. This is a way more compact code and easier to understand. One little question: Is the underscore a usual way to name dummy variables or is it just you?. – Yuta73 Sep 15 '16 at 23:24
  • I guess the only downside is a little overhead for executing enumerate(), but I'm OK with this in order to improve code maintenability. Many times you can make the code faster but if it's at expense of readability it might not be a good idea. – Yuta73 Sep 15 '16 at 23:33
  • Yes, the underscore is a common way to denote that a variable is unused. – Cameron Lee Sep 16 '16 at 03:34
  • I definitely focused on readability in this code over speed. I wanted to show that you can use higher-level building blocks to write an algorithm like this. Regarding speed, you're right that it shouldn't always be prioritised over readability. Also, it's hard to tell how fast code is just by reading it, so you really need to benchmark. In particular, I wouldn't expect enumerate to provide any kind of significant overhead. My understanding is that in Python, even just accessing variables has some overhead. So the fact that this is simpler than your algorithm might make it faster too. – Cameron Lee Sep 16 '16 at 03:48
1

This is how I accomplished to do my first request, however, it's not straightforward nor pythonic. I had to iterate the Java way. I wasn't able to make it using "for" loops.

def tupleInside(tupleSlice):
    i, j = 0, 0
    while j < len(tu):
        t = tu[j]
        ts = tupleSlice[i]
        print(t, ts, i, j)
        if ts == t:
            i += 1
            if i == len(tupleSlice):
                return True
        else:
            j -= i
            i = 0
        j += 1
    return False

tu = tuple('abcdefghaabedc')
print(tupleInside(tuple(input('Tuple slice: '))))
Yuta73
  • 188
  • 10
0

Try just playing around with tuples and splices. In this case its pretty easy because your splice is essentially indexing.

>>> tu = 12 ,23, 34,56  
>>> tu
(12, 23, 34, 56) #a tuple of ints
>>> tu[:1] # a tuple with an int in it
(12,) 
>>> tu[:1] in tu #checks for a tuple against int. no match.
False 
>>> tu[0] in tu #checks for int against ints. matched!
True
>>> #you can see as we iterate through the values...
>>> for i in tu:
         print(""+str(tu[:1])+" == " + str(i))

(12,) == 12
(12,) == 23
(12,) == 34
(12,) == 56

Splicing is returning a list of tuples, but you need to index further to compare in by values and not containers. Spliced strings return values, strings and the in operator can compare to values, but splicing tuples returns tuples, which are containers.

Joshua Klein
  • 189
  • 2
  • 13
0

Just adding to Cameron Lee's answer so that it accepts inner containing a single integer.

def contains(inner, outer):
    try:
        inner_len = len(inner)
        for i, _ in enumerate(outer):
            outer_substring = outer[i:i+inner_len]
            if outer_substring == inner:
                return True
        return False
    except TypeError:
        return inner in outer

contains(4, (3,1,2,4,5))  # returns True
contains((4), (3,1,2,4,5))  # returns True
Daniel
  • 11,332
  • 9
  • 44
  • 72