2012-05-04 34 views
7

tôi cần để mô phỏng sự đếm bằng Python, và đã làm điều đó bằng cách viết các lớp học như:phương pháp __contains__ Overriding cho một lớp

class Spam(Enum): 
    k = 3 
    EGGS = 0 
    HAM = 1 
    BAKEDBEANS = 2 

Bây giờ tôi muốn thử nghiệm nếu một số liên tục là một lựa chọn hợp lệ cho một Enum đặc biệt lớp -derived, với cú pháp sau:

if (x in Foo): 
    print("seems legit") 

Vì vậy, tôi đã cố gắng để tạo ra một "Enum" lớp cơ sở nơi tôi ghi đè lên các phương pháp __contains__ như thế này:

class Enum: 
    """ 
    Simulates an enum. 
    """ 

    k = 0 # overwrite in subclass with number of constants 

    @classmethod 
    def __contains__(cls, x): 
     """ 
     Test for valid enum constant x: 
      x in Enum 
     """ 
     return (x in range(cls.k)) 

Tuy nhiên, khi sử dụng các từ khóa in trên lớp (như ví dụ trên), tôi nhận được lỗi:

TypeError: argument of type 'type' is not iterable 

Tại sao vậy? Tôi có thể bằng cách nào đó có được cú pháp cú pháp tôi muốn không?

+0

Tôi nghĩ rằng một lời nhận xét clarifiying rằng mã sẽ làm việc tốt cho: nếu x in Foo(): in ('có vẻ hợp pháp') Làm cho cả câu hỏi và câu trả lời dễ hiểu hơn. Vì loại (Foo()) là Foo nhưng gõ (Foo) là lớp meta. –

Trả lời

13

Why that?

Khi bạn sử dụng cú pháp đặc biệt như a in Foo, phương pháp __contains__ được nhìn lên vào loại Foo. Tuy nhiên, triển khai __contains__ của bạn tồn tại trên Foo chính nó, không phải loại của nó. Loại của Footype, không thực hiện điều này (hoặc lặp lại), do đó là lỗi.

Tình huống tương tự xảy ra nếu bạn khởi tạo đối tượng và sau đó, sau được tạo, thêm chức năng __contains__ vào các biến mẫu. chức năng đó sẽ không được gọi là:

>>> class Empty: pass 
... 
>>> x = Empty() 
>>> x.__contains__ = lambda: True 
>>> 1 in x 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: argument of type 'Empty' is not iterable 

Can I somehow get the syntactic sugar I want?

Yes. Như đã đề cập ở trên, phương pháp được tìm kiếm theo kiểu của Foo. Loại của một lớp được gọi là metaclass, vì vậy, bạn cần một metaclass mới triển khai __contains__.

Hãy thử điều này một:

class MetaEnum(type): 
    def __contains__(cls, x): 
      return x in range(cls.k) 

Như bạn thấy, các phương pháp trên một metaclass lấy ví dụ metaclass - lớp - như là đối số đầu tiên của họ. Điều này sẽ có ý nghĩa. Nó rất giống với một classmethod, ngoại trừ việc phương thức sống trên metaclass chứ không phải lớp.

thừa kế từ một lớp học với một metaclass tùy chỉnh cũng được thừa hưởng các metaclass, vì vậy bạn có thể tạo một lớp cơ sở như vậy:

class BaseEnum(metaclass=MetaEnum): 
    pass 

class MyEnum(BaseEnum): 
    k = 3 

print(1 in MyEnum) # True 
+1

Ah, tôi đã có một ý tưởng mơ hồ rằng metaclasses tồn tại, nhưng tôi chưa bao giờ có một trường hợp sử dụng của riêng tôi. Điều này hoạt động tốt, cảm ơn. – clstaudt

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