2016-01-25 16 views
8

Tôi đang cố gắng thiết lập "đường ống xử lý" cho dữ liệu tôi đang đọc từ nguồn dữ liệu và áp dụng chuỗi các toán tử (sử dụng trình tạo) cho từng mục khi nó được đọc.Máy phát điện Python "chuỗi" trong vòng lặp

Một số mã mẫu thể hiện cùng một vấn đề.

def reader(): 
    yield 1 
    yield 2 
    yield 3 

def add_1(val): 
    return val + 1 

def add_5(val): 
    return val + 5 

def add_10(val): 
    return val + 10 

operators = [add_1, add_5, add_10] 

def main(): 
    vals = reader() 

    for op in operators: 
     vals = (op(val) for val in vals) 

    return vals 

print(list(main())) 

mong muốn: [17, 18, 19]
thực tế: [31, 32, 33]

Python dường như không được tiết kiệm giá trị của op mỗi lần thông qua các vòng lặp for, vì vậy nó thay vì áp dụng các chức năng thứ ba mỗi lần. Có cách nào để "liên kết" hàm vận hành thực tế với biểu thức trình tạo mỗi lần thông qua vòng lặp for?

Tôi có thể giải quyết vấn đề này bằng cách thay đổi biểu thức máy phát trong vòng lặp thành danh sách, nhưng vì dữ liệu thực tế lớn hơn nhiều, tôi không muốn lưu trữ tất cả trong bộ nhớ tại bất kỳ điểm nào .

+0

Cảm ơn mọi người! Giải pháp 'map' làm việc tốt nhất cho tôi, vì có những thứ khác mà tôi muốn làm trong vòng lặp (liên quan đến việc ghi nhật ký, kiểm tra bổ sung, v.v.).Trong chương trình thực của tôi, mỗi toán tử 'thực sự là một lớp với' __call__', và có một số hàm và thuộc tính khác mà tôi cần phải giải quyết. Giải pháp 'reduce' cũng sẽ hoạt động tốt, nhưng mất khả năng thực hiện điều đó mà không cần gói mỗi toán tử trong một hàm để thực hiện các hành động bổ sung đó. – gtback

Trả lời

2

Bạn có thể buộc biến bị ràng buộc bằng cách tạo trình tạo trong một hàm mới. ví dụ.

def map_operator(operator, iterable): 
    # closure value of operator is now separate for each generator created 
    return (operator(item) for item in iterable) 

def main(): 
    vals = reader() 
    for op in operators: 
     vals = map_operator(op, vals) 
    return vals 

Tuy nhiên, map_operator là khá nhiều giống với map dựng sẵn (trong python 3.x). Vì vậy, chỉ cần sử dụng thay vào đó.

+0

Wow, tôi thực sự tự hỏi tại sao tôi không nghĩ đến việc chỉ sử dụng 'vals = map (op, vals)' .. –

+1

Trong Python 2, hãy đảm bảo sử dụng ['itertools.imap'] (https://docs.python.org/2.7/library/itertools.html#itertools.imap). Tôi học được điều này một cách khó khăn. – gtback

1

Đây có thể là những gì bạn muốn - tạo một hàm tổng hợp:

import functools 

def compose(functions): 
    return functools.reduce(lambda f, g: lambda x: g(f(x)), functions, lambda x: x) 

def reader(): 
    yield 1 
    yield 2 
    yield 3 

def add_1(val): 
    return val + 1 

def add_5(val): 
    return val + 5 

def add_10(val): 
    return val + 10 

operators = [add_1, add_5, add_10] 

def main(): 
    vals = map(compose(operators), reader()) 
    return vals 

print(list(main())) 
2

Bạn có thể định nghĩa một helper ít mà soạn các chức năng nhưng theo thứ tự ngược:

import functools 

def compose(*fns): 
    return functools.reduce(lambda f, g: lambda x: g(f(x)), fns) 

Tức là bạn có thể sử dụng compose(f,g,h) để tạo biểu thức lambda tương đương với lambda x: h(g(f(x))). Lệnh này là không phổ biến, nhưng đảm bảo rằng chức năng của bạn được áp dụng từ trái sang phải (mà có lẽ những gì bạn mong đợi):

Sử dụng này, bạn main trở thành chỉ

def main(): 
    vals = reader() 
    f = compose(add_1, add_5, add_10) 
    return (f(v) for v in vals) 
+0

Điểm tốt về thứ tự, tôi không nghĩ về điều đó. Tôi đã sửa câu trả lời của mình. – texasflood

+0

Lưu ý rằng điều này thay đổi thứ tự hoạt động: Tức là, mã OP trước tiên tính 'op_1' cho tất cả các giá trị, sau đó' op_5', v.v. trong khi trước tiên bạn áp dụng tất cả các hoạt động cho 'val_1', sau đó đến' val_2', vv Tùy thuộc vào ứng dụng, điều này có thể là hoàn toàn ổn hoặc một vấn đề. (Chỉ muốn chỉ ra) –

+0

https://mathieularose.com/function-composition-in-python/ – Alex

1

Lý do cho vấn đề này là rằng bạn đang tạo ra một máy phát điện lồng nhau sâu sắc và đánh giá toàn bộ điều sau khi vòng lặp, khi op bị ràng buộc với phần tử cuối cùng trong danh sách - tương tự như vấn đề khá phổ biến "lambda in a loop".

Trong một nghĩa nào đó, mã của bạn là tương đương với điều này:

for op in operators: 
    pass 

print(list((op(val) for val in (op(val) for val in (op(val) for val in (x for x in [1, 2, 3]))))) 

One (không phải là rất khá) cách để sửa lỗi này sẽ được zip các giá trị với khác máy phát điện, lặp lại những hoạt động tương tự:

def add(n): 
    def add_n(val): 
     return val + n 
    return add_n 
operators = [add(n) for n in [1, 5, 10]] 

import itertools 
def main(): 
    vals = (x for x in [1, 2, 3]) 

    for op in operators: 
     vals = (op(val) for (val, op) in zip(vals, itertools.repeat(op))) 

    return vals 

print(list(main())) 
Các vấn đề liên quan