2012-06-30 20 views
23

Tôi cần tạo một lớp sử dụng một lớp cơ sở khác tùy thuộc vào một số điều kiện. Với một số lớp học tôi nhận được khét tiếng:python 3: LoạiError: xung đột metaclass: metaclass của một lớp dẫn xuất phải là một lớp con (không nghiêm ngặt) của metaclasses của tất cả các base của nó

TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases 

Một ví dụ là sqlite3, đây là một ví dụ ngắn thậm chí bạn có thể sử dụng trong các phiên dịch:

>>> import sqlite3 
>>> x = type('x', (sqlite3,), {}) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases 
>>> 

Làm thế nào tôi có thể giải quyết vấn đề này?

Cảm ơn.

+5

'sqlite3' là một mô-đun không phải là "lớp". – agf

+0

@agf: Tôi chỉ xem xét điều này và nhận ra điều tương tự khi bạn đăng nội dung đó. – jdi

+0

Cảm ơn bạn, bạn nói đúng! sqlite3.Connection làm cho nó hoạt động. –

Trả lời

11

Mặc dù ví dụ của bạn sử dụng sqlite3 không hợp lệ, vì thực tế là mô-đun và không phải là một lớp, tôi đã gặp phải vấn đề tương tự này. Lớp cơ sở có một metaclass của một kiểu khác mà lớp con, và sau đó bạn thấy lỗi này.

Tôi đã sử dụng biến thể this activestate snippet using noconflict.py. Đây không phải là python 3.x tương thích không may. Nó cung cấp cho bạn ý tưởng, nhưng đoạn mã sẽ phải được làm lại.

Vấn đề đoạn mã

class M_A(type): 
    pass 
class M_B(type): 
    pass 
class A(object): 
    __metaclass__=M_A 
class B(object): 
    __metaclass__=M_B 
class C(A,B): 
    pass 

#Traceback (most recent call last): 
# File "<stdin>", line 1, in ? 
#TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass #of the metaclasses of all its bases 

Solution đoạn mã

from noconflict import classmaker 
class C(A,B): 
    __metaclass__=classmaker() 

print C 
#<class 'C'> 

Mã công thức giải quyết đúng metaclasses cho bạn.

+1

Python 3 sử dụng 'lớp C (metaclass = MyMeta): 'cú pháp – JBernardo

+0

@JBernardo: Cảm ơn. Tôi nhận xét về nó không phải là py3 compat. Nó có một vài vấn đề khác trong đoạn mã khiến nó không hoạt động. – jdi

+0

Bạn có thể sử dụng 'lớp C (sáu.with_metaclass (MyMeta))' để làm cho nó tương thích python 3.x, bạn có thể không? – Tgsmith61591

12

Thay vì sử dụng receipe như đã đề cập bởi JDI, bạn có thể trực tiếp sử dụng:

class M_C(M_A, M_B): 
    pass 

class C(A, B): 
    __metaclass__ = M_C 
6

Để sử dụng mô hình mô tả bởi @ Michael, nhưng với cả hai Python 2 và 3 tương thích (sử dụng thư viện six):

from six import with_metaclass 

class M_C(M_A, M_B): 
    pass 

class C(with_metaclass(M_C, A, B)): 
    # implement your class here 
1

theo như tôi hiểu từ các câu trả lời trước các chỉ nghĩ chúng ta thường phải làm bằng tay là:

class M_A(type): pass 
class M_B(type): pass 
class A(metaclass=M_A): pass 
class B(metaclass=M_B): pass 

class M_C(M_A, M_B): pass 
class C:(A, B, metaclass=M_C): pass 

Nhưng chúng ta có thể tự động hai dòng cuối cùng bây giờ bằng cách:

def metaclass_resolver(*classes): 
    metaclass = tuple(set(type(cls) for cls in classes)) 
    metaclass = metaclass[0] if len(metaclass)==1 \ 
       else type("_".join(mcls.__name__ for mcls in metaclass), metaclass, {}) # class M_C 
    return metaclass("_".join(cls.__name__ for cls in classes), classes, {})    # class C 

class C(metaclass_resolver(A, B)): pass 

Vì chúng ta không sử dụng bất kỳ cú pháp metaclass phiên bản cụ thể này metaclass_resolver làm việc với Python 2 cũng như Python 3.