Choosing which elements to replace in Python - python

When I use the replace function I can input an additional 3rd argument which describes how many occurences of the particular character I might want to change.
For Example -
input_string = input()
first_char = input_string[0]
modified_string = input_string.replace(first_char, "$", input_string.count(first_char)-1)
print(modified_string)
The above code gives the following output:
Input: heyhhdh
Output: $ey$$dh
It replaced the h starting from the first occurrence but is there a way where I can specify where to start?
For instance in the problem I'm working on I need to leave the first character so is there a way to specify that in python
Edit:
The following line of code commented by Tarique performs my task
modified_string = first_char + input_string[1:].replace(first_char, "$", input_string.count(first_char)-1)
However is there a way to do this using only string functions like modifying the arguments in the replace function?

You could do what you already got, except without the pointless counting:
>>> first_char + input_string[1:].replace(first_char, '$')
'hey$$d$'
A single replace without anything else can't do it, but two can:
>>> input_string.replace(first_char, '$').replace('$', first_char, 1)
'hey$$d$'
That's only two linear-time operations instead of three, and for longer strings it's faster. For input_string = 'hey$$d$' * 10**6 the first way takes me 12.1 ms and the second way takes me 9.4 ms.
A third but silly and slow (30.9 ms) way, simulating backwards-replacing by reversing the string before and after:
>>> input_string[::-1].replace(first_char, '$', input_string.count(first_char) - 1)[::-1]
'hey$$d$'

Tarique's method is going to be the only way involving the replace method. You can specify the maximum number of characters to replace (see the bottom of this python documentation page), but that is the opposite of what you want. This is the same for Python 3, as seen here.

Related

How can I distribute the output of a list I made in python in my notepad

I was trying to solve up a problem that was going on cause my IDE could not retain a sequence of numbers cause of the range function which works as so.
And i made a Previous question about it so this is a follow-up to the question. Here's my list comment on the previous question.
I actually made some adjustments by adding a line; 'My_list = list(range(100)) before applying your code so it actually worked. But it combines the answers without commas, for example 10 does this '0123456789' instead of '0,1,2,3,4,5,.....8,9'. any suggestions?
I decided to post this question not to allow the other question go out of context (as i was advised to).
Any suggestions?
You need to understand how strings works in Python.
Strings are constants (literals) kept in a closed bucket. In official docs you can find that "Strings are immutable sequences of Unicode code points".
But programmers need to change or manipulate text in a programmable way. In your case you want:
"[x1][space][comma][x2][comma]...[xn][space][comma]"
where "xn" is a number, and " ," is constant.
In order to achieve this, in a programmable way, programmers can use "masks" to tell the software where they want to place their changes. One can use string format operators:
"%d , %f" %(my_first_integer, my_float)
[0][1][2][3][4][\0]
# Hey Python, return a string, using the above template,
# but place useful stuff where you find magic keywords.
Which means:
Create a 6 positions sequence;
In [0], place my_integer of type int converted into chr;
In [1], copy " ";
In [2], copy ",".
In [3], copy " ";
In [4], place my_float of type float converted into chr;
In [5], place "\0" so the string is over. (Automatically placed in Python)
There are other ways to do this, i.e., the string object has a handy method called formatto handle this construction:
my_integer = 2
my_string = "{0}*pi = {1}".format(my_integer, my_integer*3.14)
print(my_string)
# 2*pi = 6.28
The programmer will achieve the same final result using one or another startegy.
In Python, as well as in other languages, one can combine strings, concatenate, get sub-strings and so on, using specific methods and/or operators.
In order to keep readability you maybe (I guess) want to place each value in a line. In strings you can use special characters like \n for new lines.
my_list = list(range(100))
# ... useful code here and there ...
with open("output.txt", "w") as o:
o.write("My list:\n")
o.write("\tSize: {0}\n\n".format(len(my_list)))
o.write("\t----start----\n")
for i in range(len(my_list)):
o.write("%d\n" % my_list[i])
o.write("\n\t----end----\n")
# That writes:
# My list:
# Size: 100
#
# ----start----
# 0
# 1
# 2
# 3
...
# 99
#
# ----end----
Remember, this is not a comprehensive guide, but a layman one. I'm skipping a lot of boring words and technical details that you'll better find in Python books and courses.
You just need to insert a comma after printing each number:
my_list = list(range(100))
with open("output.txt", "w") as o:
for i in range(len(my_list)):
o.write("%d," % my_list[i]) # Here, after '%d' you can place a comma, or any text you want

