• 流畅的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,这个章节主要讲述了把函数当做对象传递比传递实例方便的操作。

  • 相关阅读:
    ERROR: do not initialise statics to false
    kernel defconfig
    python --- comment
    python --- for
    Xcode密钥没有备份或者证书过期,出现Valid Signing错误
    [iOS]XCODE5升级之路
    VirtualBOX 虚拟机安装 OS X 10.9 Mavericks 及 Xcode 5,本人X220亲测
    [下载] MultiBeast 6.2.1版,支持10.9 Mavericks。Mac上的驱动精灵,最简单安装驱动的方式。
    XCode 5资源文件不自动更新问题
    Microsoft Word 2010/2013 无法创建工作文件 请检查临时环境变量
  • 原文地址:https://www.cnblogs.com/sidianok/p/12061087.html
Copyright © 2020-2023  润新知