Why can't I call both string methods at the same time?

Hello! :grinning: I was trying to experiment a little bit with string methods and came up with this secret message:

secret_message = "Ieightlikeeighteatingninepizza"

My idea was to separate the string for every “eight” and “nine”. I tried to concatenate both methods at the same time:

decoded =secret_message.split("nine").split("eight")

However, an Attribute Error shows up. It works when I delete one of the .split()(either the “nine” or the "eight"s are still visible), but this is the error that appears when I try to do both:

Traceback (most recent call last):
  File "script.py", line 5, in <module>
    decode1 =secret_message.split("nine").split("eight")
AttributeError: 'list' object has no attribute 'split'

Why does this happen? Thank you!:smile:


The assignment statement

decoded =secret_message.split("nine").split("eight")

… is handled like all other assignment statements: first, the expression on the right is evaluated, then the resulting value is assigned to the variable on the left.

The expression secret_message.split("nine").split("eight")
is evaluated like this: (secret_message.split("nine")).split("eight")

So, when str.split() is called the first time, secret_message.split("nine"), it returns a list (for that is what str.split() does). Then you try to call split() on that list and get the error, which quite precisely identifies the problem.


What does “at the same time” mean anyway? If that is hoping the computer will figure out some kind of “right thing” then that’s not what programming is, is it? We the programmers are the ones doing the telling.

Try not to guess, rather, look at what the different things do and then think about how that can be leveraged. Or, think about what the actions you want are, and implement it yourself.
So for example here you might iterate through each location in the text, and at each position look ahead and ask whether what’s next is “nine”/“eight”, and if yes, then add the current letters to the result, skip ahead by the “nine”/“eight”, and then continue from the new position.

If you think something should/might be capable of doing something you want, but you’re not sure, then read its documentation. Actually, read the documentation for anything you use ever, because how else will you be able to use it correctly? I suppose it might sometimes seem like you’re supposed to memorise what a certain number of things do and then only use those, but in reality you’ll need more things that you can remember and it’s more about being able to browse through a module full of functions, reading what they do and picking one that promises to do something that is useful to what you’re doing.


Thank you! :smile: So, the problem was that with .split()you can only split strings and not lists, right?. I think I misunderstood when I read this other topic, where it provides an example with method chaining:

words = 'HeLlO WoRlD'.lower().title().split()
# ['Hello', 'World']

So, if I got it right, one can concatenate for example .lower() and .title() but not split() and split(), since the second split() would be affecting to a list rather than a string. Is that the case? Thank you!:smile:

Thanks for the good advice!:smile:

method chaining isn’t a thing

if you have a value, you can access attributes from it with the . operator

if you access an attribute of something, then you now have that value, so, sure, you can in turn get attributes of that, but it’s the same thing over again, it’s not special, like how you can use + multiple times in an expression without it being a different thing

.split is also not a thing. if a value has an attribute named split, then you can obtain it, again, with the . operator somevalue.split and if you want to call this value, then you would use the call operator ()

the str type defines an attribute named split, you can find that method either through the str type: str.split or from a string: 'blah'.split

other types may also define attributes named split, and those would be unrelated to str.split

1 Like

Thanks for your help! I think I understand it better now. :+1:

This is how I solved it at the end :smile:, just in case someone wants to see it :heart:

secret_message = "Ieightlikeeighteatingninepizza"

decode1 =secret_message.split("eight")

decode2 = []

for element in decode1:
  if "nine" not in element:
    new_element = element.split("nine")

It prints the list without the intruding words.:+1:

['I', 'like', ['eating', 'pizza']]

You don’t need to test if “nine” is in the string. If it isn’t, then no splitting happens. You would also end up with a more uniform structure, your outermost list would contain lists of strings, you can then join each sublist on space:

> step1 = splitOn "eight" "Ieightlikeeighteatingninepizza"
> step1
> step2 = map (splitOn "nine") step1
> step2
> step3 = map (intercalate " ") step2
> step3
["I","like","eating pizza"]
> f = splitOn "eight" >>> map (splitOn "nine") >>> map (intercalate " ")
> f "Ieightlikeeighteatingninepizza"
["I","like","eating pizza"]

Or if wanting each word separate, concatenate the sublists to flatten it one level

> f = splitOn "eight" >>> map (splitOn "nine") >>> concat
> f "Ieightlikeeighteatingninepizza"

In python concatenating would be something like:

def concat(lists):
    result = []
    for ls in lists:
        result += ls
    return result
1 Like

Thank you so much for your help! :smile:

This made for some good practice using the split() method. Since it didn’t come up in this thread though, I thought I would mention this could be accomplished the way you were originally trying to, by using the replace() method instead.

secret_message = "Ieightlikeeighteatingninepizza"

decoded = secret_message.replace("eight", " ").replace("nine", " ")

I like eating pizza

This could also be thought of as normalizing the different delimiters into just one delimiter, which turns it into an easier problem. Very nice.