流畅的python,Fluent Python 第六章笔记

使用一等函数实现设计模式。

6.1案例分析:重构策略模式。

根据策略模式定制一个网店的折扣模式:

1、有1000或以上积分的顾客,每个订单享5%的折扣

2、同一个订单中,单个商品的数量达到20个或以上,享受10%折扣

3、订单中的不同商品达到10个或以上,享7%折扣。

一个订单只能享受一个折扣。

根据书中的策略模式,我按照书中的方式写了代码,其实order属于上下文,它接收了所有的数据,进行统一汇总输出。

order

from collections import namedtuple

from lineitem import LineItem
from promo_all import *



Customer = namedtuple('Customer', 'name fidelity')

class Order:    # 上下文

    def __init__(self, customer, cart, promotion=None):
        '''参数1用户模型,参数2购物车,参数3优惠方案'''
        self.customer = customer
        self.cart = list(cart)
        self.promotion = promotion

    def total(self):
        '''没有总价,给一个总价在对象身上,避免重复运算'''
        if not hasattr(self, '__total'):
            self.__total = sum(item.total() for item in self.cart)
        return self.__total

    def due(self):
        '''折扣优惠多少'''
        if self.promotion == None:
            discount = 0
        else:
            discount = self.promotion.discount(self)
        return self.total() - discount

    def __repr__(self):
        '''格式化输出实际价格以及优惠后的价格'''
        fmt = '<Order total: {:.2f} due: {:.2f}>'
        return fmt.format(self.total(), self.due())


if __name__ == '__main__':
    joe = Customer('John', 0)
    ann = Customer('Ann Smith', 11000)
    cart = [
        LineItem('banana', 4, .5),
        LineItem('apple', 10, 1.5),
        LineItem('watermellon', 5, 5)
    ]
    joe_o = Order(joe, cart, FidelityPromo())
    print(joe_o)
    ann_o = Order(ann, cart, FidelityPromo())
    print(ann_o)
class LineItem:
    '''
    一个商品订单的详情
    '''
    def __init__(self, product, quantity, price):
        self.product = product
        self.quantity = quantity
        self.price = price

    def total(self):
        return self.price * self.quantity
from abc import ABC
from abc import abstractmethod

class Promotion(ABC):

    @abstractmethod
    def discount(self, order):
        '''返回折扣的金额'''
from promotion import Promotion

class FidelityPromo(Promotion):
    '''为积分为1000或以上的顾客的5%折扣'''

    def discount(self, order):
        '''order就是上下文对接本身'''
        return order.total() * .05 if order.customer.fidelity >= 1000 else 0


class BulkItemPromo(Promotion):
    '''单个商品为20个或以上'''

    def discount(self, order):
        discount = 0
        for item in order.carr():
            if item.quantity >= 20:
                discount += item.total * .1
        return discount


class LargeOrderPromo(Promotion):
    '''订单中的不同商品达到10个或以上提供的优惠'''

    def discount(self, order):
        distinct_items = {item.product for item in order.cart}
        if len(distinct_items) >= 10:
            return order.total() * .7
        return 0

 代码中用到了抽象基类。

从代码中发现,传入的优惠是一个实例,里面也只有简单的计算功能,完全可以改成函数。

from collections import namedtuple

from lineitem import LineItem
from promo_all import *
from promo_all_func import *

Customer = namedtuple('Customer', 'name fidelity')

class Order:    # 上下文

    def __init__(self, customer, cart, promotion=None):
        '''参数1用户模型,参数2购物车,参数3优惠方案'''
        self.customer = customer
        self.cart = list(cart)
        self.promotion = promotion

    def total(self):
        '''没有总价,给一个总价在对象身上,避免重复运算'''
        if not hasattr(self, '__total'):
            self.__total = sum(item.total() for item in self.cart)
        return self.__total

    def due(self):
        '''折扣优惠多少'''
        if self.promotion == None:
            discount = 0
        else:
            # discount = self.promotion.discount(self)
            discount = self.promotion(self)
        return self.total() - discount

    def __repr__(self):
        '''格式化输出实际价格以及优惠后的价格'''
        fmt = '<Order total: {:.2f} due: {:.2f}>'
        return fmt.format(self.total(), self.due())


if __name__ == '__main__':
    joe = Customer('John', 0)
    ann = Customer('Ann Smith', 11000)
    cart = [
        LineItem('banana', 4, .5),
        LineItem('apple', 10, 1.5),
        LineItem('watermellon', 5, 5)
    ]
    joe_o = Order(joe, cart, fidelity_promo)
    print(joe_o)
    ann_o = Order(ann, cart, fidelity_promo)
    print(ann_o)
def fidelity_promo(order):
    '''order就是上下文对接本身'''
    return order.total() * .05 if order.customer.fidelity >= 1000 else 0

def bulkitem_promo(order):
    discount = 0
    for item in order.carr():
        if item.quantity >= 20:
            discount += item.total * .1
    return discount

def largeorder_promo(order):
    distinct_items = {item.product for item in order.cart}
    if len(distinct_items) >= 10:
        return order.total() * .7
    return 0

 改函数的话,order里面就在优惠的执行上面改了一下,不需要调用方法,直接()调用函数。

因为有三个不同的优惠方案,选一个合适的方法。当然也可以通过函数实现。(书中介绍了三种导入优惠函数的方法。)

第一种也是我这样的人想到的一种:

promos = [fidelity_promo, bulkitem_promo, largeorder_promo]

def best_promo(order):
    '''选择最佳折扣方案'''
    return max(promo(order) for promo in promos)

 直接导入三个函数,循环执行函数,然后挑出优惠最大的。这个唯一的不方便就是每次都要自己手动添加函数。

第二种用过globals函数执行,取出所有的赋值对象。

promos = [globals()[name] for name in globals()
          if name.endswith('_promo') and name != 'best_promo'
          ]
def best_promo(order):
    '''选择最佳折扣方案'''
    return max(promo(order) for promo in promos)

 然后根据条件选择出自己所要的函数,唯一的缺点,函数的名字需要统一名称。

第三种,通过inspect的使用,导入整个模块,判断里面的模块是否是函数,和姓名等信息,输出到列表。

import promo_all_func
    import inspect
    # 导入模块通过inspect.isfunction来获取木块内的方法于属性,通过条件判断,
    # 输出需要的函数,最方便的。
    promos = [func for name,func in inspect.getmembers
    (promo_all_func, inspect.isfunction) if name != 'best_promo']
    
    print(best_promo(ann_o))

 三种不同的方法,明显最后一种最方便了,如果有后续的优惠方案,直接添加进去就可以了。

over,这个章节主要讲述了把函数当做对象传递比传递实例方便的操作。

原文地址:https://www.cnblogs.com/sidianok/p/12061087.html