0

I am currently learning on Codecademy and can't get past one problem:

Write a function called censor that takes two strings, text and word, as input. It should return the text with the word you chose replaced with asterisks.

My code is:

def censor(text, word):
  word_converter = ("*" * len(word))
  words = text.split()
  print(words)
  for bad_word in words:
    if bad_word == word:
      words.replace(bad_word, word_converter)
  print(words)

censor("What the curseword is that", "curseword")

This is what is returned:

Traceback (most recent call last):
  File "C:\Users\andre\PycharmProjects\fun\main.py", line 10, in <module>
    censor("hello i am andrew", "hello")
  File "C:\Users\andre\PycharmProjects\fun\main.py", line 7, in censor
    words.replace(bad_word, word_converter)
AttributeError: 'list' object has no attribute 'replace'

I don't understand why the replace function does not work.

Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
keefxr
  • 13
  • 2
  • 3
    `.replace` works on strings: `'hello world'.replace('world', '*****')` -> `'hello *****'` but you're calling it on a *list* of strings `['hello', 'world'].replace('world', '*****')` and it doesn't support lists. – Boris Verkhovskiy Jul 14 '21 at 20:34
  • How would I use .replace for a list then. Sorry I am new to Python – keefxr Jul 14 '21 at 20:36
  • 2
    you can do `words = [(('*' * len(w)) if w in bad_words else w) for w in words]` right after `words = text.split()` – Boris Verkhovskiy Jul 14 '21 at 20:37

3 Answers3

5

The .replace syntax works on a string but not a list. You can swap an item in the list by using a pointer to the index. Updating the iteration syntax - thanks to comment from Boris

for i, w in enumerate(words):
    if w == word:
        words[i] = "*" * len(w)

Typically, you would build up a new list instead of altering the list you are iterating through, because changing what you are iterating on can cause problems. If you don't try replacing items in the list, you don't need the pointer.

cleaned_words = []
for w in words:
    if w == word:
        cleaned_words.append("*" * len(w))
    else:
        cleaned_words.append(w)
Hammurabi
  • 1,141
  • 1
  • 4
  • 7
  • Why use a list at all? [`replace`](https://docs.python.org/3/library/stdtypes.html#str.replace) returns a new string and replaces ***all*** occurrences of the substring. Just do `text.replace(word, '*'*len(word))`... – Tomerikoo Jul 14 '21 at 20:49
  • @Tomerikoo that's a clabuttic mistake. – Boris Verkhovskiy Jul 14 '21 at 20:53
  • @Boris Can you explain? I have no idea what that word means – Tomerikoo Jul 14 '21 at 20:54
  • @Tomerikoo https://en.wikipedia.org/wiki/Scunthorpe_problem – Boris Verkhovskiy Jul 14 '21 at 20:56
  • @Boris LOL that was a good one. Indeed you are right. I would claim, in my defense, that 'ass' in 'classic' should be censored as well xD – Tomerikoo Jul 14 '21 at 20:58
  • It's funny because every time this comes up [I said "clabuttic"](https://stackoverflow.com/questions/66713265/replace-acronyms-with-their-values-python#comment117931791_66713265) without realizing that I just say the "a" when talking so that the word is pronounceable, but in writing there should be no "a". – Boris Verkhovskiy Jul 14 '21 at 21:07
  • @Boris Same goes in the [only explanation I found](https://slythkris.home.blog/2018/05/14/xliaxtasadako-bold-sartorial-statement/) for this – Tomerikoo Jul 14 '21 at 21:12
1

If you are comfortable with regex, this can be easily done with the sub function, which is the regex version of str.replace. Using the word boundary \b, we can ensure that only full words are replaced and not parts of words. For example:

import re

def censor(text, word):
    return re.sub(rf"\b{word}\b", '*' * len(word), text)

print(censor("What a classic ass", "ass"))

Will give:

What a classic ***

As opposed to:

>>> "What a classic ass".replace("ass", "***")
'What a cl***ic ***'
Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
0

The existing answers are already very good — as @Hammurabi says, replace is a method that only works on string objects, whereas the .split method will return a list of strings — list objects have no replace method.

Here, however, is a one-liner solution that doesn't require learning regular expressions. (I would recommend learning regular expressions at some point — they're extremely powerful — but I know they can be quite daunting for a beginner.)

def censor(text, word):
    return ' '.join((('*' * len(word)) if w == word else w) for w in text.split(' '))
Alex Waygood
  • 6,304
  • 3
  • 24
  • 46