2012-07-05 18 views
8

Tôi có một lớp đối tượng mà thực hiện một cử động __ iter __ sử dụng một máy phát bộ nhớ đệm (Tôi cũng có một phương pháp vô hiệu các bộ nhớ cache ITER) như sau:__iter __() thực hiện như một máy phát điện

def __iter__(self): 
    print("iter called") 
    if self.__iter_cache is None: 
     iter_seen = {} 
     iter_cache = [] 
     for name in self.__slots: 
      value = self.__slots[name] 
      iter_seen[name] = True 
      item = (name, value) 
      iter_cache.append(item) 
      yield item   
     for d in self.__dc_list: 
      for name, value in iter(d): 
       if name not in iter_seen: 
        iter_seen[name] = True 
        item = (name, value) 
        iter_cache.append(item) 
        yield item 
     self.__iter_cache = iter_cache 
    else: 
     print("iter cache hit") 
     for item in self.__iter_cache: 
      yield item 

Dường như với được làm việc ... Có bất kỳ gotchas tôi có thể không nhận thức được? Tôi có làm điều gì đó vô lý không?

+2

Tôi ít nhất sẽ sử dụng ['set'] (http://docs.python.org/library/stdtypes.html#set) thay vì' dict' cho cấu trúc 'iter_seen'. –

+0

Hm, điều gì khiến tôi thực sự? Vì tôi không cần thiết lập đại số, sẽ không dict được một thực hiện hợp lý hơn và nhẹ? –

+2

thay thế 'cho _ trong lần lặp (bất kỳ)' bằng 'cho _ trong bất cứ điều gì'. Bạn không bao giờ cần 'iter' bên trong câu lệnh' for' – jfs

Trả lời

1

Có vẻ như đó là một cách tiếp cận rất mong manh. Nó đủ để thay đổi bất kỳ __slots, __dc_list, __iter_cache nào trong quá trình lặp hoạt động để đưa đối tượng vào trạng thái không nhất quán.

Bạn cần cấm thay đổi đối tượng trong khi lặp hoặc tạo tất cả các mục trong bộ nhớ cache cùng một lúc và trả về một bản sao của danh sách.

+0

Đúng. __slots chỉ được thay đổi bởi __setitem__ hoặc __delitem__, tôi có thể dễ dàng cấm các op đó (tăng exc) khi máy phát đang hoạt động. __dc_list hiện chỉ được thiết lập/thay đổi trong __init__, nếu tôi thêm một phương thức để cập nhật nó (tôi có thể sẽ) tôi cần phải sao chép ngữ nghĩa cấm từ __slots. __iter_cache không phải là một vấn đề. Nó chỉ được cập nhật bởi __iter__ và chỉ sau khi toàn bộ chuỗi được liệt kê. –

+1

nhiều lần lặp đồng thời tương tự như đa luồng trong một số khía cạnh. Nó dễ dàng hơn nhiều để lý do về nó nếu các đối tượng là bất biến. Hãy tưởng tượng ba lần lặp: lần đầu tiên điền vào bộ đệm, thứ ba sử dụng bộ đệm, thứ hai bắt đầu một thời gian trước khi bộ nhớ cache được đặt nhưng sau khi đối tượng được thay đổi (nó có thể thấy giá trị mới hơn sau đó lần lặp thứ 3 được bắt đầu sau nó) – jfs

+1

I yêu trang này Câu trả lời nhanh chóng và dễ hiểu của bạn đã tăng đáng kể kiến ​​thức Py của tôi, cảm ơn các bạn! –

2

container.__iter__() trả về đối tượng trình lặp. Các đối tượng iterator mình được yêu cầu để hỗ trợ hai phương pháp sau đây, mà cùng nhau tạo thành giao thức iterator:

iterator.__iter__() 

Trả về đối tượng iterator riêng của mình.

iterator.next() 

Trả lại mục tiếp theo từ vùng chứa.

Đó chính là điều mà mỗi máy phát có. Vì vậy, đừng sợ bất kỳ tác dụng phụ nào.

+3

Phương thức '__iter __()' của một đối tượng chứa một trình tạo bằng cách sử dụng một hoặc nhiều câu lệnh 'yield' là một phím tắt chung, tránh phải định nghĩa và mã hóa một lớp trình lặp riêng biệt và các phương thức của nó. – martineau

2

Nó có thể là tốt hơn để tách các iteration của đối tượng từ bộ nhớ đệm của các giá trị nó trả về. Điều đó sẽ đơn giản hóa quá trình lặp lại và cho phép bạn dễ dàng kiểm soát cách bộ nhớ đệm được thực hiện cũng như cho dù nó được kích hoạt hay không, ví dụ.

Một khác có thể là xem xét quan trọng là mã của bạn sẽ không tiên đoán xử lý tình huống mà đối tượng được lặp lại bị thay đổi giữa các lần gọi liên tiếp đến phương thức. Một cách đơn giản để giải quyết vấn đề đó là điền toàn bộ nội dung của bộ nhớ cache vào cuộc gọi đầu tiên và sau đó chỉ yield nội dung chứa trong mỗi cuộc gọi - và ghi lại hành vi.

+0

Điểm tốt về việc chia tách, tôi thực sự đã thử rằng :-) –

0

Những gì bạn đang làm là hợp lệ mặc dù lạ. __slots hoặc __dc_list là gì ?? Nói chung tốt hơn là mô tả nội dung của đối tượng của bạn trong một tên thuộc tính, thay vì loại của nó (ví dụ: self.users chứ không phải self.u_list).

Bạn có thể sử dụng trang trí LazyProperty để đơn giản hóa điều này một cách đáng kể.

Chỉ cần trang trí phương pháp của bạn với @LazyProperty. Nó sẽ được gọi là lần đầu tiên, và người trang trí sau đó sẽ thay thế thuộc tính bằng các kết quả. Yêu cầu duy nhất là giá trị có thể lặp lại; nó không phụ thuộc vào trạng thái có thể thay đổi được. Bạn cũng có yêu cầu đó trong mã hiện tại của bạn, với tự của bạn .__ iter_cache.

def __iter__(self) 
    return self.__iter 

@LazyProperty 
def __iter(self) 
    def my_generator(): 
     yield whatever 
    return tuple(my_generator()) 
+0

Lạ lùng, có thể. slot là các thuộc tính của riêng các đối tượng (ghi đè) và dc_list là một danh sách các đối tượng nguyên mẫu cho các vị trí sao chép (đệ quy) từ. Tôi đang cố gắng để thực hiện một cái gì đó giống như cơ cấu đoàn đại biểu của Tự trong Py. –

+0

'__iter__' phải trả về một trình lặp, tuple không phải là một. – jfs

+0

Điểm tốt, tôi muốn thay đổi __iter để chỉ cần trả lại my_generator –

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