Python Interpreter Pattern
The Interpreter Pattern is a behavioral design pattern that defines the grammar of a language and establishes an interpreter to interpret sentences in that language. Simply put, it is like a small compiler that can parse and execute expressions with specific syntactic rules.
Core Idea
Imagine a calculator scenario: when you input"2 + 3 * 4"an expression like this, the calculator needs to understand the meaning of the string and perform calculations according to mathematical operation rules. The Interpreter Pattern is a design solution that implements this process of "understanding" and "execution".
Applicable Scenarios
The Interpreter Pattern is especially suitable for the following situations:
- When there is a need to interpret and execute a language or expression
- When the grammar is relatively simple and not overly complex
- When execution efficiency is not a key consideration
- When grammar rules need to be frequently extended
Pattern Structure
Let's understand the core components of the Interpreter Pattern through a class diagram:

Component Description
AbstractExpression
- Defines the interface for interpretation operations
- Usually contains
interpret()method
TerminalExpression
- Implements the interpretation operations related to terminal symbols in the grammar
- Cannot be decomposed into smaller expressions
NonterminalExpression
- Implements the interpretation operations related to nonterminal symbols in the grammar
- Usually contains references to other expressions
Context
- Contains some global information needed by the interpreter
- Stores input and output results
Basic Syntax and Implementation
Abstract Expression Class
Example
class Expression(ABC):
"""Abstract expression class, defines the interpretation interface"""
@abstractmethod
def interpret(self, context):
"""Interpretation method, implemented by concrete subclasses"""
pass
Terminal Expression
Example
"""Number expression - terminal expression"""
def __init__(self, number):
self.number = number
def interpret(self, context):
# Directly return the numeric value
return self.number
def __str__(self):
return f"Number({self.number})"
Nonterminal Expression
Example
"""Addition expression - nonterminal expression"""
def __init__(self, left, right):
self.left = left # Left expression
self.right = right # Right expression
def interpret(self, context):
# Interpret the left and right expressions respectively, then add them together
return self.left.interpret(context) + self.right.interpret(context)
def __str__(self):
return f"({self.left} + {self.right})"
class SubtractExpression(Expression):
"""Subtraction expression - nonterminal expression"""
def __init__(self, left, right):
self.left = left
self.right = right
def interpret(self, context):
return self.left.interpret(context) - self.right.interpret(context)
def __str__(self):
return f"({self.left} - {self.right})"
class MultiplyExpression(Expression):
"""Multiplication expression - nonterminal expression"""
def __init__(self, left, right):
self.left = left
self.right = right
def interpret(self, context):
return self.left.interpret(context) * self.right.interpret(context)
def __str__(self):
return f"({self.left} * {self.right})"
Complete Example: Simple Calculator
Let's implement a complete simple calculator to interpret mathematical expressions:
Example
"""Calculator context, stores calculation-related information"""
def __init__(self):
self.variables = {} # Store variable values
def set_variable(self, name, value):
"""Set variable value"""
self.variables[name] = value
def get_variable(self, name):
"""Get variable value"""
return self.variables.get(name, 0)
class VariableExpression(Expression):
"""Variable expression - terminal expression"""
def __init__(self, name):
self.name = name
def interpret(self, context):
# Get the variable value from the context
return context.get_variable(self.name)
def __str__(self):
return self.name
class Calculator:
"""Calculator class - builds and interprets expressions"""
@staticmethod
def parse_expression(expression_str, context):
"""
Parse string expressions into an expression tree
Simplified here; actual applications may require a more complex parser
"""
# Simple expression parsing (for real projects, it is recommended to use a dedicated parsing library)
tokens = expression_str.replace('(', ' ( ').replace(')', ' ) ').split()
return Calculator._parse_tokens(tokens)
@staticmethod
def _parse_tokens(tokens):
"""Recursively parse the token list"""
if not tokens:
return None
token = tokens.pop(0)
if token == '(':
# Start parsing compound expressions
left = Calculator._parse_tokens(tokens)
operator = tokens.pop(0)
right = Calculator._parse_tokens(tokens)
tokens.pop(0) # Remove ')'
if operator == '+':
return AddExpression(left, right)
elif operator == '-':
return SubtractExpression(left, right)
elif operator == '*':
return MultiplyExpression(left, right)
else:
# Parse basic expressions (numbers or variables)
if token.isdigit() or (token[0] == '-' and token[1:].isdigit()):
return NumberExpression(int(token))
else:
return VariableExpression(token)
# Usage example
def main():
# Create context
context = CalculatorContext()
context.set_variable('x', 10)
context.set_variable('y', 5)
# Manually build expression: (x + 3) * (y - 2)
expression = MultiplyExpression(
AddExpression(VariableExpression('x'), NumberExpression(3)),
SubtractExpression(VariableExpression('y'), NumberExpression(2))
)
print(f"Expression: {expression}")
result = expression.interpret(context)
print(f"Result: {result}") # Output: (10 + 3) * (5 - 2) = 39
# Use the parser
simple_expr = AddExpression(NumberExpression(5), NumberExpression(3))
print(f"Simple expression: {simple_expr} = {simple_expr.interpret(context)}")
if __name__ == "__main__":
main()
Advanced Application: SQL WHERE Condition Interpreter
Let's look at a more practical example - implementing a simplified SQL WHERE condition interpreter:
Example
"""SQL query context"""
def __init__(self, record):
self.record = record # Data records
def get_value(self, field):
"""Get field value"""
return self.record.get(field)
class FieldExpression(Expression):
"""Field expression"""
def __init__(self, field_name):
self.field_name = field_name
def interpret(self, context):
return context.get_value(self.field_name)
class ConstantExpression(Expression):
"""Constant expression"""
def __init__(self, value):
self.value = value
def interpret(self, context):
return self.value
class EqualsExpression(Expression):
"""Equality comparison expression"""
def __init__(self, left, right):
self.left = left
self.right = right
def interpret(self, context):
return self.left.interpret(context) == self.right.interpret(context)
class AndExpression(Expression):
"""AND expression"""
def __init__(self, left, right):
self.left = left
self.right = right
def interpret(self, context):
return self.left.interpret(context) and self.right.interpret(context)
class OrExpression(Expression):
"""OR expression"""
def __init__(self, left, right):
self.left = left
self.right = right
def interpret(self, context):
return self.left.interpret(context) or self.right.interpret(context)
# Usage example: simulate SQL WHERE condition filtering
def sql_demo():
# Simulate data records
records = [
{'name': 'Alice', 'age': 25, 'department': 'Engineering'},
{'name': 'Bob', 'age': 30, 'department': 'Sales'},
{'name': 'Charlie', 'age': 28, 'department': 'Engineering'},
{'name': 'Diana', 'age': 35, 'department': 'Marketing'}
]
# Build WHERE condition: (department = 'Engineering') AND (age > 26)
class GreaterThanExpression(Expression):
def __init__(self, left, right):
self.left = left
self.right = right
def interpret(self, context):
return self.left.interpret(context) > self.right.interpret(context)
# Build expression tree
condition = AndExpression(
EqualsExpression(FieldExpression('department'), ConstantExpression('Engineering')),
GreaterThanExpression(FieldExpression('age'), ConstantExpression(26))
)
print("Qualifying records:")
for record in records:
context = SQLContext(record)
if condition.interpret(context):
print(f"- {record['name']}, {record['age']} years old, {record['department']}")
if __name__ == "__main__":
sql_demo()
Pros and Cons of the Pattern
Advantages
- Easy to extend grammar: Adding new expression classes can extend grammar rules
- Easy to implement: Each expression class is relatively simple and easy to implement and maintain
- Strong flexibility: Can dynamically change the interpretation approach
- Follows the Open/Closed Principle: Open for extension, closed for modification
Disadvantages
- Performance issues: The Interpreter Pattern is usually inefficient, especially for complex grammars
- Increased complexity: For complex grammars, a large number of classes are generated, increasing system complexity
- Difficult debugging: Debugging complex expression trees can be difficult
Best Practices and Considerations
When to Use
- When the grammar is relatively simple and stable
- When execution efficiency is not the primary consideration
- When new grammar rules need to be added frequently
Alternatives
For complex grammars, consider the following alternatives:
- Use existing parser generators(such as ANTLR, PLY)
- Use compiler/interpreter frameworks
- Consider other design patterns, such as the Visitor Pattern to handle abstract syntax trees
Performance Optimization Tips
Example
"""Optimized expression base class"""
def __init__(self):
self._cache = {} # Add caching mechanism
def interpret(self, context):
# Use cache to avoid repeated calculations
cache_key = id(context)
if cache_key not in self._cache:
self._cache[cache_key] = self._do_interpret(context)
return self._cache[cache_key]
def _do_interpret(self, context):
"""Actual interpretation logic, implemented by subclasses"""
pass
Practice Exercises
Exercise 1: Extend the Calculator
Add the following functionality to the calculator:
- Division operation
- Modulo operation
- Support parenthesized precedence
Exercise 2: Implement a Boolean Expression Interpreter
Create a boolean expression interpreter that supports:
- AND, OR, NOT operations
- Comparison operations (>, <, >=, <=, ==, !=)
- Variable substitution
Exercise 3: Design a Rule Engine
Use the Interpreter Pattern to design a simple rule engine that can:
- Parse business rules
- Evaluate rules based on input data
- Output decision results
Summary
The Interpreter pattern provides an elegant solution for handling Domain-Specific Languages (DSL). Although in actual projects, for complex grammars we tend to use professional parsing tools, understanding the principles of the Interpreter pattern is of great significance for mastering compilation principles and language processing.
Remember the core ideas of design patterns:Not every pattern fits every scenario; choosing the solution that best suits the current needs is the key。
Other extensions