I'm interested in how is Python handling slicing where the stop attribute is larger than the length of the string we are working with, for example:
my_string = 'abc'
my_slice = my_string[:10]
I am aware that my_slice == 'abc', what interests me is how efficient this is and how it works under the hood.
I've read Time complexity of string slice and Understanding slice notation, but didn't find the exact case I was looking for.
My guess based on mentioned sources would be that a shallow copy of the string is returned (my_string[:10] is the same as my_string[:] in this case), is this the case?
The logic of python is that it will extract all the values that are before that index in the list.
So if the stop value is higher than the length of the list it will return the whole list because all the values' indexes are under the stop number, so it will extract all the values.
Slicing in Python follows the following format:
myVar[fromIndex:toIndex]
The end of the slicing marked by the toIndex, counts to its index, which is one character less:
my_string = 'abc'
print(my_string[2]) #Output: 'c'
print(my_string[:2]) #Output: 'ab'
If we set a final index greater than the length of characters of the data, it will take it entire (it prints from the first value to the last one found). For example (following the example you have given):
0 1 2 3
a b c
my_string = 'abc'
print(my_string[:len(my_string)]) #Output: 'abc'
With the following example, you can clearly see that [:] (being the full length of the string) is equivalent to printing the value by slicing from the beginning to the end of the string:
0 1 2
a b c
my_string = 'abc'
print(my_string[:]) #Output: 'abc'
0 1 2 3
a b c
my_string = 'abc'
print(len(my_string)) #Output: '3'
print(my_string[:len(my_string)]) #Output: 'abc'
Related
def isRotation(s1, s2):
return len(s1) == len(s2) and s2 in s1*2
isRotation("ABCD", "CDAB")
>> True
The code above was given as one of many ways to check if two strings are a rotation of each other.
However, I don't understand why String1(s1) has to be multiplied by 2 in the code.
In python, multiplying strings is repeating the string, for example:
>>> 'abc'*2
'abcabc'
>>> 'abc'*4
'abcabcabcabc'
So in order to know if a string is a rotation of another string, you would need to multiplie it by 2, for example:
>>> 'bca' in 'abc'
False
>>> 'bca' in 'abc' * 2
True
Multiplying a string by 2 will double the original string. And because s1 wraps around, regardless of its starting position the whole string can be found somewhere within twice its length
ABCDABCD
|||| DABC
||| CDAB
|| BCDA
| ABCD
Rotating a string is as good as chopping it at point of rotation and putting that chopped part at the end of string.
ABCDEFGH rotated 4 places to left is:
Chop at 4-> ABCD EFGH
Place at the end -> EFGHABCD
Python str*x gives you str concatenated to itself x times.
>>> a = 'String'
>>> a*2
'StringString'
>>>
Putting these two points together with the fact that any string can be rotated by maximum amount equal to its length (when it becomes the original string) gives you the logic to check the rotated string's presence in str*2.
Understanding of your question is- To compare a string with the rotation string.
My approach to address the same would be.
1- To get the rotation string.
def rotate_str(strg, n):
return strg[n:] + strg[:n]
length = 2 #can change this to whatever value suits to you or even pass this as arg.
print(rotate('SAMPLE', length))
2- compare strings.
str1 = 'SAMPLE'
str2 = rotate(str1, length)
def compare_str(str1, str2):
return str1 == str2
Here when we multiply s1 with 2. It returns s1s1 like if s1="ABCD", it will return "ABCDABCD", and in if s2 in s1*2 we are checking that if "CRAB" is in "ABCDABCD" which is true.
I am starting to learn Python and looked at following website: https://www.w3resource.com/python-exercises/string/
I work on #4 which is "Write a Python program to get a string from a given string where all occurrences of its first char have been changed to '$', except the first char itself."
str="restart"
char=str[0]
print(char)
strcpy=str
i=1
for i in range(len(strcpy)):
print(strcpy[i], "\n")
if strcpy[i] is char:
strcpy=strcpy.replace(strcpy[i], '$')
print(strcpy)
I would expect "resta$t" but the actual result is: $esta$t
Thank you for your help!
There are two issues, first, you are not starting iteration where you think you are:
i = 1 # great, i is 1
for i in range(5):
print(i)
0
1
2
3
4
i has been overwritten by the value tracking the loop.
Second, the is does not mean value equivalence. That is reserved for the == operator. Simpler types such as int and str can make it seem like is works in this fashion, but other types do not behave this way:
a, b = 5, 5
a is b
True
a, b = "5", "5"
a is b
True
a==b
True
### This doesn't work
a, b = [], []
a is b
False
a == b
True
As #Kevin pointed out in the comments, 99% of the time, is is not the operator you want.
As far as your code goes, str.replace will replace all instances of the argument supplied with the second arg, unless you give it an optional number of instances to replace. To avoid replacing the first character, grab the first char separately, like val = somestring[0], then replace the rest using a slice, no need for iteration:
somestr = 'restart' # don't use str as a variable name
val = somestr[0] # val is 'r'
# somestr[1:] gives 'estart'
x = somestr[1:].replace(val, '$')
print(val+x)
# resta$t
If you still want to iterate, you can do that over the slice as well:
# collect your letters into a list
letters = []
char = somestr[0]
for letter in somestr[1:]: # No need to track an index here
if letter == char: # don't use is, use == for value comparison
letter = '$' # change letter to a different value if it is equal to char
letters.append(letter)
# Then use join to concatenate back to a string
print(char + ''.join(letters))
# resta$t
There are some need of modification on your code.
Modify your code with as given in below.
strcpy="restart"
i=1
for i in range(len(strcpy)):
strcpy=strcpy.replace(strcpy[0], '$')[:]
print(strcpy)
# $esta$t
Also, the best practice to write code in Python is to use Function. You can modify your code as given below or You can use this function.
def charreplace(s):
return s.replace(s[0],'$')[:]
charreplace("restart")
#'$esta$t'
Hope this helpful.
I'm trying to write a script that finds the substring 'bob' in a given string. I thought string slicing would be a good way of doing it, but I'm getting "TypeError: string indices must be integers". I'm confused by this as both of the variables I've used as indices are integers as far as I can tell.
Also even if this code is not an efficient way of doing this I am curious as to why I'm having issues using variables as indices as all of my googling has turned up that that is an okay thing to do.
s = 'azcbobobegghakliia'
bob = 'bob'
startindex = 0
endindex = 2
numBob = 0
while len(s) > endindex:
if s[startindex,endindex] == 'bob':
numBob += 1
startindex += 1
endindex += 1
print(numBob)
I expected it to print 2 as 'bob' is contained twice in this string (...bobob...). The actual output is "TypeError: string indices must be integers"
Comma-separated variables by default make a tuple. You want to use : to make a slice instead.
Longer explanation: a[b,c] is equivalent to a[(b,c)], that is, a indexed with the tuple (b,c). To get the elements from b to c, you need a[b:c].
I am looking at adding numbers to a string as python reads through a string.
So if I had a string a = "253+"
I would then have an empty string b.
So, how would I have python read the 2, add it to string b, then read the 5 and add it to string b, and then add the 5 and add it to string b, when it hits something that isnt an integer though, it stops the function.
then string b would be b = "253"
is there a specific call in an iteration that would ask for integers and then add i to another string?
tl;dr
I want to use an iteration to add numbers from one string to another, which stops when it reaches a non-integer.
string b would be an empty string, and string a would be a="253+"
after the call would be done, strng b would equal b="253"
I know this sounds like a homework question, but its not. If you need anything else clarified, I would be happy to.
Here is a simple method for extracting the digits from a string:
In [13]: a="253+"
In [14]: ''.join(c for c in a if c.isdigit())
Out[14]: '253'
The question is a bit unclear, but is this what you're looking for?
a = "123+"
b=""
for c in a:
try:
int(c)
b = b + c
except ValueError:
print 'This is not an int ' + c
break
Running this results in this b being 123 and breaking on the + character. It sounds like the part that's tricky for you at the moment is the try..except ValueError bit. Not that I don't have to break the loop when a ValueError happens, I could just keep looping over the remaining characters in the string and ignore ones that cannot be parsed into an int
In [201]: import itertools as IT
In [202]: a = "253+9"
In [203]: ''.join(IT.takewhile(str.isdigit, a))
Out[203]: '253'
IT.takewhile will stop at the first character in a which is not a digit.
Another way would be to use a regex pattern. You could split the string on non-digits using the pattern r'\D':
In [208]: import re
In [209]: re.split(r'\D', a, maxsplit=1)[0]
Out[209]: '253'
With the use of the for loop, this is relatively easy. If we use our ASCII knowledge, we know that the ASCII values of the digits range from 48 (which represents 0 as a string) to 57 (which represents 9 as a string).
We can find the ASCII value of a character by using the built in method ord(x) where x is the character (i.e. ord('4') is equal to 52, the integer).
Now that we have this knowledge, it will be easy to add this to our for-loop. We simply make a for-loop that goes from 0 to the length of the string minus 1. In the for loop, we are going to use the iteration that we are on as an index, find the character at that index in our string, and finally check to see if its ord value falls in the range that we want.
This will look something like this:
def method(just_a_variable):
b = ''
for i in range(0, len(a)):
if (#something):
if (#something):
b = b+a[i]
return b
Can you fill in the "#somethings"?
Try this:
a = "i889i" #Initial value of A
b = "" #Empty string to store result into
for each in a: #iterate through all characters of a
if each.isdigit(): #check if the current character is a digit
b += each #append to b if the current character is a digit
else: #if current character is NOT a digit
break #break out of for loop
print b #print out result
Hope this helps!
You can write a generator with a regex and generate them one by one:
>>> import re
>>> s='123+456abc789'
>>> nums=(m.group(1) for m in re.finditer(r'(\d+)', s))
>>> next(nums)
'123'
>>> next(nums)
'456'
>>> next(nums)
'789'
I have the following problem: I would like to write a function in Python which, given a string, returns a string where every group of two characters is swapped.
For example given "ABCDEF" it returns "BADCFE".
The length of the string would be guaranteed to be an even number.
Can you help me how to do it in Python?
To add another option:
>>> s = 'abcdefghijkl'
>>> ''.join([c[1] + c[0] for c in zip(s[::2], s[1::2])])
'badcfehgjilk'
import re
print re.sub(r'(.)(.)', r'\2\1', "ABCDEF")
from itertools import chain, izip_longest
''.join(chain.from_iterable(izip_longest(s[1::2], s[::2], fillvalue = '')))
You can also use islices instead of regular slices if you have very large strings or just want to avoid the copying.
Works for odd length strings even though that's not a requirement of the question.
While the above solutions do work, there is a very simple solution shall we say in "layman's" terms. Someone still learning python and string's can use the other answers but they don't really understand how they work or what each part of the code is doing without a full explanation by the poster as opposed to "this works". The following executes the swapping of every second character in a string and is easy for beginners to understand how it works.
It is simply iterating through the string (any length) by two's (starting from 0 and finding every second character) and then creating a new string (swapped_pair) by adding the current index + 1 (second character) and then the actual index (first character), e.g., index 1 is put at index 0 and then index 0 is put at index 1 and this repeats through iteration of string.
Also added code to ensure string is of even length as it only works for even length.
string = "abcdefghijklmnopqrstuvwxyz123"
# use this prior to below iteration if string needs to be even but is possibly odd
if len(string) % 2 != 0:
string = string[:-1]
# iteration to swap every second character in string
swapped_pair = ""
for i in range(0, len(string), 2):
swapped_pair += (string[i + 1] + string[i])
# use this after above iteration for any even or odd length of strings
if len(swapped_pair) % 2 != 0:
swapped_adj += swapped_pair[-1]
print(swapped_pair)
badcfehgjilknmporqtsvuxwzy21 # output if the "needs to be even" code used
badcfehgjilknmporqtsvuxwzy213 # output if the "even or odd" code used
Here's a nifty solution:
def swapem (s):
if len(s) < 2: return s
return "%s%s%s"%(s[1], s[0], swapem (s[2:]))
for str in ("", "a", "ab", "abcdefgh", "abcdefghi"):
print "[%s] -> [%s]"%(str, swapem (str))
though possibly not suitable for large strings :-)
Output is:
[] -> []
[a] -> [a]
[ab] -> [ba]
[abcdefgh] -> [badcfehg]
[abcdefghi] -> [badcfehgi]
If you prefer one-liners:
''.join(reduce(lambda x,y: x+y,[[s[1+(x<<1)],s[x<<1]] for x in range(0,len(s)>>1)]))
Here's a another simple solution:
"".join([(s[i:i+2])[::-1]for i in range(0,len(s),2)])