7
\$\begingroup\$

I'm creating a Discord Bot and i made a !love command which is testing the love rate between 2 persons or entities.

Example : !love Peignoir Emma

Result: The loverate between Peignoir and Emma is x%

Here's the code :

@bot.command(name="love", aliases=["l"])
async def love(ctx, Personne1, Personne2):
    if ctx.message.channel.id != 763352957579690018:
        printIt = 1
        wordBanList = ['@everyone', '@here', '<@&763489250162507809>','<@&777564025432965121>','<@&822200827347075132>',
                       '<@&763815680306184242>','<@&764422266560839680<','<@&763815728972300338>','<@&763815728972300338>',
                       '<@&763815228323528725>','<@&763815784904261632>','<@&764422166116171806>','<@&764422057353936897>',
                       '<@&804807279043674143>','<@&828664814678179861>','<@&823562218095640646>','<@&823638574809219163>']
        LoveRate = str(random.randrange(0, 100))
        for y in range(len(wordBanList)):
            if(Personne1 == wordBanList[y] or Personne2 == wordBanList[y]):
                printIt = 0

        if(printIt == 0):
            await ctx.send("Tu t'es pris pour qui ?")
            if debug == True:
                print("[DEBUG] !love : Someone tried to use a banned word !")
        else:
            await ctx.send("L'amour entre **"+Personne1+"** et **"+Personne2+"** est de **"+LoveRate+"%** <:flushed:830502924479758356>")
            if debug == True:
                print("[DEBUG] !love : The love rate ("+LoveRate+"%) between "+Personne1+" and "+Personne2+" has been printed in channel ID "+str(ctx.channel.id))
    else:
        botChannel = discord.utils.get(ctx.guild.channels, id=768194273970880533)
        messageBot = await ctx.send("Va faire cette commande dans "+botChannel.mention+" ou je te soulève <:rage:831149184895811614>")
        await asyncio.sleep(5)
        await ctx.message.delete()
        await messageBot.delete()

I'm pretty sure this code is optimizable, but i don't know how, can somebody help me :) ?

\$\endgroup\$

2 Answers 2

6
\$\begingroup\$

Never iterate over indices in python

In Python this pattern is generally not recommended:

for y in range(len(wordBanList)):
    if(Personne1 == wordBanList[y] or Personne2 == wordBanList[y]):
        printIt = 0

You almost never need to iterate over and access elements by their indices. Instead you should directly iterate over the elements of the iterable:

for word in wordBanList:
    if Personne1 == word or Personne2 == word:
        printIt = 0

Also notice that you do not need the parentheses around the or statement.

If you do need the index as well you can use built-in enumerate:

for index, word in enumerate(wordBanList):
    ...

Membership tests

As Harsha pointed out, sets are optimized for membership tests, so use them when possible. Doing so can also make your assignment of printIt way more concise:

wordBanList = {'@everyone', '@here', '<@&763489250162507809>', ...}
printIt = 0 if (Personne1 in wordBanList or Personne2 in wordBanList) else 1

Parantheses are also not required here, but might improve readability.


Naming convention

PEP 8:

Function names should be lowercase, with words separated by underscores as necessary to improve readability.

Variable names follow the same convention as function names.

Variable and function names should follow snake_case.


debug == True

As a general rule of thumb, you should always use is with the built-in constants: True, False and None. It might also suffice to check debug for truthiness (if debug:). This however depends on the use case, since the expressions are not equivalent. The second one will fail for all falsey values, not just False (e.g. an empty string, an empty list). If you're interested, you can read more about Truthy and Falsy Values in Python.


String formatting

LoveRate = str(random.randrange(0, 100))

LoveRate (or better love_rate) should not be of type str, since you only need its str-represantation to print it. You can let Python handle formatting for you, by using f-strings. That way you never have to think about casting to str.

print("[DEBUG] !love : The love rate ("+LoveRate+"%) between "+Personne1+" and "+Personne2+" has been printed in channel ID "+str(ctx.channel.id))

