简体   繁体   中英

Is there a better way to organize a Python (3.6.2) files?

Abstract.

I have two python files. The first one reads some inputs (texts) and the second one stores some functions. The problem here is the method I'm using to look for the function that I need. Now I'm using a If / Elif method to compare the input and the functions but this method needs to compare all the functions stored in the second file with the input and I was thinking if "this is the best way to do it?" .

Full explanation.

I'm trying to build a Telegram chat bot, just to practice some Python. Before anything I drew a mental map to know what I want this scripts will do. Because of that mental map, I came with the idea of spliting the code in different files in order organize everything better and to do the debugging process a little bit easier. So, I did that. I store the credentials (for the api's) in one file, the mainframe in other file and the functions in other file. I import in the files into the mainframe with From Credentials import * and From Comands import * . When any text come to the bot it first checks how it starts, if it starts with "/" then it stores whatever comes after the slash in a variable and send it as a parameter to the functions file. When is there it starts to look for the exact command required and execute it. And it works but I guessing if there is a better way to do it. I hope I explained well the problem and you can help me. Here's the extract of code that I'm talking.

mainframe.py

from Comands import *
from credentials import *

...

if text.startswith("/"):
            comando = text[1:]
            print(comando)
            Comands.search(comando)
elif text in items:
            db.delete_item(text, chat)
            items = db.get_items(chat)
            keyboard = build_keyboard(items)
            send_message("Select an item to delete", chat, keyboard)
else:
            db.add_item(text, chat)
            items = db.get_items(chat)
            message = "\n".join(items)
            send_message(message, chat)
...

Comands.py

from message_dictionary import *
from mainframe import *

def search(comando):
    if comando == "start":
        def start():
            keyboard = build_keyboard(acuerdo)
            send_message(mensaje["start"], chat, keyboard)
    elif comando == "done":
        def done():
            keyboard = build_kerboard(items)
            send_message("Select an item to delete", chat, keyboard)

First, I'll start with 2 recommendations:

  • Don't use stars in imports (it is hard to tell where functions are declared later)
  • Try to avoid cycle imports (import Command in mainframe.py and import mainframe in command.py)

mainframe.py

import command

comando_str = 'str_command'

# at this stage we want to call command.str_command(), but we have a string, not a function
if hasattr(command, comando_str):  # let's check if such command exist in `command` module
    comando_function = getattr(command, comando_str)  # now let's get a reference to that function

    # we can now call comando_function()!
    # but maybe you have some parameters to call (like passing in `keyboard`)
    arg_count = comando_function.__code__.co_argcount  # total number of arguments to the function
    # co_varnames has all the variables that function would use, but arguments come firs
    arg_names = comando_function.__code__.co_varnames[:arg_count]  

    if arg_count >= 1 and arg_names[0] == 'keyboard':
         comando_function(keyboard)
    elif arg_count == 0:  # function require no arguments
         comando_function()
    else:
        raise Exception('Function %s could not be called', comando_str)
else:
    raise Exception('Function command.%s is not declared', comando_str)

command.py

import message_dictionary as md  # no *!

def str_command(keyboard):
    md.send_message(mensaje["start"], chat, keyboard)  # now I know that send_message is in md (message_dictionary)

def start():
    keyboard = build_keyboard(acuerdo)  
    send_message(mensaje["start"], chat, keyboard)  

some notes about your comand.py:

def search(comando):  # ok
    if comando == "start":  # ok
        # here you define function `start`
        # yet, you have not called `start` function 
        # so, when you exit `search` function, this function is gone!
        def start():
            keyboard = build_keyboard(acuerdo)  # where is acuerdo is declared?
            send_message(mensaje["start"], chat, keyboard)  # where mensaje and chat are declared?
    elif comando == "done":
        # same problem as with `start` function above
        def done():
            keyboard = build_kerboard(items)
            send_message("Select an item to delete", chat, keyboard)

modified comand.py to return callable function (to address questions in comments):

def search(comando): 
    if comando == "start":
        def start(): 
            keyboard = build_keyboard(acuerdo)  
            send_message(mensaje["start"], chat, keyboard)  
        return start
    elif comando == "done":
        # same problem as with `start` function above
        def done():
            keyboard = build_kerboard(items)
            send_message("Select an item to delete", chat, keyboard)
        return done

modified fragment mainframe.py to use returned values:

if text.startswith("/"):
    comando = text[1:]
    print(comando)  # look at the logging as a fancy replacing for print
    call_me = Comands.search(comando)
    if call_me:  # checking that something is returned (not None)
        call_me()  # example of calling it

