Python newbie clarification about tuples and strings - python

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?.

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

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

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: '))))

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.

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

Related

Class pattern is matching the wrong cases

I'm writing an object serializer but am having issues where the class patterns are not matching the expected cases:
def dump_obj(x):
match(x):
case list():
emit('L')
dump_obj(len(x))
for elem in x:
dump_obj(elem)
case Iterable():
emit('I')
dump_obj((type(x), list(x)))
case tuple():
emit('T')
dump_obj(list(x))
case str():
emit('S')
dump_obj(len(x))
emit(x)
case int():
emit('D')
emit(str(x))
case _:
raise TypeError(f'Unknown obj {x!r}')
When I call dump_obj() with a tuple, it giving an infinite recursion on the I-case for iterables rather than matching the T-case for tuples.
When I call dump_obj() with a list subclass, it is matching the L-case for lists instead of the intended I-case for iterables.
First problem: Ordering
The cases are not independent of one another. They are tested from the top-down (like a long if/elif chain) and the first to match wins.
In the example, the specific match tests like like list, tuple, and str need to come before more general matches like Iterable. Otherwise with the current code, a tuple input like (10, 20, 30) will match the I-case instead of the intended T-case.
Second problem: Specificity
A class pattern performs an isinstance() check which would match both a type and subclasses of the type. To restrict the case to an exact match, use a type guard:
case list() if type(x) == list:
...
Putting it all together
With both solutions applied, here is the new code:
def dump_obj(x):
match(x):
case list() if type(x) == list: # <-- Added guard
emit('L')
dump_obj(len(x))
for elem in x:
dump_obj(elem)
case tuple() if type(x) == tuple: # <-- Added guard
emit('T')
dump_obj(list(x))
case str() if type(x) == str: # <-- Added guard
emit('S')
dump_obj(len(x))
emit(x)
case Iterable(): # <-- Move after list, tuple, str
emit('I')
dump_obj((type(x).__name__, list(x)))
case int():
emit('D')
emit(str(x))
case _:
raise TypeError(f'Unknown obj {x!r}')
Sample runs
Here we show that the two problematic cases work as expected.
>>> dump_obj((10, 20)) # Tuple of integers
T
L
D
2
D
10
D
20
>>> class List(list):
... pass
...
>>> dump_obj(List((30, 40))) # List subclass
I
T
L
D
2
S
D
4
List
L
D
2
D
30
D
40

Check if two tuples have elements in common in their corresponding positions

Here is what I want to do:
pair1 = (1,2)
pair2 = (3,3)
pair3 = (3,2)
# Is there a way that I can compare any of these two objects and yields the following:
def myComp(...):
#...
myComp(pair1,pair2) gives False
myComp(pair1,pair3) gives True #They both have 2 at index 1
myComp(pair1,pair3) gives True #They both have 3 at index 0
Any ideas or advice will be greatly appreciated.
There are builtins to do this much more easily than hardcoding the if statement conditions. You can use zip and any:
def myComp(pair1, pair2):
return any(x == y for x, y in zip(pair1, pair2))
>>> myComp(pair1, pair2)
False
>>> myComp(pair2, pair3)
True
>>> myComp(pair1, pair3)
True
What happens is, the two lists are zipped together using zip which creates a generator of tuples. This is unpacked inside a generator comprehension. any will then test to see if any of the comparisons x == y are True. If yes, the resultant is True and is returned. Else, False is returned.
This approach should work with any arbitrary sized lists, provided they are equal.
Your myComp function just needs an if to do comparison so it would be like:
def myComp(pair1,pair2)
if (pair1[0]==pair2[0] || pair1[1]==pair2[1])
return true;
return false;

Python: can a function return a string?

