Использовать exec или избегать?
Пишу discord бота на питоне. Хотелось бы вынести каждую команду в отдельный файл. Единственное что я придумал - читать содержимое файла и использовать функцию exec. Использовать import не получится, так как в коде команд используются глобальные переменные.
Уместно ли такое решение, или есть варианты получше? Недаром ведь говорят "eval is evil" :)
(код подаю в сокращенном и обрезанном виде, оставил только элементы по сути вопроса)
bot=Bot()
@bot.command()
async def command1(ctx):
await ctx.send("command1 invoked")
@bot.command()
async def command2(ctx):
await ctx.send("bot is on %s guilds"%len(bot.guilds))
Ответы (3 шт):
Вариант №1, если хочется чтобы декораторы выглядели именно как декораторы
В отдельных файлах заворачиваете ваши функции так:
def make_command1(bot):
@bot.command()
async def command1(ctx):
await ctx.send("command1 invoked")
В главном файле используете их так:
from command1_file import make_command1
bot = Bot()
command1 = make_command1(bot)
Вариант №2
В отдельных файлах пишите только функции без декораторов:
async def command1(ctx):
await ctx.send("command1 invoked")
А в главном файле привязываете их к боту так:
from command1_file import command1
bot = Bot()
command1 = bot.command()(command1)
Вариант 3, модификация варианта 1. Если вам так уж хочется иметь доступ к глобальным переменным не передавая их явно.
Вот так в отдельном файле:
def make_command1():
bot = globals()['bot']
@bot.command()
async def command1(ctx):
await ctx.send("command1 invoked")
Вот так в главном:
from command1_file import make_command1
bot = Bot()
command1 = make_command1()
Но я крайне не рекомендую так делать, лучше все переменные передавать явно. В данном варианте вам самостоятельно придётся следить за тем, чтобы все нужные глобальные переменные были объявлены строго до строчек command1 = make_command1()
Нашел оптимальный для меня вариант.
helper_lib.py
#импорты библиотек и объявления функций
bot_commands/command1.py
from helper_lib import *
@bot.command()
async def command1(ctx):
await ctx.send("command1")
bot.py
import helper_lib
bot=Bot()
helper_lib.bot=bot
#другие глобальные переменные
from bot_commands import command1
Вроде получилось нормально, комментируйте если что.
Используйте Cogs: https://discordpy.readthedocs.io/en/stable/ext/commands/cogs.html
# mycog.py
from discord.ext import commands
class MyCog(commands.Cog):
"""My awesome cog""" # будет показываться в !help MyCog
def __init__(self, bot: commands.Bot):
# можно обойтись и без ссылки на объект бота в классе,
# но в таком случае объект бота придётся доставать через ctx.bot
self.bot = bot
@commands.command()
async def cmd(ctx):
await ctx.send("invoked cmd")
# main.py
from mycog import MyCog
bot = commands.Bot()
bot.add_cog(MyCog(bot)) # добавить "модуль"
# bot.remove_cog("MyCog") # убрать "модуль"