Python 中的软件设计。我应该使用一个模块还是单独使用?

Software Design in Python. Should I use one module or separate?

我在Python写了两个脚本:

第一个将字典的键值附加到列表。

第二个使用该列表在 MySQL 数据库上创建列。

原来我把它们写在同一个模块里。但是,将它们分开放在两个不同的模块中还是将它们放在一起更好?

如果最好将它们分开,那么在另一个文件中使用列表的 pythonic 方式是什么?我知道不推荐导入变量。

代码如下:

import pymysql

# This script first extract dictionary key values. Then, it creates columns using the keys values from the dictionaries.

# login into mysql
conn = pymysql.connect("localhost", "*****", "******", "tutorial")

# creating a cursor object
c = conn.cursor()

# update here table from mysql
table_name = "table01"

# data
dicts = {'id': 5141, 'product_id': 193, 'price_ex_tax': 90.0000, 'wrapping_cost_tax': 0.0000, 'type': 'physical', 'ebay_item_id': 444, 'option_set_id': 38, 'total_inc_tax': 198.0000, 'quantity': 2, 'price_inc_tax': 99.0000, 'cost_price_ex_tax': 0.0000, 'name': 'UQ Bachelor Graduation Gown Set', 'configurable_fields': [], 'base_cost_price': 0.0000, 'fixed_shipping_cost': 0.0000, 'wrapping_message': '', 'order_address_id': 964, 'total_ex_tax': 180.0000, 'refund_amount': 0.0000, 'event_name': None, 'cost_price_inc_tax': 0.0000, 'cost_price_tax': 0.0000, 'wrapping_cost_inc_tax': 0.0000, 'wrapping_name': '', 'price_tax': 9.0000, 'is_bundled_product ': False, 'ebay_transaction_id': 4444, 'bin_picking_number': 4444, 'parent_order_product_id': None, 'event_date': '', 'total_tax': 18.0000, 'wrapping_cost_ex_tax': 0.0000, 'base_total': 198.0000, 'product_options': [{'id': 4208, 'display_name': 'Gown size (based on height)', 'name': 'Bachelor gown size', 'display_value': 'L (175-182cm)', 'display_style': 'Pick list', 'type': 'Product list', 'option_id': 19, 'value': 77, 'product_option_id': 175, 'order_product_id': 5141}, {'id': 4209, 'display_name': 'Hood', 'name': 'H-QLD-BAC-STD', 'display_value': 'UQ Bachelor Hood', 'display_style': 'Pick list', 'type': 'Product list', 'option_id': 42, 'value': 119, 'product_option_id': 176, 'order_product_id': 5141}, {'id': 4210, 'display_name': 'Trencher size (based on head circumference)', 'name': 'Trencher size', 'display_value': 'M (53-54cm)', 'display_style': 'Pick list', 'type': 'Product list', 'option_id': 20, 'value': 81, 'product_option_id': 177, 'order_product_id': 5141}], 'base_price': 99.0000, 'sku': 'S-QLD-BAC-STD', 'return_id': 0, 'applied_discounts': [{'id': 'coupon', 'amount': 30}], 'quantity_shipped': 0, 'base_wrapping_cost': 0.0000, 'is_refunded': False, 'weight': 2.0000, 'order_id': 615496}  # noqa

# creating empty lists
int_keys_lists = []
str_keys_lists = []
list_keys_lists = []


def extractDictKeys():

    # for loop that runs through the dictionary, extracting the keys when their valures are int or str, and appending to the corresponding list
    for i, j in enumerate(dicts):
        k, v = list(dicts.items())[i]
        if type(dicts[j]) != str:
            int_keys_lists.append(k)
        else:
            str_keys_lists.append(k)


def createColumnStrKeys():
    # for loop that create a column for each str key on the list
    for i, j in enumerate(str_keys_lists):
        c.execute("ALTER TABLE {0} ADD COLUMN {1} VARCHAR(255)".format(table_name, str_keys_lists[i]))
        conn.commit()


def createColumnIntKeys():
    # for loop that create a column for each id or float key on the list
    for i, j in enumerate(int_keys_lists):
        c.execute("ALTER TABLE {0} ADD COLUMN {1} int(30)".format(table_name, int_keys_lists[i]))
        conn.commit()

extractDictKeys()
createColumnStrKeys()
createColumnIntKeys()

你的设计有些问题。

函数不应该使用全局变量。函数接收变量作为参数,然后 return 一个值(尽管在某些情况下该值是 void

例如:

def extract_dict_keys(dictionary):
    key_list = list()
    int_key_list = list()
    str_key_list = list()
    # Your code here
    ..........
    return key_list, int_key_list, str_key_list

def create_col__str_keys(conn, dictionary, key_list):
    cursor = conn.cursor()
    # Your code here
    ..........
    # Commit outside of the loop
    conn.commit() # Though it's usually not recommended to commit in the function

def create_col__int_keys(conn, dictionary, key_list):
    cursor = conn.cursor()
    # Your code here
    ..........
    conn.commit()

只有当你把所有的代码片段都相互独立之后,你才能将它们组织成模块

如何将代码构建到模块中取决于代码的可重用程度以及它们之间的关系。例如,我会将所有全局变量放入一个将要执行的主文件中,将实用函数放入另一个模块中,并将 sql 相关函数放入另一个模块中:

main.py

from utilities import extract_dict_keys
from sql import create_col_str_keys, create_col_int_keys

# login into mysql
conn = pymysql.connect("localhost", "*****", "******", "tutorial")

# data
data = {...}  # noqa
keys, int_keys, str_keys = extract_dict_keys(data)
create_col_int_keys(conn, data, int_keys)
create_col_str_keys(conn, data, str_keys)

utilities.py

def extract_dict_keys(dictionary):
    key_list = list()
    int_key_list = list()
    str_key_list = list()
    # Your code here
    ..........
    return key_list, int_key_list, str_key_list

sql.py

import pymysql

def create_col__str_keys(conn, dictionary, key_list):
    cursor = conn.cursor()
    # Your code here
    ..........
    conn.commit()

def create_col__int_keys(conn, dictionary, key_list):
    cursor = conn.cursor()
    # Your code here
    ..........
    conn.commit()

此外,关于SQL注入漏洞,你不应该使用cursor.execute(a_built_string)

考虑这种情况:

cursor.execute("DELETE FROM users WHERE id = {uid}".format(uid=user_id))

还有一个足够聪明的人可以输入 user_id="1 or 1 = 1"。结果将是:

cursor.execute("DELETE FROM users WHERE id = 1 or 1 = 1")

这可能会删除您宝贵用户中的所有用户 table。

因此,改用这个:

cursor.execute("DELETE FROM users WHERE id = %d", user_id)

不会创建字符串,编译执行,而是编译SQL语句,然后插入变量,再执行,安全很多

在问自己这个问题之前,我建议先研究一下两件事。

首先,您依赖于模块范围内的一堆全局变量。当您编写更复杂的程序时,您看到的使这些全局变量在其他模块中可见的问题只会变得更糟。您应该开始考虑涉及 类 一点,导入它们,实例化它们并查看单例等设计模式。在 类 下定义一堆重要的列表、变量等,然后导入它们并在函数之间传递它们的实例会更容易、更清晰。面向对象是pythonic。

其次,您的函数在概念上看起来更像是子例程。最好将函数视为……嗯……函数。他们应该接受一些参数(输入)并产生(即 return)一些输出。避免试图操纵全局变量,这很容易成为问题,并更多地考虑使用输入来创建和 return 输出。

研究并结合这些概念,代码应该放在哪里的答案将变得更加清晰。