I am making a recursive function that slices string until it is empty. When it is empty it alternatively selects the characters and is supposed to print or return the value. In this case I am expecting my function to return two words 'Hello' and 'World'. Maybe I have got it all wrong but what I don't understand is that my function doesn't let me print or return string. I am not asking for help but I'd like some explanation :) thanks
def lsubstr(x):
a= ''
b= ''
if x == '':
return ''
else:
a = a + x[0:]
b = b + x[1:]
lsubstr(x[2:])
#print (a,b)
return a and b
lsubstr('hweolrllod')
so I changed my code to this:
def lsubstr(x):
if len(x) <1:
return x
else:
return (lsubstr(x[2:])+str(x[0]),lsubstr(x[2:])+str(x[1]))
lsubstr('hweolrllod')
and what I am trying to make is a tuple which will store 2 pairs of characters and concatenate the next ones,
the error I get is
TypeError: Can't convert 'tuple' object to str implicitly
what exactly is going wrong, I have checked in visualization, it has trouble in concatenating.
The and keyword is a boolean operator, which means it compares two values, and returns one of the values. I think you want to return a tuple instead, like this:
...
return (a, b)
And then you can access the values using the indexing operator like this:
a = lsubstr( ... )
a[0]
a[1]
Or:
word1, word2 = lsubstr( ... )

How do I check existence of a string in a list of strings, including substrings?

I have written a function to check for the existence of a value in a list and return True if it exists. It works well for exact matches, but I need for it to return True if the value exists anywhere in the list entry (e.g. value <= listEntry, I think.) Here is the code I am using for the function:
def isValInLst(val,lst):
"""check to see if val is in lst. If it doesn't NOT exist (i.e. != 0),
return True. Otherwise return false."""
if lst.count(val) != 0:
return True
else:
print 'val is '+str(val)
return False
Without looping through the entire character string and/or using RegEx's (unless those are the most efficient), how should I go about this in a pythonic manner?
This is very similar to another SO question, but I need to check for the existence of the ENTIRE val string anywhere in the list. It would also be great to return the index / indices of matches, but I'm sure that's covered elsewhere on Stackoverflow.
If I understood your question then I guess you need any:
return any(val in x for x in lst)
Demo:
>>> lst = ['aaa','dfbbsd','sdfdee']
>>> val = 'bb'
>>> any(val in x for x in lst)
True
>>> val = "foo"
>>> any(val in x for x in lst)
False
>>> val = "fde"
>>> any(val in x for x in lst)
True
Mostly covered, but if you want to get the index of the matches I would suggest something like this:
indices = [index for index, content in enumerate(input) if substring in content]
if you want to add in the true/false you can still directly use the result from this list comprehension since it will return an empty list if your input doesn't contain the substring which will evaluate to False.
In the terms of your first function:
def isValInLst(val, lst):
return bool([index for index, content in enumerate(lst) if val in content])
where the bool() just converts the answer into a boolean value, but without the bool this will return a list of all places where the substring appears in the list.
There are multiple possibilities to do that. For example:
def valInList1 (val, lst):
# check `in` for each element in the list
return any(val in x for x in lst)
def valInList2 (val, lst):
# join the list to a single string using some character
# that definitely does not occur in val
return val in ';;;'.join(lst)

How do you make an object return a sorted array instead of an empty one in python?

