python 设计模式之解释器(Interpreter)模式
#写在前面
关于解释器模式,我在网上转了两三圈,心中有了那么一点概念 ,也不知道自己理解的是对还是错。
其实关于每一种设计模式,我总想找出一个答案,那就是为什么要用这种设计模式, 如果不用会怎么样,会导致什么后果?
我想只有搞懂了这个答案,我才有可能会学明白,才能给出实例。
下面开始落笔,沉淀出我对解释器模式的印象。
#到底什么是解释器模式?
这都是从下面的链接里面摘的,我感觉利于我理解,我就放在博客里。
为人处事是一门大学问,察言观色、听懂弦外之音都是非常重要的。
老板跟你说“XX你最*表现**啊,还得要多努力”,
如果你不当回事,*常对待,可能下次就是“XX,恩,你人还是不错,*常工作也很努力,但是我想这份工作可能不是很适合你…..”。
又比如你老大说“XX,你最*表现不错,工作积极性很高啊!继续保持啊!”,
你高兴乐呵着心想是不是老板要给我加工资了,可能你等到花都谢了也没有,得到的可能会是更多的工作量。
对于我们刚刚入社会的人不够圆滑,不会察言观色,更听不懂老板的弦外之音,所以我们期待如果有一个翻译机该多好,直接将别人的弦外之音给翻译出来就好了。
在我们实际的生活中是这样,在软件的世界里也同样存在着翻译机,只不过在软件中我们称之为解释器。在系统中如果某一特定类型的问题在频繁的发生,此时我们就有必要将这些问题的实例表述为一个语言中句子,因此可以构建一个解释器,然后利用该解释器来解释这些句子来解决这些问题。
#到底什么是解释器模式?
对每个应用来说,至少有以下两种不同的用户分类。
1] 基本用户:这类用户只希望能够凭直觉使用应用。他们不喜欢花太多时间配置或学习应用的内部。对他们来说,基本的用法就足够了。
2] 高级用户:这些用户,实际上通常是少数,不介意花费额外的时间学习如何使用应用的高级特性。如果知道学会之后能得到以下好处,他们甚至会去学习一种配置(或脚本)语言。
a]能够更好地控制一个应用
b]以更好的方式表达想法
c]提高生产力
解释器(Interpreter)模式仅能引起应用的高级用户的兴趣。这是因为解释器模式背后的主要思想是让非初级用户和领域专家使用一门简单的语言来表达想种简单的语言法。然而,什么是一种简单的语言?对于我们的需求来说,一就是没编程语言那么复杂的语言。
一般而言,我们想要创建的是一种领域特定语言(Domain Specific Language,DSL)。DSL是一种针对一个特定领域的有限表达能力的计算机语言。很多不同的事情都使用DSL,比如,战斗模拟、记账、可视化、配置、通信协议等。DSL分为内部DSL和外部DSL。
内部DSL构建在一种宿主编程语言之上。内部DSL的一个例子是,使用Python解决线性方程组的一种语言。使用内部DSL的优势是我们不必担心创建、编译及解析语法,因为这些已经被宿主语言解决掉了。劣势是会受限于宿主语言的特性。如果宿主语言不具备这些特性,构建一种表达能力强、简洁而且优美的内部DSL是富有挑战性的。
外部DSL不依赖某种宿主语言。DSL的创建者可以决定语言的方方面面(语法、句法等),但也要负责为其创建一个解析器和编译器。为一种新语言创建解析器和编译器是一个非常复杂、长期而又痛苦的过程。
解释器模式仅与内部DSL相关。因此,我们的目标是使用宿主语言提供的特性构建一种简单但有用的语言,在这里,宿主语言是Python。注意,解释器根本不处理语言解析,它假设我们已经有某种便利形式的解析好的数据,可以是抽象语法树(abstract syntax tree,AST)或任何其他好用的数据结构。
#到底什么是解释器模式?
所谓解释器(Interpreter)就是将一系列指令转化成代码,能够执行的代码。Interpreter本来就有翻译的意思。
GoF给它的定义是:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
这很像命令模式,但是命令模式的命令必须是实现了Command的类的实例,而解释器模式的命令则可以是任何对象,如字符串,整数等,只要解释器能将其解释成具体行为就行。
#到底什么是解释器模式?
虽然目前计算机编程语言有好几百种,但有时人们还是希望用一些简单的语言来实现特定的操作,只需要向计算机输入一个句子或文件,就能按照预定的文法规则来对句子或文件进行解释。例如,我们想要只输入一个加法/减法表达式,它就能够计算出表达式结果。例如输入“1+2+3-4+1”时,将输出计算结果为3。像C++,Java或C#都无法直接解释类似这样的字符串,因此用户必须自定义一套文法规则来实现对这些语句的解释,即设计一个自定义语言。如果所基于的编程语言是面向对象语言,此时可以使用解释器模式实现自定义语言。
#哪些地方用到解释器模式了?
1]第一个能想到的利用解释器模式的程序应该就是编译器了,编译器能将源代码翻译成机器能认识的机器码,这是很典型的解释器模式。
2]另外正则表达式(RegExp)也是解释器模式的一个应用。
其它的领域使用解释器模式是极少的,使用极其有限,另外有了yacc和lex等语法分析工具后使用解释器模式的地方就更少了。
#优点
1]易于改变和扩展文法 => 通过继承来改变或扩展
2]增加新的解释表达式较为方便 => 只需对应新增一个新的终结符或非终结符表达式,原有代码无须修改,符合开闭原则!
#缺点
执行效率较低。解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢,且代码的调试过程也比较麻烦。
会引起类膨胀。解释器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以管理与维护。
可应用的场景比较少。在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式很少被使用到。
#应用场景
在软件开发中,会遇到有些问题多次重复出现,而且有一定的相似性和规律性。
其他几乎用不到
#解释器模式角色介绍
Context:上下文环境,包含解释器之外的全局信息
Client:客户端,解析表达式,构建语法树,执行具体的解释操作等
AbstractExpression:抽象表达式,声明一个抽象的解释操作弗雷,并定义一个抽象的解释方案,其具体的实现在各个具体的子类解释器中完成。
TerminalExpression:终结符表达式,实现文法中终结符有关的解释操作。文法中每一个终结符都有一个具体的终结表达式与之对应。
NonterminalExpression:非终结表达式,实现文法中非终结符有关的解释操作。
其中AbstractExpression的interpret()是抽象的解析方法,参数是上下文的环境,而interpret()方法的具体实现则由TerminalExpression和NonterminalExpression实现。
#结构图
#解释器模式主要包含以下几个类
from abc import ABCMeta, abstractmethod class AbstractExpression(): """ 抽象表达式类,声明一个抽象的解释操作,这个接口为抽象语法树中所有的节点所共享 """ __metaclass__ = ABCMeta @abstractmethod def interpret(self, context): pass class TerminalExpression(AbstractExpression): """ 终结符表达式,实现与文法中的终结符相关联的解释操作。实现抽象表达式中所要求的接口,主要是一个interpret()方法。 文法中的每一个终结符都有一个具体终结符表达式与之相对应。 """ def interpret(self, context): print("终结符表达式") class NontermialExpression(AbstractExpression): """ 非终结符表达式,为文法中的非终结符实现解释操作。对文法中每一条规则R1,R2,...Rn都需要一个具体的非终结符表达式类。 通过实现抽闲表达式的interpret()方法实现解释操作。解释操作以递归的方式调用上 main所提到的代表R1,R2,...Rn中各 个符号的实例变量。 """ def interpret(self, context): print("非终结符表达式") class Context(): """ 上下文类,包含解释器之外的一些全局信息 """ def __init__(self): self.input = None self.output = None def main(): """ 客户端代码,构建表示该文法定义的语言中一个特定的句子的抽象语法树。 """ context = Context() exp_list = [] exp_list.append(TerminalExpression()) exp_list.append(NontermialExpression()) exp_list.append(TerminalExpression()) exp_list.append(NontermialExpression()) for exp in exp_list: exp.interpret(context) main()
#举个例子
class Variables(object): # Context def __init__(self): self._v = {} def put(self, variable, value: int): self._v[variable] = value def get(self, variable) -> int: return self._v.get(variable) class ArithmeticExpression(object): # AbstractExpression def interpret(self, variables) -> float: pass class Variable(ArithmeticExpression): # TerminalExpression def __init__(self, value: str): self.variable = value def interpret(self, variables): return variables.get(self.variable) class Plus(ArithmeticExpression): # NonTerminalExpression def __init__(self, left: ArithmeticExpression, right: ArithmeticExpression): self._left = left self._right = right def interpret(self, variables: Variables): return self._left.interpret(variables) + self._right.interpret(variables) class Substract(ArithmeticExpression): # NonTerminalExpression def __init__(self, left: ArithmeticExpression, right: ArithmeticExpression): self._left = left self._right = right def interpret(self, variables: Variables): return self._left.interpret(variables) - self._right.interpret(variables) class Multiply(ArithmeticExpression): # NonTerminalExpression def __init__(self, left: ArithmeticExpression, right: ArithmeticExpression): self._left = left self._right = right def interpret(self, variables: Variables): return self._left.interpret(variables) * self._right.interpret(variables) class Division(ArithmeticExpression): # NonTerminalExpression def __init__(self, left: ArithmeticExpression, right: ArithmeticExpression): self._left = left self._right = right def interpret(self, variables: Variables): return self._left.interpret(variables) / self._right.interpret(variables) class Calculator(object): def __init__(self, expression: str, context: Variables): self.expression = expression self.context = context self.value = None def get_value(self): self.calculate() return self.value def calculate(self): OP = ['+', '-', '*', '/', '(', ')', '='] priority = [ # 各运算符相遇时,优先级比较 1: 大于,2: 小于,3: 多弹一个符号 [1, 1, 2, 2, 2, 1, 1], [1, 1, 2, 2, 2, 1, 1], [1, 1, 1, 1, 2, 1, 1], [1, 1, 1, 1, 2, 1, 1], [2, 2, 2, 2, 2, 3, 0], [1, 1, 1, 1, 0, 1, 1], [2, 2, 2, 2, 2, 0, 3] ] opan = [] # 操作数 opat = ['='] # 操作符,初始带一个=为的是作为结束符使用 for char in self.expression: while True: if char not in OP: # 变量 opan.append(Variable(char)) break else: # 操作符 level = priority[OP.index(opat[-1::1][0])][OP.index(char)] if level == 1: op = opat.pop() b = opan.pop() a = opan.pop() if op == '+': opan.append(Plus(a, b)) elif op == '-': opan.append(Substract(a, b)) elif op == '*': opan.append(Multiply(a, b)) elif op == '/': opan.append(Division(a, b)) elif level == 2: opat.append(char) break elif level == 3: opat.pop() break else: break self.value = opan.pop().interpret(self.context) if __name__ == '__main__': variables = Variables() variables.put('a', 1) variables.put('b', 2) variables.put('c', 3) variables.put('d', 4) aa = Variable('a') bb = Variable('b') cc = Variable('c') dd = Variable('d') # (1-2)+(3*4) print(Plus(Substract(aa, bb), Multiply(cc, dd)).interpret(variables)) print(Calculator('a-b+c*d=', variables).get_value())
这是简书上的一个例子, https://www.jianshu.com/p/45153c25c29c
感谢作者
后面取priority 那块我也没太看懂,也不影响作为解释器模式的例子
这里就作参考
参考:
https://www.cnblogs.com/hujingnb/p/10171605.html
https://www.cnblogs.com/cbf4life/archive/2009/12/17/1626125.html
https://www.e-learn.cn/content/qita/708812
https://blog.csdn.net/hbu_pig/article/details/80808881
https://www.jianshu.com/p/d9365606fd8f
https://www.cnblogs.com/Siny0/p/11155954.html
https://www.cnblogs.com/imakoo/articles/2944578.html
https://www.cnblogs.com/chenssy/p/3679190.html
https://www.cnblogs.com/chenssy/p/3346427.html
https://www.cnblogs.com/edisonchou/p/7512733.html
https://www.jianshu.com/p/0e7e26bbe204
https://www.cnblogs.com/CheeseZH/p/9491324.html