3

I keep getting an error in Python when I try to split a single word. From what I read, this is because the default split() command looks for whitespace. The problem is, I want the second assigned variable (asset in this case) to return nothing or null. This is what I am working with:

slack_text.startswith("!help"):
command, asset = slack_text.split() 
    if asset != "":
        if asset == "commandlist":
            slack_reply = "Available Commands: !addme, !getBalance, !buy <asset> <quantity>"
        elif asset == "ships":
            slack_reply = getAllShips()
        elif asset == "buildings":
            slack_reply = getAllBuildings()
        elif shipExists(asset):
                        slack_reply = getShip(asset)
        elif buildingExists(asset):
             slack_reply = getBuilding(asset)
        else:
             slack_reply = "Not a valid asset."
    else:
        slack_reply = "Available help modifiers are: commandlist, <ship_name>, <building_name>. (!help <modifier>)"

So with this code, I can type '!help ships' in Slack and cast no error and return the getAllShips() function. But if I type simply '!help', Python casts an error.

I basically want to be able to return a statement if there is no modifier. However, not having a modifier casts an error. Is there something else I can do to approach this problem? Can someone point me in the right direction here?

snovosad54
  • 361
  • 1
  • 3
  • 13

3 Answers3

11

One solution is to make sure there are always at least two items in the sequence (by adding something to the end) then slice the first two items of the sequence.

For example:

command, asset = (slack_text.split() + [None])[:2]

or:

command, asset, *_ = slack_text.split() + [None]

(here the variable _ ends up with any extra items)

Of course you could also just do it the old-fashioned way:

 command = slack_text.split()[:2]
 if len(command) > 1:
     command, asset = command
 else:
     command, asset = command[0], None
kindall
  • 178,883
  • 35
  • 278
  • 309
  • I disagree that either of these are good solutions. They incur performance penalties vs catching an error – user3684792 May 08 '18 at 20:33
  • 1
    Is this really a standard approach, to concatenate a list? I don't think I've seen that before. – roganjosh May 08 '18 at 20:34
  • Also, if the input could ever be an empty string, this wouldn't work because `split()` on an empty string returns `[]` so you're still only going to have one item in the list. I'm not sure why you're dodging `try`/`except`. – roganjosh May 08 '18 at 20:38
  • @roganjosh make an answer – user3684792 May 08 '18 at 20:43
  • The handler should only run when a message is entered in the same channel as the slackbot. I don't think the string could ever be empty. Although, I guess someone could enter whitespace. I might need to force the handler to only run when a '!' is present, but even then, it would still have to check all the messages. – snovosad54 May 09 '18 at 17:56
  • Hey, OP wanted a workaround (i.e. a way to prevent the error) so that's what I provided. I'm not sure the performance of processing user input really matters that much today (an Apple II running at 1 MHz was sufficiently responsive), but I will say that exception handling is relatively time-consuming. The `try/except` version of this is ~3 times slower in the exceptional case than in the non-exceptional case, and the non-exceptional case is only slightly faster than my "extend and slice" approach, which is about the same speed regardless of input. – kindall May 10 '18 at 14:52
  • Not knocking it, I tried both methods and prefer this for my application. There isn't a case where split would ever be used on an empty string in my code. – snovosad54 May 18 '18 at 15:58
4

In Python there is a notion of "better to ask for forgiveness than permission". In other words, just try what you think might work and then recover from it if it doesn't, rather than try check that it might work in the first place. An example would be trying to access a list index that doesn't exist, rather than checking the length of the list first. There are debates about how far this goes e.g. here among many others.

The simplest example here would be:

command = '!help'
split_string = command.split()
try:
    modifiers = split_string[1]
except IndexError: # Well, seems it didn't work
    modifiers = None

It's not a good idea to just blanket except all errors. Although you're recovering from a failure, you know beforehand what might go wrong here so you should catch that specific error.

roganjosh
  • 12,594
  • 4
  • 29
  • 46
0

Why not search for white space first and then process the split?

if ' ' in slack_text:

< your code >

Yuca
  • 6,010
  • 3
  • 22
  • 42
  • Then you're searching the text twice, which would be slower. Although I suppose you've already accepted a ton of slowness if you're using Python. – Timmmm Sep 29 '21 at 11:15
  • @Timmmm still no reason to add unnecessary slowness ;) – Martin Oct 01 '21 at 13:45