pythonic方式来编写这段代码

时间:2012-08-01 00:11:10

标签: python

我有这段丑陋的代码..

 orders = got_something_from_db()
 if orders:
    for order in orders:
        #print order
        event_id = order["event_id"]
        if event_id in event_id_dict: # something i grabbed earlier
            product_id = order["product_id"] # products in an event
            qty = order["qty"]
            if product_id in product_sku_dict: 
                sku_id =product_sku_dict[product_id]
                for i in range(qty):
                    sku_ids.append(sku_id)

我如何使这更加pythonic(和简洁)

5 个答案:

答案 0 :(得分:4)

首先,代码并不是很糟糕 - 它已经足够清晰,可以阅读和理解,所以你肯定已经超过了第一道可维护性。

如果有问题则是深层嵌套。我会通过创建一些函数来解决这个问题,每个函数都有一个以其名称表达的独特业务目的:

def order_has_valid_event(order):
    """Returns True if the order was raised by an event
    in event_id_dict"""
    event_id = order["event_id"]
    return event_id in event_id_dict # something i grabbed earlier

def get_sku_from_order(order):
    """Return the SKU of the product in an order"""
    product_id = order['product_id']
    try:
        return product_sku_dict[product_id]
    except KeyError:
        raise KeyError("Product {0} is not in SKU dictionary".format(product_id))

def get_order_skus(order):
    """Returns a list of SKUs in an order"""
    sku_id = get_sku_from_order(order)
    qty = order["qty"]
    return [sku_id] * qty

# Modify got_something_from_db to always return a list, even if empty 
orders = got_something_from_db()
for order in orders:
    #print order
    if order_has_valid_event(order):
        try:
            sku_ids.extend(get_order_skus(order))
        except KeyError:
            continue

如果名称与您的意图不符,请原谅此尝试 - 只需将其重新命名。

我的改进尝试:

  • 您可以使用列表乘法创建qty元素列表,而不是循环qty次。['a'] * 3 = ['a','a','a “]
  • 在业务级别,当产品ID不在SKU词典中时,我会质疑默认忽略的错误。这很可能是一个真正的问题(所有产品都应该在我所从事的行业中拥有SKU)。如果是这样,让错误快速并“大声”传播到适当的错误处理程序,或者更可能是可以拒绝订单的地方。为了更清楚地支持这一点,我在KeyError中捕获了普通get_sku_from_order并抛出了更明确的异常消息。
  • 修改got_something_from_db以始终返回列表,即使为空。这简化了逻辑和流程

答案 1 :(得分:2)

在有限的背景下,这是我能做的最好的事情。

orders = got_something_from_db()
for order in orders: #make got something return empty iterable on failure
    if order["event_id"] in event_id_dict:
        product_id = order["product_id"]
        try:
            sku_id = product_sku_dict[product_id]
            #Change sku_ids to a collections.Counter (assuming order is unimportant)
            sku_ids[sku_id] += order["qty"] 
        except KeyError:
            pass

另外,请考虑将event_id,product_id等更改为属性。你可能想要一个名字小组,而不是一个字典。

答案 2 :(得分:2)

我的结构如下:

def add_product_to_skus(product_id, qty):
    if product_id in product_sku_dict: 
        sku_id = product_sku_dict[product_id]
        sku_ids.extend(qty*[sku_id])    

# ...

orders = got_something_from_db()
if not orders:
    return

valid_orders = (o for o in orders if o['event_id'] in event_id_dict)

for o in valid_orders:
    add_product_to_skus(o['product_id'], o['qty'])

上面的一些高尔夫球会给你:

orders = got_something_from_db()
if not orders:
    return
add_products_to_skus((o['product_id'], o['qty']) for o in orders 
                     if o['event_id'] in event_id_dict 
                     if o['product_id'] in product_sku_dict)
# ...  
def add_product_to_skus(product_qtys):
    for product_id, qty in product_qtys:
        sku_id = product_sku_dict[product_id]
        sku_ids.extend(qty*[sku_id])

但是从原始形式到这种形式的转换不一定清楚(甚至是正确的),列表理解可能值得一个解释过滤的评论。

答案 3 :(得分:1)

sku_ids = []
event_ids = get_event_id_dict()
orders = got_something_from_db()

for order in orders:
    if order["event_id"] in event_ids:
        try:
            sku_id = product_sku_dict[order["product_id"]]
        except KeyError:
            continue

        sku_ids.extend([sku_id] * order["qty"])

答案 4 :(得分:1)

您可能希望引发异常,而不是默默地忽略丢失的SKU项目。这是如何成为最模块化和优雅,而不是必要的pythonic:

database = ...
productId_to_sku = {...}

def productIdToSku(product_id):
    try:
        return productId_to_sku[product_id]
    except IndexError:
        raise Exception('product id #{} not in SKU database; have a manager add it'.format(product_id))

def getSkusFromEvent(event_id):
    orders = database.fetch(event_id=event_id)
    for order in orders:
        yield (productIdToSku(order.PRODUCT_ID), order.QTY)

collections.Counter(getSkusFromEvent(YOUR_EVENT_ID))