I'm trying to create a library of some common algorithms so that people will be able to use them easily. I created an object called Compare, which has some methods that would be useful in these algorithms.
Code for Compare:
class Compare(list):
def __init__(self,arr):
self.arr = arr
def __compare(self,u,v):
# Compares one item of a Compare
# object to another
if u < v:
return 1
if u == v:
return 0
if u > v:
return -1
def __swap(self,arr,i,j):
# Exchanges i and j
temp = arr[i]
arr[i] = arr[j]
a[j] = temp
def __determine(self,arr):
# Determines if the array is sorted or not
for i in range(0,len(array)):
if self.__compare(arr[i], arr[i+1]) == -1:
return False
return True
def __printout(self,arr):
for i in range(0,len(array)):
return arr[i] + '\n'
def sorted(self):
if self.__determine(arr):
return True
return False
Here's one of the algorithms that uses this class:
def SelectionSort(array):
try:
array = Compare(array)
for ix in range(0, len(array)):
m = ix
j = ix+1
for j in range(0,len(array)):
if array.__compare(array[j], array[m]) == -1:
m = j
array.__swap(arr, ix, m)
return array
except(TypeError) as error:
print "Must insert array for sort to work."
The problem I'm having is that whenever I try to use this or any of the other algorithms, it returns an empty array instead of the sorted array. I'm not sure how to get the Compare object to return the sorted array.
I'm pretty sure this is what is happening. When you call :
array = Compare(array)
You overwrite the reference to the original array. Array is now a reference to a Compare object. Replace array with array.arr (or name array something better) and this should work I think! :)
Remember that python is loosely typed, so that your "array" variable is just a reference to some data. In this case, you are switching it from a reference to a list to a reference to a Compare object.
Think about:
>>> x = 1
>>> x
1
>>> x = 's'
>>> x
's'
And think about what happens to the 1 ;)
Your code has many problems some of them make it to fail
for example
in sorted you are using a maybe global arr that doesn't exist, instead
of self.arr).
in swap you also use a[j] = temp, but a is local to the method and you do not use it for anything
you are using two underscores for your methods. This puts name mangling to work, So the calls in the function do not work in the way you do them. Probably you want a single underscore to indicate that this are private methods.
But the main problem is that Compare is not returnig a list. For that you need:
class Compare(list):
def __init__(self, arr):
list.__init__(self, arr)
then:
>>> print Compare([1,2,3,4])
[1, 2, 3, 4]
In this way you should use in your methods self instead of self.arr because your instance is a list (or an instance of a subclass of list).
So the following is your code modified to actually work. The only problem is that your sorting algorithn is wrong an it is not sorting right. But you can do from here I suppose:
class Compare(list):
def __init__(self, arr):
list.__init__(self, arr)
def _compare(self, u, v):
# Compares one item of a Compare
# object to another
if u < v:
return 1
if u == v:
return 0
if u > v:
return -1
def _swap(self, i, j):
# Exchanges i and j
temp = self[i]
self[i] = self[j]
self[j] = temp
def _determine(self):
# Determines if the array is sorted or not
for i in range(len(array)):
if self._compare(self[i], self[i+1]) == -1:
return False
return True
def _printout(self):
for i in self:
return i + '\n'
def sorted(self):
if self._determine():
return True
return False
def SelectionSort(array):
try:
array = Compare(array)
for ix in range(len(array)):
m = ix
j = ix + 1
for j in range(len(array)):
if array._compare(array[j], array[m]) == -1:
m = j
array._swap(ix, m)
return array
except(TypeError) as error:
print "Must insert array for sort to work."
You're not returning the array, you're returning a Compare wrapped around the array. If you intend Compare to be a proxy, the wrapping is incomplete, as you don't forward the standard container operations to the proxied array. In addition, you'll need to consistently use the Compare instance. Currently, you sometimes use the Compare and other times use the original sequence object, such as every place you pass the sequence to a method. Instead, use the Compare object within its own methods.
However, that's having Compare do two things: be an algorithm collection, and be a sequence. If you keep the Compare object separate and work on the list directly, you can switch out the algorithms easily. This is the more typical approach; list.sort works this way, taking a comparator as an argument. You'll also need to fix your implementation of Compare, which uses the wrong variable name in numerous places (array, when the local variable is named arr). If you want anyone to use your library, it's going to have to be much better designed.
As further reasons not to make Compare a sequence, consider what happens when you need to change comparison methods: you end up wrapping the Compare in another, making the wrapped Compare useless.
Consider the approach used in math: an order is a relationship defined on a set, not an intrinsic part of the set, and it especially isn't a part of sequences of items from the set. This reveals another conceptual error with your original approach: it couples an ordering (which is a set relationship) with operations on sequences of elements from the set. The two should be kept separate, so that you can use different comparisons with the sequence operations.
Off-Topic
There are a number of other mistakes of various types in the code. For example, in SelectionSort you assume that type errors must be due to a non-sequence being passed as array. Comparing instances of uncomparable types (such as 0 and 'd') will also result in a type error. For another example, Compare.sorted is useless; it's of the pattern:
if test:
return True
return False
This is logically equivalent to:
return test
which means Compare.sorted is equivalent to Compare.__determine. Make the latter the former, as sorted is a more descriptive name. "determine" is too ambiguous; it begs the question of what's being determined.
You can get more code reviews at codereview.stackexchange.com.

Categories