Iterating over a string in Python - python

I want to make a script which will type string letters one by one
def autotype(info):
count = len(info) #Countign number of letters of the string
splitlist = list(info)
i = int(count) #getting an error on this line! it accept i=int(0) but my loop doesnt work because of this
while i>0:
sys.stdout.write(splitlist[i])
time.sleep(0.2)
i -= 1
info = str("hello world")
autotype(info)
the error is: list index out of range
how do i fix it?

The length of a list is the number of elements in a list. But, lists start at index 0, and so they will end at index length - 1. So, to fix your code as is, it should be i = count - 1. (You don't need to cast it to an int, it's already one.)
Better yet, rather than iterating using a counter in a while loop, just use a for loop. You can use the for loop to iterate over the characters in a string.
for ch in info:
sys.stdout.write(ch)
sys.stdout.flush() # as mawimawi suggests, if you don't do this, it will
# actually just come out all on one line at once.
time.sleep(0.2)
You also don't need to cast "hello world" to a string - it's already one.

Your script is quite un-pythonic. Here is something that would do the same. Strings are iterables, so:
def autotype(info):
for x in info:
sys.stdout.write(x)
sys.stdout.flush() # you need this, because otherwise its' buffered!
time.sleep(0.2)
That's all you need.

You're starting your loop at i=len(info), which is one more than the final index in the string. The last index in a string (or other iterable) is len(string) - 1, because indices begin at 0.
Note that in Python, you can (and are encouraged to) make use of the natural language constructs and the fact that collections are easy to iterate over:
for letter in reversed(info): # Much clearer way to go backwards through a string
sys.stdout.write(letter)
Since you've clarified in your comments that you actually want to go forwards through the text, you can just take out the reversed bit. The code as you posted will iterate backwards through the text, not forwards -- another benefit to using the standard iteration techniques is that it's much easier to see if you've done something you didn't mean to do!
for letter in info: # Very clear that you're going forward through the string
sys.stdout.write(letter)
Finally, as others have mentioned, you should add an explicit call to sys.stdout.flush() after every write, because otherwise there's no guarantee that you'll see output at regular intervals (it could be written to the buffer but not flushed to the screen until much later).

Lists are zero-indexed, so the last element is at len(info)-1.
To fix this, you need to subtract 1 from the count:
i = int(count) - 1

Indexes are counted from zero... if you have 5 items in a list, then indexes are 0,1,2,3,4
you are setting index out of bounds in this line:
i = int(count)
If count is 5 then max index is 4.
To fix this change that line into:
i = int(count) - 1
Next thing, you won't print the first character.
to fix that change a line:
while i>0:
into:
while i>=0:
By the way all your characters are printed backwards.

Unidiomatically, but concisely:
import time
import sys
autotype = lambda instr:map((lambda ch:(sys.stdout.write(ch), time.sleep(0.2))), instr)
autotype("hello world")
The main problem with the above code is that it's not usual to sequence two functions using tuple if you don't care for their return values.

Here is code to "make a script which will type string letters one by one"
print info
If what you need is to type letters in a string consecutively, there is no need to reinvent the wheel.
More than likely, your question was underspecified.

Related

I got "TypeError: string indices must be integers" even though I'm using integers