Here it is.

mainframe.py (it is actually called Prubeas.py)

#PYTHON 3.6.2
#LGsus

import json
import requests
import time
import urllib
from dbhelper import DBHelper
from credentials import *
from message_dictionary import *
import Comands

db = DBHelper()

#DECLARAR ID DEL BOT Y URL DE TELEGRAM
URL = "https://api.telegram.org/bot{}/".format(telegram_token)

#CONSULTAR ESTADO
def get_url(url):
    response = requests.get(url)
    content = response.content.decode("utf8")
    return content

#CAMBIAR DE JSON A PYTHON (PARSE)
def get_json_from_url(url):
    content = get_url(url)
    js = json.loads(content)
    return js

#SOLICITAR LISTA DE MENSAJES
def get_updates(offset=None):
    url = URL + "getUpdates?timeout=100"
    if offset:
        url += "&offset={}".format(offset)
    js = get_json_from_url(url)
    return js

#DETERMINAR MENSAJES NO  LEÍDOS
def get_last_update_id(updates):
    update_ids = []
    for update in updates["result"]:
        update_ids.append(int(update["update_id"]))
    return max(update_ids)

#RESPONDER A TODOS LOS NO LEIDOS
def handle_updates(updates):
    for update in updates["result"]:
        text = update["message"]["text"]
        chat = update["message"]["chat"]["id"]
        items = db.get_items(chat)
        if text.startswith("/"):
            comando = text[1:]
            print(comando)
            Comands.search(comando)
            #fin = text.find(" ")
            #print(fin)

            #if text == "/done":
            #    keyboard = build_keyboard(items)
            #    send_message("select an item to delete", chat, keyboard)
            #elif text == "/start":
            #    keyboard = build_keyboard(acuerdo)
            #    send_message(mensajes["start"], chat, keyboard)
            #elif text.startswith("/"):
            #    continue
        elif text in items:
            db.delete_item(text, chat)
            items = db.get_items(chat)
            keyboard = build_keyboard(items)
            send_message("Select an item to delete", chat, keyboard)
        else:
            db.add_item(text, chat)
            items = db.get_items(chat)
            message = "\n".join(items)
            send_message(message, chat)

#SOLICITAR ULTIMO MENSAJE Y ID DEL CHAT
def get_last_chat_id_and_text(updates):
    global Texto
    global full_last
    num_updates = len(updates["result"])
    lista = updates["result"]
    data = json.dumps(lista)
    last_update = num_updates - 1
    full_last = updates["result"][last_update]
    Texto = "text" in full_last["message"]
    if Texto == True:
        text = updates["result"][last_update]["message"]["text"]
    else:
        text = "Entrada invalida"
    chat_id = updates["result"][last_update]["message"]["chat"]["id"]
    return (text, chat_id)

#CREAR EL TECLADO
def build_keyboard(items):
    keyboard = [[item] for item in items]
    reply_markup = {"keyboard":keyboard, "one_time_keyboard":True}
    return json.dumps(reply_markup)

#ENVIAR MENSAJE
def send_message(text, chat_id, reply_markup=None):
    text = text.encode(encoding='utf-8')
    text = urllib.parse.quote_plus(text)
    url = URL + "sendMessage?text={}&chat_id={}&parse_mode=Markdown".format(text, chat_id)
    if reply_markup:
        url += "&reply_markup={}".format(reply_markup)
    get_url(url)
    print (text)

text, chat = get_last_chat_id_and_text(get_updates())
send_message(text, chat)

##EJECUTAR
def main():
    db.setup()
    last_update_id = None
    while True:
        updates = get_updates(last_update_id)
        if len(updates["result"]) > 0:
            last_update_id = get_last_update_id(updates) + 1
            handle_updates(updates)
        time.sleep(0.5)

#CONDICION PARA EJECUTAR
if __name__ == '__main__':
    main()





#import ccxt

#b = ccxt.bitso({
#    'apiKey': "XXXXXX",
#    'secret': "XXXXXX",
#    'verbose': False,
#    })
#
#print(b.fetchBalance())

Whats it commented at the end of this code is for an API i'll try to use later, once I done with the telegram api.

Comands.py

#Lista de comandos

from message_dictionary import *
from Prubebas import *

def search(comando):
    if comando == "start":

    #def start():
        keyboard = build_keyboard(acuerdo)
        send_message(mensaje["start"], chat, keyboard)

    def done():
        keyboard = build_kerboard(items)
        send_message("Select an item to delete", chat, keyboard)

I apologies for mixing the languages. I really appreciate your help, thanks.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM