对接口编程,而不是对实现编程。 优先使用对象组合,而不是类继承。
策略模式:定义一系列算法,把他们一一封装起来,并且使它们可以相互替换。本模式使得算法可以独立于使用它的客户而变化。
抽象基类
在Python3.4中,声明抽象基类最简单的方式是子类话abc.ABC
.需要使用装饰器@abstractmethod
from abc import ABC, abstractmethod
class Foo(ABC):
@abstractmethod
def fun(self):
'''please Implemente in subclass'''
class SubFoo(Foo):
def fun(self):
print('fun in SubFoo')
a = SubFoo()
a.fun()
Python3.0到Python3.3,必须在class语句中使用metaclass=ABCMeta
from abc import abstractmethod, ABCMeta
class Bar(metaclass=ABCMeta):
@abstractmethod
def fun(self):
'''please Implemente in subclass'''
class SubBar(Bar):
def fun(self):
print('fun in SubBar')
b = SubBar()
b.fun()
Python2中使用__metaclass__=ABCMeta
from abc import ABCMeta, abstractmethod
class FooBar():
__metaclass__ = ABCMeta
@abstractmethod
def fun(self):
'''please Implemente in subclass'''
class SubFooBar(FooBar):
def fun(self):
print('fun in SubFooBar')
a = SubFooBar()
a.fun()
使用策略进行折扣(中文注释)
代码语言:javascript复制# -*- coding: utf-8 -*-
# @Time : 2020/12/16 下午8:34
# @Author : zhongxin
# @Email : 490336534@qq.com
# @File : 策略模式.py
from abc import ABC, abstractmethod
from collections import namedtuple
Customer = namedtuple("Customer", "name fidelity")
class LineItem():
def __init__(self, product, quantity, price):
"""
:param product: 产品
:param quantity:数量
:param price:价格
"""
self.product = product
self.quantity = quantity
self.price = price
def total(self):
"""
计算总价
:return:
"""
return self.price * self.quantity
class Order():
def __init__(self, customer: Customer, cart: [LineItem], promotion=None):
"""
:param customer: 顾客
:param cart: 选购清单
:param promotion: 折扣策略
"""
self.customer = customer
self.cart = list(cart)
self.promotion = promotion
def total(self):
"""
计算总价
:return:
"""
if not hasattr(self, '__total'):
self.__total = sum(item.total() for item in self.cart)
return self.__total
def due(self):
"""
计算折扣后价格
:return:
"""
if self.promotion is None:
discount = 0 # 没有任何优惠
else:
discount = self.promotion.discount(self) # 调用折扣计算方法,获得优惠金额
return self.total() - discount
def __repr__(self):
return f"<原价:{self.total():.2f} 折后价 {self.due():.2f}>"
class Promotion(ABC):
"""
策略:抽象基类
"""
@abstractmethod
def discount(self, order):
"""返回折扣金额(正值)"""
class FidelityPromo(Promotion):
"""
折扣策略:积分1000 的顾客5%折扣
"""
def discount(self, order):
return order.total() * 0.05 if order.customer.fidelity >= 1000 else 0
class BulkItemPromo(Promotion):
"""
折扣策略:单个商品20 个10%折扣
"""
def discount(self, order):
discount = 0
for item in order.cart:
if item.quantity >= 20:
discount = item.total() * 0.1 # 对数量大于20的单个商品进行折扣
return discount
class LargeOrderPromo(Promotion):
"""
折扣策略:不同商品达到10 个 7%折扣
"""
def discount(self, order):
distinct_items = {item.product for item in order.cart}
if len(distinct_items) >= 10:
return order.total() * 0.07 # 对全部商品进行折扣
return 0
if __name__ == '__main__':
joe = Customer('Joe Doe', 0) # 积分0
ann = Customer("Ann Smith", 1100) # 积分1100
cart = [
LineItem('香蕉', 4, 0.5),
LineItem('苹果', 10, 1.5),
LineItem('西瓜', 5, 5.0),
]
print(Order(joe, cart, FidelityPromo())) # <原价:42.00 折后价 42.00>
print(Order(ann, cart, FidelityPromo())) # <原价:42.00 折后价 39.90>
banana_cart = [
LineItem("香蕉", 30, 0.5),
LineItem("苹果", 10, 1.5),
]
print(Order(joe, banana_cart, BulkItemPromo())) # <原价:30.00 折后价 28.50>
long_cart = [LineItem(str(item_code), 10, 1.0) for item_code in range(10)] # 10件不同的商品
print(Order(joe, long_cart, LargeOrderPromo())) # <原价:100.00 折后价 93.00>
享元:享元是可共享的对象,可以同时在多个上下文中使用。
Order
类可以通过传入不同的promotion
来实现不同的折扣策略
inspect 检查对象
官方文档:https://docs.python.org/zh-cn/3/library/inspect.html
inspect
模块提供了一些有用的函数帮助获取对象的信息,例如模块、类、方法、函数、回溯、帧对象以及代码对象。例如它可以帮助你检查类的内容,获取某个方法的源代码,取得并格式化某个函数的参数列表,或者获取你需要显示的回溯的详细信息。
该模块提供了4种主要的功能:
- 类型检查
- 获取源代码
- 检查类与函数
- 检查解释器的调用堆栈
判断是否为类
代码语言:javascript复制def isclass(object):
"""Return true if the object is a class.
Class objects provide these attributes:
__doc__ documentation string
__module__ name of module in which this class was defined"""
return isinstance(object, type)
判断是否为函数
代码语言:javascript复制def isfunction(object):
"""Return true if the object is a user-defined function.
Function objects provide these attributes:
__doc__ documentation string
__name__ name with which this function was defined
__code__ code object containing compiled function bytecode
__defaults__ tuple of any default values for arguments
__globals__ global namespace in which this function was defined
__annotations__ dict of parameter annotations
__kwdefaults__ dict of keyword only parameters with defaults"""
return isinstance(object, types.FunctionType)
包含对象的所有成员的(name, value)列表
代码语言:javascript复制def getmembers(object, predicate=None):
"""Return all members of an object as (name, value) pairs sorted by name.
Optionally, only return members that satisfy a given predicate."""
if isclass(object):
mro = (object,) getmro(object)
else:
mro = ()
results = []
processed = set()
names = dir(object) # 使用dir方法拿到全部的属性
# :dd any DynamicClassAttributes to the list of names if object is a class;
# this may result in duplicate entries if, for example, a virtual
# attribute with the same name as a DynamicClassAttribute exists
try:
for base in object.__bases__:
for k, v in base.__dict__.items():
if isinstance(v, types.DynamicClassAttribute):
names.append(k)
except AttributeError:
pass
for key in names:
# First try to get the value via getattr. Some descriptors don't
# like calling their __get__ (see bug #1785), so fall back to
# looking in the __dict__.
try:
value = getattr(object, key)
# handle the duplicate key
if key in processed:
raise AttributeError
except AttributeError:
for base in mro:
if key in base.__dict__:
value = base.__dict__[key]
break
else:
# could be a (currently) missing slot member, or a buggy
# __dir__; discard and move on
continue
if not predicate or predicate(value):
results.append((key, value))
processed.add(key)
results.sort(key=lambda pair: pair[0])
return results
getmembers
获取源码
代码语言:javascript复制inspect.getsource(Order)
获取源码