คุณสามารถใช้โมดูล ast และเขียน NodeVisitor เพื่อตรวจสอบว่าประเภทของแต่ละโหนดเป็นส่วนหนึ่งของรายการที่อนุญาตพิเศษ
import ast, math
locals = {key: value for (key,value) in vars(math).items() if key[0] != '_'}
locals.update({"abs": abs, "complex": complex, "min": min, "max": max, "pow": pow, "round": round})
class Visitor(ast.NodeVisitor):
def visit(self, node):
if not isinstance(node, self.whitelist):
raise ValueError(node)
return super().visit(node)
whitelist = (ast.Module, ast.Expr, ast.Load, ast.Expression, ast.Add, ast.Sub, ast.UnaryOp, ast.Num, ast.BinOp,
ast.Mult, ast.Div, ast.Pow, ast.BitOr, ast.BitAnd, ast.BitXor, ast.USub, ast.UAdd, ast.FloorDiv, ast.Mod,
ast.LShift, ast.RShift, ast.Invert, ast.Call, ast.Name)
def evaluate(expr, locals = {}):
if any(elem in expr for elem in '\n#') : raise ValueError(expr)
try:
node = ast.parse(expr.strip(), mode='eval')
Visitor().visit(node)
return eval(compile(node, "<string>", "eval"), {'__builtins__': None}, locals)
except Exception: raise ValueError(expr)
เนื่องจากการทำงานผ่านรายการที่อนุญาตมากกว่าบัญชีดำจึงปลอดภัย ฟังก์ชันและตัวแปรเดียวที่สามารถเข้าถึงได้คือฟังก์ชันที่คุณให้สิทธิ์เข้าถึงอย่างชัดเจน ฉันเติมคำสั่งด้วยฟังก์ชันที่เกี่ยวข้องกับคณิตศาสตร์เพื่อให้คุณสามารถเข้าถึงสิ่งเหล่านั้นได้อย่างง่ายดายหากต้องการ แต่คุณต้องใช้มันอย่างชัดเจน
หากสตริงพยายามเรียกใช้ฟังก์ชันที่ไม่ได้จัดเตรียมไว้หรือเรียกใช้เมธอดใด ๆ จะมีการเพิ่มข้อยกเว้นและจะไม่ดำเนินการ
เนื่องจากสิ่งนี้ใช้ตัวแยกวิเคราะห์และตัวประเมินในตัวของ Python จึงสืบทอดลำดับความสำคัญและกฎการส่งเสริมการขายของ Python ด้วยเช่นกัน
>>> evaluate("7 + 9 * (2 << 2)")
79
>>> evaluate("6 // 2 + 0.0")
3.0
โค้ดด้านบนได้รับการทดสอบบน Python 3 เท่านั้น
หากต้องการคุณสามารถเพิ่มมัณฑนากรแบบหมดเวลาในฟังก์ชันนี้ได้