2010-10-12 31 views
9

Tôi đang cố gắng để viết một metaclass chung để theo dõi các lớp conGeneric Python metaclass để theo dõi các lớp con

Vì tôi muốn đây là chung chung, tôi không muốn để bất kỳ hardcode tên lớp trong metaclass này, do đó tôi đã đưa ra một chức năng mà tạo ra các metaclass thích hợp, một cái gì đó như:

def make_subtracker(root): 
    class SubclassTracker(type): 
     def __init__(cls, name, bases, dct): 
      print('registering %s' % (name,)) 
      root._registry.append(cls) 
      super(SubclassTracker, cls).__init__(name, bases, dct) 
    return SubclassTracker 

bằng cách này tôi có thể gọi nó để tạo ra một metaclass cho một cụ thể gốc lớp với:

__metaclass__ = make_subtracker(Root) 

Đây là nơi tôi gặp phải sự cố. Tôi không thể làm điều này:

class Root(object): 
    _registry = [] 
    __metaclass__ = make_subtracker(Root) 

... vì Root không được định nghĩa nhưng khi tôi sử dụng make_subtracker(Root). Tôi đã cố gắng thêm metaclass thuộc tính sau, do đó ít nhất nó có thể được áp dụng trong các lớp con:

class Root(object): 
    _registry = [] 

Root.__metaclass__ = make_subtracker(Root) 

... nhưng điều này không hoạt động. metaclass có xử lý đặc biệt khi định nghĩa lớp được đọc, theo quy định tại http://docs.python.org/reference/datamodel.html#customizing-class-creation

Tôi đang tìm gợi ý để làm được điều này (hoặc thay đổi một metaclass lớp trong thời gian chạy theo một cách mà nó được áp dụng cho các lớp con của nó hoặc bất kỳ phương án thay thế nào khác).

+1

Vui lòng không làm điều này. Folks người đến sau khi bạn sẽ rip nó ra bởi vì nó quá phức tạp. Vui lòng sử dụng chức năng của nhà máy để tạo các đối tượng của lớp con thích hợp. –

Trả lời

8

Python thực hiện điều này tự động cho các lớp học kiểu mới, như đã đề cập trong answer này đến queston tương tự How can I find all subclasses of a given class in Python? đây.

+1

Mặc dù kỹ thuật này vẫn theo dõi, tùy thuộc vào cách sử dụng của bạn, nó có thể không hiệu quả lắm. Nó trả về một danh sách, vì vậy nếu bạn đang tìm kiếm một lớp với các thuộc tính cụ thể, bạn sẽ phải lặp qua toàn bộ danh sách. Sử dụng một lớp meta cho phép bạn lập chỉ mục một cách hiệu quả sublcasses tuy nhiên bạn muốn. – Cerin

+0

@Cerin: Có lẽ ... nếu hiệu quả cao là mối quan tâm hoặc vấn đề - không thể xác định được từ câu hỏi không chỉ rõ cách thông tin sẽ được sử dụng. Bất kể, tôi muốn được quan tâm thấy việc thực hiện cụ thể ý tưởng của bạn (s) vì tất cả các câu trả lời khác hiện cũng dựa trên danh sách - vì vậy hãy tự do thêm một trong những ý tưởng của riêng bạn. – martineau

+0

@martinaeu, Việc triển khai của tôi giống như aaronasterling, ngoại trừ đăng ký là một từ điển thay vì danh sách và khóa là bất kỳ biểu diễn băm nào bạn muốn sử dụng cho mỗi lớp. Tôi hiện đang sử dụng điều này như là một thay thế cho đăng ký() mô hình trong Django, để liên kết một lớp ModelForm với một slug duy nhất, để gửi URL nhanh chóng. – Cerin

8

Tôi nghĩ rằng bạn muốn một cái gì đó như thế này (chưa được kiểm tra):

class SubclassTracker(type): 
    def __init__(cls, name, bases, dct): 
     if not hasattr(cls, '_registry'): 
      cls._registry = [] 
     print('registering %s' % (name,)) 
     cls._registry.append(cls) 
     super(SubclassTracker, cls).__init__(name, bases, dct) 

Sau đó, cho Python 2, bạn có thể gọi nó như:

class Root(object): 
    __metaclass__ = SubclassTracker 

cho Python 3

class Root(object, metaclass=SubclassTracker): 

Lưu ý rằng bạn không cần phải gắn thuộc tính _registry vào đó vì những thứ như thế là metaclas ses là cho. Vì bạn đã xảy ra để có một xung quanh ...;)

Cũng lưu ý rằng bạn có thể muốn di chuyển mã đăng ký thành mệnh đề else để lớp không tự đăng ký làm lớp con.

+0

Có một lỗi cú pháp trong loại (metaclass.root._registry.append (cls) ... Tôi đã thay đổi nó thành: 'cls._registry.append (cls)' làm việc –

+0

@Carles Đó là một lỗi cú pháp xấu xí. Tôi đã chỉnh sửa một vài lần vì vậy đó là một vài ý tưởng khác nhau cho một vài dòng khác nhau mà tất cả nên được xóa. Tôi xin lỗi về điều đó.Điều này có vẻ tốt ngay bây giờ Cảm ơn bạn đã chỉ ra. – aaronasterling

+0

Trong thực tế, vì bạn SubclassTracker không bao gồm bất kỳ tham chiếu hardcoded đến * root * loại, không có nhu cầu cho một chức năng để tạo ra nó, do đó tôi trực tiếp xác định một lớp SubclassTracker và quên về make_subtracker hoàn toàn. Hoạt động tốt, cảm ơn! –

1

Dưới đây là một cái gì đó tôi đã được chơi đùa với (làm việc):

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