4

I am trying to build a simple function to censor a specific word in a sentence. For example "hello hello hi" to "***** ***** hi" if I feed censor("hello hello hi", "hello"). Assuming I will not receive punctuation and sentences with upper case letters or empty strings.

After researching online I understand there is simpler solution, for example:

def censor(text, word):
    return text.replace(word, "*" * len(word)) 

I still want to understand from a learning perspective, what I did wrong in the below more complicated code.

def censor(text,word):
    split_text = text.split()
    length = len(word)
    for item in split_text:
        if item == word:
            item = ("*" * length)
        else:
            item = item
    return " ".join(split_text)
Victor Yip
  • 295
  • 1
  • 5
  • 11
  • Does the code not work? Or else why do you assume you did something "wrong"? – Silvio Mayolo Jul 28 '15 at 23:42
  • You are not updating the values in `split_text` at all. Basically `lst = [0, 1]; a = lst[0]; a = 2` won't affect `lst`. – Ashwini Chaudhary Jul 28 '15 at 23:42
  • I am doing this on a codecademy course, the virtual machine gave this error: **Oops, try again. Your function fails on censor("hey hey hey","hey"). It returns "hey hey hey" when it should return "*** *** ***". – Victor Yip Jul 28 '15 at 23:43
  • Changing `item` doesn't change `split_test`. Your code is simply splitting the text and then joining it back together – Bobbyrogers Jul 28 '15 at 23:45

5 Answers5

2

The solution I came up with was a little simpler and gets the job done.

1.I make sure both arguments are a string and then check if the word is in the text string. 2.Then if the word is in the text string I get the length of the word. 3.Then I replace the word inside the string with * however many times the word is long.

def censor(text,word):
t=str(text)
w=str(word)
if w in t:
    l=len(w)
    item = ("*" * l)
    return t.replace(w,item)
else:
    return t
Mike - SMT
  • 14,784
  • 4
  • 35
  • 79
1

The for-loop in your code does not reference or modify the actual item in split_text. Try this:

for item in range(len(split_text)):
        if item == word:
            split_text[item] = ("*" * length)
            ...

Also, it is generally considered poor practice to modify a list within a loop. See this post: How to modify list entries during for loop?

Community
  • 1
  • 1
deezy
  • 1,480
  • 1
  • 11
  • 22
1

You probably see what you did wrong based on the comments. But you might not understand why (and based on your question I think you may be interested to know).

In python you can roughly divide the datatypes into 2 categories: mutable and immutable. The basic difference is that you can create references to mutable type variables whereas you can only pass around the values of immutable type variables. Refer to this question for examples.

In your case you are iterating through a list containing strings. Strings happen to be immutable. So when you change item it won't have any effect on the corresponding element in split_text.

If instead you were iterating through a list of lists, you would actually be able to change the list elements using the placeholder variable.

Illustrated with an example:

x = [["foo"],["foo","bar"]]
for i in x:
    if len(i) == 1:
        i[0] += "bar"
    else:
        i.append("foobar")
print(x) #[["foobar"],["foo","bar","foobar"]]  

Hope this helps! :)

Community
  • 1
  • 1
Michael S Priz
  • 1,116
  • 7
  • 17
  • OMG this is amazing. I was a bit aware of this difference unconsciously but the online course didn't really mention it - probably too conceptual for an entry course. But now as you point it out it explains some of the mistakes I made in other exercises too. Just one question - how do you identify mutable vs immutable datatypes when you code? are there any rule of thumbs or simply memorisation? Thank you. – Victor Yip Jul 29 '15 at 14:08
  • 1
    Without getting too abstract, things which are dynamically sized will probably be mutable. Numbers, booleans, and strings will be immutable. refer to [this wikibook](https://en.wikibooks.org/wiki/Python_Programming/Data_Types#Mutable_vs_Immutable_Objects) for examples. If you are interested in a more abstract/philosophical analysis on what types should be immutable vs. mutable you shouuld check out a book on OOP design ideologies! :) – Michael S Priz Jul 29 '15 at 14:26
1

As mentioned other answers, you can't change list value in for loop.

You can access list value using enumerate, get index of value. And, you don't need else because you don't change value.

for idx, item in enumerate(split_text):
    if item == word:
        split_text[idx] = ("*" * length)
changhwan
  • 1,000
  • 8
  • 22
  • Thanks, enumerate was one of the early functions I learnt from the course but never really practised. This answer is clean yet utilises beginner knowledge. – Victor Yip Jul 29 '15 at 14:14
0

There is a difference between the variable and the value that the variable has at a given point at the running time. The statement

for item in split_text:

Sequentially assigns the value of each item in split_text to item.

item = ("*" * length)

Changes item to a new value computed from its length. But split_text remains exactly what it was at the beginning.

Michael S Priz
  • 1,116
  • 7
  • 17
innoSPG
  • 4,588
  • 1
  • 29
  • 42