Python字符串模板替换定制:仅允许唯一合法输出的技术问询
Ensuring All Placeholders Are Replaced & No Extra Keys Allowed
To make sure only the first print statement produces a valid output (and the other two fail), we need two key checks:
- Every placeholder in the template has a corresponding value in the substitution dict (already handled by
substituteviaKeyErrorfor missing keys). - The substitution dict doesn't contain any extra keys that aren't present as placeholders in the template (this is the missing check in your current code).
Here's the adjusted MyTemplate implementation that adds these safeguards:
import string import re class MyTemplate(string.Template): delimiter = '$' # Fixed the braced placeholder pattern to properly match ${name}$ (optional but correct) pattern = r''' \$(?: (?P<escaped>\$)| # Matches $$ (escaped $) (?P<named>[_a-z][_a-z0-9]*)\$| # Matches $name$ (named placeholder) (?P<braced>\{[_a-z][_a-z0-9]*\})\$| # Matches ${name}$ (braced placeholder) (?P<invalid>) # Catches invalid $ usage ) ''' def substitute(self, mapping=None, **kwds): # Merge mapping and keyword arguments into a single dictionary if mapping is None: mapping = {} full_mapping = dict(mapping, **kwds) # Extract all valid placeholders from the template placeholders = set() for match in re.finditer(self.pattern, self.template, re.VERBOSE): groups = match.groupdict() # Add named placeholders if groups['named']: placeholders.add(groups['named']) # Add braced placeholders (strip braces to get the name) if groups['braced']: placeholders.add(groups['braced'].strip('{}')) # Check for extra keys in the substitution dict extra_keys = set(full_mapping.keys()) - placeholders if extra_keys: raise ValueError(f"Extra unused keys provided: {', '.join(extra_keys)}") # Let the parent class handle missing placeholders (raises KeyError) return super().substitute(full_mapping) # Test cases data1 = "max=$max$ min=$min$" data2 = "max=$max$ " # 1. Valid case: all placeholders present, no extra keys try: print(MyTemplate(data1).substitute({"max":"10","min":"1"})) # Output: max=10 min=1 except Exception as e: print(f"Error: {e}") # 2. Invalid case: extra key 'min' provided try: print(MyTemplate(data2).substitute({"max":"10","min":"1"})) except Exception as e: print(f"Error: {e}") # Output: Error: Extra unused keys provided: min # 3. Invalid case: missing key 'min' try: print(MyTemplate(data1).substitute({"max":"10"})) except Exception as e: print(f"Error: {e}") # Output: Error: 'min'
Key Changes Explained:
- Fixed Braced Placeholder Pattern: The original pattern incorrectly treated braced placeholders the same as named ones. Now it properly matches
${name}$if you need braced syntax. - Custom
substituteMethod:- Merges input mapping and keyword arguments into one dict for consistency.
- Uses the template's regex pattern to extract all valid placeholders from the template string.
- Checks if the substitution dict has any keys that aren't in the placeholder set, raising a
ValueErrorif so. - Calls the parent
substitutemethod, which automatically raises aKeyErrorif any placeholder is missing from the mapping.
This ensures only the first test case succeeds, while the other two fail with clear errors—exactly the single valid output scenario you want.
内容的提问来源于stack exchange,提问作者Gianni




