2012-06-20 29 views
11

Cho một xâu nhưtạo một hàm lambda từ một chuỗi ** đúng **

"2*(i+j) <= 100" 

Tôi muốn tạo ra các chức năng lambda tương ứng,

fn = lambda i,j: 2*(i+j) <= 100 
  • tôi có thể làm điều này với eval, nhưng tôi đang tìm kiếm một phương pháp ít xấu xa hơn.

  • Tôi đã tìm thấy

    import ast 
    f = ast.Lambda('i,j', '2*(i+j) <= 100') 
    

    nhưng tôi đã không tìm ra cách để thực hiện kết quả!

  • Lý tưởng nhất, tôi muốn tự động kéo danh sách tham số ('i', 'j') - ngay bây giờ, tôi chỉ sử dụng re.findall ('\ w +'), nhưng tôi rất thích để có thể sử dụng đúng các chức năng hiện có như cos thay vì ẩn chúng thành 'từ khóa'.


tôi đang tìm kiếm tại Is there a Python library for handling complicated mathematical sets (constructed using mathematical set-builder notation)? và cố gắng tìm ra cách tốt nhất để phân tích các ký hiệu thiết xây dựng vào lambdas để nuôi đến chế-giải.

Tôi về cơ bản là mong ước cho ast.literal_eval cũng sẽ nhận ra các biến.

Lý tưởng nhất là, được cung cấp i >= 20 Tôi muốn lấy lại ((lambda x: x >= 20), ['i']) mà sau đó tôi có thể cấp nguồn trực tiếp cho constraint.

+0

Bạn đang cố gắng giải quyết vấn đề gì? Bạn đang thực hiện một vòng lặp đọc giải thích gp? – starbolin

Trả lời

2

Nếu đầu vào của bạn là từ một nguồn đáng tin cậy , các eval() là cách dễ dàng nhất, rõ ràng nhất, và đáng tin cậy nhất để đi.

Nếu đầu vào của bạn là không đáng tin cậy, thì cần phải là đã khử trùng.

Một cách tiếp cận hợp lý là sử dụng regex. Đảm bảo không có cuộc gọi hàm, tra cứu thuộc tính hoặc dấu gạch dưới kép trong chuỗi.

Cách khác, cách tiếp cận phức tạp hơn là đi bộ cây phân tích AST để xác định xem có bất kỳ cuộc gọi đáng ngờ nào không.

Cách tiếp cận thứ ba là đi bộ cây phân tích AST và thực thi nó trực tiếp. Điều đó đặt bạn hoàn toàn kiểm soát những gì được gọi. Hàm ast.literal_eval sử dụng phương thức này.Có lẽ bạn bắt đầu với nguồn của nó và thực hiện một số bản dựng cho bất kỳ hoạt động nào bạn muốn hỗ trợ:

def literal_eval(node_or_string): 
    """ 
    Safely evaluate an expression node or a string containing a Python 
    expression. The string or node provided may only consist of the following 
    Python literal structures: strings, numbers, tuples, lists, dicts, booleans, 
    and None. 
    """ 
    _safe_names = {'None': None, 'True': True, 'False': False} 
    if isinstance(node_or_string, basestring): 
     node_or_string = parse(node_or_string, mode='eval') 
    if isinstance(node_or_string, Expression): 
     node_or_string = node_or_string.body 
    def _convert(node): 
     if isinstance(node, Str): 
      return node.s 
     elif isinstance(node, Num): 
      return node.n 
     elif isinstance(node, Tuple): 
      return tuple(map(_convert, node.elts)) 
     elif isinstance(node, List): 
      return list(map(_convert, node.elts)) 
     elif isinstance(node, Dict): 
      return dict((_convert(k), _convert(v)) for k, v 
         in zip(node.keys, node.values)) 
     elif isinstance(node, Name): 
      if node.id in _safe_names: 
       return _safe_names[node.id] 
     elif isinstance(node, BinOp) and \ 
      isinstance(node.op, (Add, Sub)) and \ 
      isinstance(node.right, Num) and \ 
      isinstance(node.right.n, complex) and \ 
      isinstance(node.left, Num) and \ 
      isinstance(node.left.n, (int, long, float)): 
      left = node.left.n 
      right = node.right.n 
      if isinstance(node.op, Add): 
       return left + right 
      else: 
       return left - right 
     raise ValueError('malformed string') 
    return _convert(node_or_string) 
14

Bạn đang tìm kiếm giải pháp thay thế cho eval, nhưng tại sao? Bạn chấp nhận mã tùy ý và thực thi mã đó, vậy tại sao không sử dụng eval? Lý do duy nhất để tránh eval là bởi vì nó nguy hiểm, nhưng lambda bạn tạo ra sẽ chỉ là nguy hiểm.

Ngoài ra, hãy nhớ, you really can't make it safe to do this in CPython

+4

** Không thể thực hiện được ** là cụm từ hiếm khi áp dụng cho Python. Hàm * ast.literal_eval * là một ví dụ tốt về cách đánh giá mã tùy ý trong khi giới hạn những gì bạn có thể chấp nhận. Ngoài ra, nếu OP làm việc với đầu vào * tin cậy *, thì * eval * hoặc * exec * hoàn toàn hợp lý (ví dụ: Guido sử dụng chúng trong mô-đun * timeit *). –

+1

'ast.literal_eval' không phù hợp với vấn đề của OP, vì anh ta muốn biểu thức có đánh giá trong đó. Quan điểm của tôi ở đây là tiếp tục và sử dụng 'eval', nó nguy hiểm, nhưng mục đích thực sự của anh ta cũng vậy, vì vậy bất kỳ phương pháp nào khác cũng sẽ nguy hiểm như nhau. –

+0

Bài đăng trên blog hay. – dreftymac

Các vấn đề liên quan