becomes

print(f"[DEBUG] !love : The love rate ({LoveRate}%) between {Personne1} and {Personne2} has been printed in channel ID {ctx.channel.id}")

A few other small points:

  1. Instead of wrapping the main code inside an if-else-block we can use ctx.message.channel.id == 763352957579690018 as an exit condition.
  2. print_it is of type int, but basically acts as a variable of type bool. We can also eliminate that variable entirely, since we only use it once.
  3. Line length should ideally not exceed 80 characters. You can use parantheses to conveniently concatenate strings across multiple lines, as described in this reddit tip. My IDE is set to hard wrap only at 100 characters, so that's the maximum length for the lines below.
@bot.command(name="love", aliases=["l"])
async def love(ctx, personne1, personne2):
    if ctx.message.channel.id == 763352957579690018:
        bot_channel = discord.utils.get(ctx.guild.channels, id=768194273970880533)
        message_bot = await ctx.send(f"Va faire cette commande dans {bot_channel.mention} "
                                     f"ou je te soulève <:rage:831149184895811614>")
        await asyncio.sleep(5)
        await ctx.message.delete()
        await message_bot.delete()
        return

    word_ban_list = {'@everyone', '@here', '<@&763489250162507809>', '<@&777564025432965121>',
                     '<@&822200827347075132>', '<@&763815680306184242>', '<@&764422266560839680<',
                     '<@&763815728972300338>', '<@&763815728972300338>', '<@&763815228323528725>',
                     '<@&763815784904261632>', '<@&764422166116171806>', '<@&764422057353936897>',
                     '<@&804807279043674143>', '<@&828664814678179861>', '<@&823562218095640646>',
                     '<@&823638574809219163>'}

    love_rate = str(random.randrange(0, 100))

    if personne1 in word_ban_list or personne2 in word_ban_list:
        await ctx.send("Tu t'es pris pour qui ?")
        if debug:
            print("[DEBUG] !love : Someone tried to use a banned word !")
    else:
        await ctx.send(f"L'amour entre **{personne1}** et **{personne2}** est de **{love_rate}%** "
                       f"<:flushed:830502924479758356>")
        if debug:
            print(f"[DEBUG] !love : The love rate ({love_rate}%) between {personne1} and"
                  f"{personne2} has been printed in channel ID {ctx.channel.id}")
\$\endgroup\$
3
  • 2
    \$\begingroup\$ Wow! Thank you for your answer, it truly helped me. I'm very happy to have such answers on my first question, Stack Exchange's community is very friendly and helpful ! :) If you wanna check out our project on Github, you can click here \$\endgroup\$
    – Daf'ium
    Commented Apr 12, 2021 at 22:32
  • 2
    \$\begingroup\$ @JSuisEnPeignoir I'd also recommend using logging module over those print statements. \$\endgroup\$
    – hjpotter92
    Commented Apr 13, 2021 at 2:05
  • \$\begingroup\$ @hjpotter92 Alright, i'll check it out, thank you for your help as well ! :) \$\endgroup\$
    – Daf'ium
    Commented Apr 13, 2021 at 10:48
3
\$\begingroup\$
for y in range(len(wordBanList)):
            if(Personne1 == wordBanList[y] or Personne2 == wordBanList[y]):
                printIt = 0

This code is checking to see if Personne1 or Personne2 are in wordBanList and has O(len(wordBanList)) runtime time. You can change the data type of your wordBanList to set() and have a O(1) run time. So your code could be transformed to:

if Personne1 in wordBanList or Personne2 in wordBanList:
    printIt = 0

P.S: If you have a small wordBanList (like <100 words), leaving the wordBanList as a list would not make a HUGE difference compared to sets. But, when there are a lot of words (>10,000,000), the difference is significant. https://towardsdatascience.com/faster-lookups-in-python-1d7503e9cd38

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.