What's the most Pythonic way to remove a number from start of a string?

I have various strings
123_dog
2_fish
56_cat
45_cat_fish
There is always one number. Always a '_' after the number.
I need to remove the number and the underscore. I can use regex, but I wonder if there is some pythonic way that uses builtin methods?
(I'm an experienced coder - but new to Python.)
Assuming that there is always an underscore after the number, and that there is always exactly a single number, you can do this:
s = '45_cat_fish'
print s.split('_', 1)[1]
# >>> cat_fish
The argument to split specifies the maximum number of splits to perform.
Using split and join:
>>> a="45_cat_fish"
>>> '_'.join(a.split('_')[1:])
'cat_fish'
Edit: split can take a maxsplit argument (see YS-L answer), so '_'.join is unnecessary, a.split('_',1)[1]…
Using find
>>> a[a.find('_')+1:]
'cat_fish'
Another way is:
s = "45_cat_fish"
print ''.join(c for c in s if c.isalpha() or c == '_')[1:]
gives cat_fish

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

Python trick in finding leading zeros in string

I have a binary string say '01110000', and I want to return the number of leading zeros in front without writing a forloop. Does anyone have any idea on how to do that? Preferably a way that also returns 0 if the string immediately starts with a '1'
If you're really sure it's a "binary string":
input = '01110000'
zeroes = input.index('1')
Update: it breaks when there's nothing but "leading" zeroes
An alternate form that handles the all-zeroes case.
zeroes = (input+'1').index('1')
Here is another way:
In [36]: s = '01110000'
In [37]: len(s) - len(s.lstrip('0'))
Out[37]: 1
It differs from the other solutions in that it actually counts the leading zeroes instead of finding the first 1. This makes it a little bit more general, although for your specific problem that doesn't matter.
A simple one-liner:
x = '01110000'
leading_zeros = len(x.split('1', 1)[0])
This partitions the string into everything up to the first '1' and the rest after it, then counts the length of the prefix. The second argument to split is just an optimization and represents the number of splits to perform, meaning the function will stop after it found the first '1' instead of splitting it on all occurences. You could just use x.split('1')[0] if performance doesn't matter.
I'd use:
s = '00001010'
sum(1 for _ in itertools.takewhile('0'.__eq__, s))
Rather pythonic, works in the general case, for example on the empty string and non-binary strings, and can handle strings of any length (or even iterators).
If you know it's only 0 or 1:
x.find(1)
(will return -1 if all zeros; you may or may not want that behavior)
If you don't know which number would be next to zeros i.e. "1" in this case, and you just want to check if there are leading zeros, you can convert to int and back and compare the two.
"0012300" == str(int("0012300"))
How about re module?
a = re.search('(?!0)', data)
then a.start() is the position.
I'm using has_leading_zero = re.match(r'0\d+', str(data)) as a solution that accepts any number and treats 0 as a valid number without a leading zero

Python text encryption: rot13

I am currently doing an assignment that encrypts text by using rot 13, but some of my text wont register.
# cgi is to escape html
# import cgi
def rot13(s):
#string encrypted
scrypt=''
alph='abcdefghijklmonpqrstuvwxyz'
for c in s:
# check if char is in alphabet
if c.lower() in alph:
#find c in alph and return its place
i = alph.find(c.lower())
#encrypt char = c incremented by 13
ccrypt = alph[ i+13 : i+14 ]
#add encrypted char to string
if c==c.lower():
scrypt+=ccrypt
if c==c.upper():
scrypt+=ccrypt.upper()
#dont encrypt special chars or spaces
else:
scrypt+=c
return scrypt
# return cgi.escape(scrypt, quote = True)
given_string = 'Rot13 Test'
print rot13(given_string)
OUTPUT:
13 r
[Finished in 0.0s]
Hmmm, seems like a bunch of things are not working.
Main problem should be in ccrypt = alph[ i+13 : i+14 ]: you're missing a % len(alph) otherwise if, for example, i is equal to 18, then you'll end out of the list boundary.
In your output, in fact, only e is encoded to r because it's the only letter in your test string which, moved by 13, doesn't end out of boundary.
The rest of this answer are just tips to clean the code a little bit:
instead of alph='abc.. you can declare an import string at the beginning of the script and use a string.lowercase
instead of using string slicing, for just one character it's better to use string[i], gets the work done
instead of c == c.upper(), you can use builtin function if c.isupper() ....
The trouble you're having is with your slice. It will be empty if your character is in the second half of the alphabet, because i+13 will be off the end. There are a few ways you could fix it.
The simplest might be to simply double your alphabet string (literally: alph = alph * 2). This means you can access values up to 52, rather than just up to 26. This is a pretty crude solution though, and it would be better to just fix the indexing.
A better option would be to subtract 13 from your index, rather than adding 13. Rot13 is symmetric, so both will have the same effect, and it will work because negative indexes are legal in Python (they refer to positions counted backwards from the end).
In either case, it's not actually necessary to do a slice at all. You can simply grab a single value (unlike C, there's no char type in Python, so single characters are strings too). If you were to make only this change, it would probably make it clear why your current code is failing, as trying to access a single value off the end of a string will raise an exception.
Edit: Actually, after thinking about what solution is really best, I'm inclined to suggest avoiding index-math based solutions entirely. A better approach is to use Python's fantastic dictionaries to do your mapping from original characters to encrypted ones. You can build and use a Rot13 dictionary like this:
alph="abcdefghijklmnopqrstuvwxyz"
rot13_table = dict(zip(alph, alph[13:]+alph[:13])) # lowercase character mappings
rot13_table.update((c.upper(),rot13_table[c].upper()) for c in alph) # upppercase
def rot13(s):
return "".join(rot13_table.get(c, c) for c in s) # non-letters are ignored
First thing that may have caused you some problems - your string list has the n and the o switched, so you'll want to adjust that :) As for the algorithm, when you run:
ccrypt = alph[ i+13 : i+14 ]
Think of what happens when you get 25 back from the first iteration (for z). You are now looking for the index position alph[38:39] (side note: you can actually just say alph[38]), which is far past the bounds of the 26-character string, which will return '':
In [1]: s = 'abcde'
In [2]: s[2]
Out[2]: 'c'
In [3]: s[2:3]
Out[3]: 'c'
In [4]: s[49:50]
Out[4]: ''
As for how to fix it, there are a number of interesting methods. Your code functions just fine with a few modifications. One thing you could do is create a mapping of characters that are already 'rotated' 13 positions:
alph = 'abcdefghijklmnopqrstuvwxyz'
coded = 'nopqrstuvwxyzabcdefghijklm'
All we did here is split the original list into halves of 13 and then swap them - we now know that if we take a letter like a and get its position (0), the same position in the coded list will be the rot13 value. As this is for an assignment I won't spell out how to do it, but see if that gets you on the right track (and #Makoto's suggestion is a perfect way to check your results).
This line
ccrypt = alph[ i+13 : i+14 ]
does not do what you think it does - it returns a string slice from i+13 to i+14, but if these indices are greater than the length of the string, the slice will be empty:
"abc"[5:6] #returns ''
This means your solution turns everything from n onward into an empty string, which produces your observed output.
The correct way of implementing this would be (1.) using a modulo operation to constrain the index to a valid number and (2.) using simple character access instead of string slices, which is easier to read, faster, and throws an IndexError for invalid indices, meaning your error would have been obvious.
ccrypt = alph[(i+13) % 26]
If you're doing this as an exercise for a course in Python, ignore this, but just saying...
>>> import codecs
>>> codecs.encode('Some text', 'rot13')
'Fbzr grkg'
>>>

Categories