I'm pretty early on in python (only learning for loop right now) and I have gotten into a bit of a problem.
As you can see I am using integers (as I have checked using type()) but is still raises the TypeError. Any idea why or how to fix it?
The problem is in line 8 where you are trying to break a string into multiple parts, but with a wrong syntax. You will have to perform slicing operation as
item = my_string[:6] # starting index is 0 by default, and final index is 6-1, i.e., 5
item = my_string[0:6] # starting index is 0, and final index is 6-1, i.e., 5
item = my_string[0:6:1] # starting index is 0, and final index is 6-1, i.e., 5, with a jump of 1 character to the right, i.e., index 0,1,2,3,4 and 5.
any of which will produce the same result, instead of
item = my_string[0,6]
as this will return TypeError which you can easily verify by just executing
"Hello"[0,6]
PS: You can achieve the same end result by doing
print(my_string.split(','))
as suggested by OneCricketeer. Also, if you want the user to input the string, try this
grocery_list = input("enter a comma separated string without spaces: ").split(',')
print(grocery_list)
Have fun coding!
This is just a syntax problem. If you want to get all characters up to index 6, you need to do
my_string[0:6]
In practice, you would usually use a shorter version (with the same functionality):
my_string[:6]
Additionally, in your code, you probably want to do
my_string[pos1:pos2]
Currently, you get pos1 and pos2 but don't use it.
Lastly, you could have searched "How to get the first 6 characters from a string in Python", and you might have found the answer yourself, e.g. here. Searching for your problem online and finding a solution is perhaps the most important skill you need now :)
Happy coding!
def make_list(my_string):
my_string += ' '
grocery_list = []
pos1 = 0
for item in range(my_string.count(",") + 1):
pos2 = my_string.find(",", pos1)
grocery_list += [my_string[pos1:pos2]]
pos1 = pos2+1
print(grocery_list)
call the function make_list('tomato,apple,orange')
This code work in my computer. I hope you get solution of your question.
The problem is that comma , to index the string. You have to use colon :
Correct syntax of slicing is (start:stop[:step])
item = mystring[0:6] # start at index 0, stop at index 6 (not included)
item = mystring[:6] # same notation, start at index 0

The number of differences between characters in a string in Python 3

Given a string, lets say "TATA__", I need to find the total number of differences between adjacent characters in that string. i.e. there is a difference between T and A, but not a difference between A and A, or _ and _.
My code more or less tells me this. But when a string such as "TTAA__" is given, it doesn't work as planned.
I need to take a character in that string, and check if the character next to it is not equal to the first character. If it is indeed not equal, I need to add 1 to a running count. If it is equal, nothing is added to the count.
This what I have so far:
def num_diffs(state):
count = 0
for char in state:
if char != state[char2]:
count += 1
char2 += 1
return count
When I run it using num_diffs("TATA__") I get 4 as the response. When I run it with num_diffs("TTAA__") I also get 4. Whereas the answer should be 2.
If any of that makes sense at all, could anyone help in fixing it/pointing out where my error lies? I have a feeling is has to do with state[char2]. Sorry if this seems like a trivial problem, it's just that I'm totally new to the Python language.
import operator
def num_diffs(state):
return sum(map(operator.ne, state, state[1:]))
To open this up a bit, it maps !=, operator.ne, over state and state beginning at the 2nd character. The map function accepts multible iterables as arguments and passes elements from those one by one as positional arguments to given function, until one of the iterables is exhausted (state[1:] in this case will stop first).
The map results in an iterable of boolean values, but since bool in python inherits from int you can treat it as such in some contexts. Here we are interested in the True values, because they represent the points where the adjacent characters differed. Calling sum over that mapping is an obvious next step.
Apart from the string slicing the whole thing runs using iterators in python3. It is possible to use iterators over the string state too, if one wants to avoid slicing huge strings:
import operator
from itertools import islice
def num_diffs(state):
return sum(map(operator.ne,
state,
islice(state, 1, len(state))))
There are a couple of ways you might do this.
First, you could iterate through the string using an index, and compare each character with the character at the previous index.
Second, you could keep track of the previous character in a separate variable. The second seems closer to your attempt.
def num_diffs(s):
count = 0
prev = None
for ch in s:
if prev is not None and prev!=ch:
count += 1
prev = ch
return count
prev is the character from the previous loop iteration. You assign it to ch (the current character) at the end of each iteration so it will be available in the next.
You might want to investigate Python's groupby function which helps with this kind of analysis.
from itertools import groupby
def num_diffs(seq):
return len(list(groupby(seq))) - 1
for test in ["TATA__", "TTAA__"]:
print(test, num_diffs(test))
This would display:
TATA__ 4
TTAA__ 2
The groupby() function works by grouping identical entries together. It returns a key and a group, the key being the matching single entry, and the group being a list of the matching entries. So each time it returns, it is telling you there is a difference.
Trying to make as little modifications to your original code as possible:
def num_diffs(state):
count = 0
for char2 in range(1, len(state)):
if state[char2 - 1] != state[char2]:
count += 1
return count
One of the problems with your original code was that the char2 variable was not initialized within the body of the function, so it was impossible to predict the function's behaviour.
However, working with indices is not the most Pythonic way and it is error prone (see comments for a mistake that I made). You may want rewrite the function in such a way that it does one loop over a pair of strings, a pair of characters at a time:
def num_diffs(state):
count = 0
for char1, char2 in zip(state[:-1], state[1:]):
if char1 != char2:
count += 1
return count
Finally, that very logic can be written much more succinctly — see #Ilja's answer.

Replacing Odd and Even-indexed characters in a string

How can I replace even and odd-indexed letters in my strings? I'd like to replace odd-indexed characters with uppercased letters and even-indexed characters with lowercased ones.
x=input("Enter String: ")
How can I modify the inputted string?
This sounds a little like a "do my homework for me" post, but I'll help you out, as I need the training myself.
You can do this by breaking down the problem. (As I am quite new with python syntax, I'm gonna assume that the user has already given an input to string x)
Make a loop, or otherwise iterate through the characters of your string
Make sure you have an index number for each character, which increments for each one
Check if the number is even, by using modulus of 2 (%2). This returns the remainder of a number when divided by 2. In the case of even numbers, that will be 0.
If %2 == 0 set letter to lower case, else set letter to upper case.
append letter to new String, which you defined before the loop. You cannot directly alter a single character in a String, because they are immutable. This means that you cannot change the String itself, but you can assign a new String to the variable.
Done. Print and see if it worked.
Code:
x = "seMi Long StRing WiTH COMPLetely RaNDOM CasINg"
result_string = ""
index = 0;
for c in x:
if(index%2 == 0):
result_string += c.lower()
else:
result_string += c.upper()
index+=1
print(result_string)
s=input()
l=[]
s=s.lower()
l=[i.upper() if s.index(i)%2==0 else i for i in s ]
print("".join(l))
x = 'myname'
for item in range(len(x)):
if item%2==0:
print(x[item].upper())
else:
print(x[item].lower())
this is the for loop i was referring to. but the thing with this line of code is that it is specific to the value you have assigned to the variable x where as the function i provided above can take any string value without us having to repeat the code each time.
def myfunc(string):
result=''
for x in range(len(string)):
if x%2==0:
result=result+string[x].upper()
else:
result=result+string[x].lower()
return result
The above is a function for the question you asked.
A non-function for loop might be easier to grasp right now (like you I am very new to Python as well. So for me it was easier to understand the for loop before I got into functions. Look at my next post for the same.

My Python code is only selecting half of a list's contents?

I'm very new to Python, and I'm going through some example projects I found online but I'm stuck on my palindrome checker at the moment.
Right now, my code takes a word as an input, splits it in half, saves each part into separate variables, makes both of the variables lists, and from there it SHOULD reverse the second list so I can compare it to the first, but from what I've gathered trying to fix it, it's only appending half of the selection to the new list.
For example, if I enter "racecar", it'll split it into "race" and "ecar" just fine, but then when I go to reverse "ecar" it only gives me back "['c', 'e']". (Also, if I switch the variables around to reverse the first half, I get the same error)
I've been trying to figure it out for quite a while now and I'm not making any progress so some help would be very much appreciated!
Ninja Edit: If there's an easier way to do this (which I'm sure there is) I'd love to know, but I still want to figure out what I've done wrong in the code I already have so I can try to learn from it
Here's my code so far:
print "Please enter a word you want to check is a palindrome"
input = raw_input('> ')
#Gets lengths of input
full_length = len(input)
split_length = len(input) / 2
#If word has an even length split like this
if full_length % 2 == 0:
first_half = input[0: split_length]
second_half = input[split_length:full_length]
#If word does not have even length split like this
else:
first_half = input[0:split_length+1]
second_half = input[split_length:full_length]
#Make both halves lists
first_half_list = list(first_half)
print first_half_list
second_half_list = list(second_half)
print second_half_list
# Reverse second half
rev_second_half = []
for x in second_half_list:
current_letter = second_half_list[0]
second_half_list.remove(second_half_list[0])
rev_second_half.insert(0, current_letter)
print rev_second_half
"""
#Check to see if both lists are identical
#If they are identical
print "This word is a palindrome!"
#If they are not identical
print "This word is not a palindrome."
"""
And this is the output I get when I enter 'racecar':
racecar
['r','a','c','e']
['e','c','a','r']
['c', 'e']
There's a lot of unnecessary work going on. No need to convert to lists; the interpreter can manage this all for you. No need to manually reverse a string; use slicing. No need to manually declare the indices of the first and last characters in your string; the interpreter knows where they are. Here's a fixed version of the code; you can view a demo at IDE One:
input = 'racecar'
#Gets lengths of input
full_length = len(input)
split_length = len(input) / 2
#If word has an even length split like this
if full_length % 2 == 0:
first_half = input[:split_length]
second_half = input[split_length:]
#If word does not have even length split like this
else:
first_half = input[:split_length+1]
second_half = input[split_length:]
print first_half
print second_half
rev_second_half = second_half[::-1]
print rev_second_half
race
ecar
race
Notice the way that the second half is getting reversed, by using a slice with a negative iteration step? You can just do that once, to your source string, and compare the result to the original. Now you have a one line method to check if a string is a palindrome: input == input[::-1]
A bit more on slicing syntax (you might like to check out this question). input[::-1] is exactly the same as input[0:len(input):-1]. The colons separate the three arguments, which are start : end : step. The first two create a range which includes start and everything between it and end, but not end itself. Not specifying start or end causes the interpreter to assume you mean "use 0" and "use len", respectively. Not specifying step causes an assumption of 1. Using a negative step means "start at end and go backwards by magnitude of step".
If you want to omit arguments and specify a range with a slice, you need to include the colons, so the interpreter can tell which arguments are omitted. For example, input[-1] will return the last element of input, because no colons means you're specifying an index, and negative means "go backwards from the end", so print input[:-1] would yield "raceca" if your input was "racecar".
As for what was going wrong with your code, the problem is in your reversing loop.
for x in second_half_list:
current_letter = second_half_list[0]
second_half_list.remove(second_half_list[0])
rev_second_half.insert(0, current_letter)
You're removing items from the list you're iterating through. Don't do that, it's a great way to cause problems; it's why you're only getting half the list in this case. There's also needless copying going on, though that won't cause incorrect results. Finally, you're not using your iterated variable at all, which is a sure sign of some sort of problem with your loop code. Here, if you fixed the list mutation but continued using second_half_list[0], you'd get that letter repeated len(second_half_list) times. If you really need to actually reverse a list, you can do it like this instead:
for x in second_half_list:
rev_second_half.insert(0, x)
But you should only actually iterate the list if you need some sort of side effects during the iteration. For a pure reversal in python, you want this, which will perform better:
rev_second_half = [reversed(second_half_list)]
To reverse the string (not in place):
rev_second_half = second_half_list[::-1]
To extend:
I'd suggest keeping the halves as strings, as you can then just compare them with:== and the above reversing technique also works on strings.
The reason you're only getting two values is you're mutating your list while you iterate on it -- you just shouldn't do this, if only because it's a pain to reason about. As an example:
In [34]: nums = range(5) # [0, 1, 2, 3, 4]
In [35]: for num in nums:
....: print "num", num
....: print "nums", nums
....: nums.remove(nums[0])
....:
num 0
nums [0, 1, 2, 3, 4]
num 2
nums [1, 2, 3, 4]
num 4
nums [2, 3, 4]
Notice that this only looped three times. The first time through, everything's dandy, but you remove the first element. However, Python's looping logic thinks it has to go to the second item -- but you removed the first item! Does that mean the second item now, or the second item when things started? For Python's internals, it means the second item now -- which is the third item when things started (i.e. the value 2). From there, stuff just snowballs.
The lesson here is don't mutate a list while you iterate on it. Just use the other means for reversing folks have mentioned here.

Display the number of lower case letters in a string

This is what I have so far:
count=0
mystring=input("enter")
for ch in mystring:
if mystring.lower():
count+=1
print(count)
I figured out how to make a program that displays the number of lower case letters in a string, but it requires that I list each letter individually: if ch=='a' or ch=='b' or ch=='c', etc. I am trying to figure out how to use a command to do so.
This sounds like homework! Anway, this is a fun way of doing it:
#the operator module contains functions that can be used like
#their operator counter parts. The eq function works like the
#'=' operator; it takes two arguments and test them for equality.
from operator import eq
#I want to give a warning about the input function. In python2
#the equivalent function is called raw_input. python2's input
#function is very different, and in this case would require you
#to add quotes around strings. I mention this in case you have
#been manually adding quotes if you are testing in both 2 and 3.
mystring = input('enter')
#So what this line below does is a little different in python 2 vs 3,
#but comes to the same result in each.
#First, map is a function that takes a function as its first argument,
#and applies that to each element of the rest of the arguments, which
#are all sequences. Since eq is a function of two arguments, you can
#use map to apply it to the corresponding elements in two sequences.
#in python2, map returns a list of the elements. In python3, map
#returns a map object, which uses a 'lazy' evaluation of the function
#you give on the sequence elements. This means that the function isn't
#actually used until each item of the result is needed. The 'sum' function
#takes a sequence of values and adds them up. The results of eq are all
#True or False, which are really just special names for 1 and 0 respectively.
#Adding them up is the same as adding up a sequence of 1s and 0s.
#so, map is using eq to check each element of two strings (i.e. each letter)
#for equality. mystring.lower() is a copy of mystring with all the letters
#lowercase. sum adds up all the Trues to get the answer you want.
sum(map(eq, mystring, mystring.lower()))
or the one-liner:
#What I am doing here is using a generator expression.
#I think reading it is the best way to understand what is happening.
#For every letter in the input string, check if it is lower, and pass
#that result to sum. sum sees this like any other sequence, but this sequence
#is also 'lazy,' each element is generated as you need it, and it isn't
#stored anywhere. The results are just given to sum.
sum(c.islower() for c in input('enter: '))
You have a typo in your code. Instead of:
if my.string.lower():
It should be:
if ch.islower():
If you have any questions ask below. Good luck!
I'm not sure if this will handle UTF or special characters very nicely but should work for at least ASCII in Python3, using the islower() function.
count=0
mystring=input("enter:")
for ch in mystring:
if ch.islower():
count+=1
print(count)
The correct version of your code would be:
count=0
mystring=input("enter")
for ch in mystring:
if ch.islower():
count += 1
print(count)
The method lower converts a string/char to lowercase. Here you want to know if it IS lowercase (you want a boolean), so you need islower.
Tip: With a bit of wizardry you can even write this:
mystring= input("enter")
count = sum(map(lambda x: x.islower(), mystring))
or
count = sum([x.islower() for x in mystring])
(True is automatically converted to 1 and False to 0)
:)
I think you can use following method:
mystring=input("enter:")
[char.lower() for char in mystring].count( True ) )

Categories