2015-06-23 10 views
6

Vì vậy, tôi có metaclass này mà tôi muốn sử dụng để đăng ký tự động các thành phần mới, tức là lớp con của một số thành phần cơ bản lớp học. Khi đăng ký thành phần mới, dụ của chúng tôi dự kiến ​​sẽ được chuyển đến hàm register_component() để xử lý điều đó.Lập tức một lớp được tạo ra trong một kết quả metaclass trong một RuntimeError ("super(): empty __class__ cell")

metaclass mã (phiên bản rút gọn):

class AutoRegisteredMeta(type): 
    def __new__(metacls, name, bases, attrs): 

     # ... (omitted) check if "name" has already been registered ... 

     new_class = super().__new__(metacls, name, bases, attrs) 
     register_component(name, new_class()) # RuntimeError(super(): empty __class__ cell) 
     return new_class 

Vấn đề là cách gọi new_class() kết quả trong một lỗi - nhưng không phải cho tất cả các lớp học. Sau khi một số thử nghiệm tôi nhận ra rằng điều này chỉ xảy ra nếu một lớp con gọi super().__init__() theo phương thức __init__() của riêng nó.

thành phần mẫu và các lớp cơ sở:

class BaseComponent(metaclass=AutoRegisteredMeta): 
    def __init__(self): 
     # do some work here ... 

class ComponentFoo(BaseComponent): 
    def __init__(self): 
     super().__init__() # <--- RuntimeError occurs here 
     self.foo = 'bar' 

Tôi đang làm gì sai ở đây? Đọc this Tôi phát hiện ra rằng tôi có lẽ không nên làm việc thuyết minh trong metaclass'es __new__() hoặc __init__(), phải không? Điều này có lẽ có thể được phá vỡ bằng cách nào đó?

Ngoài ra, một số giải thích về thuật ngữ của giáo dân sẽ tốt đẹp, tôi không biết nhiều nội bộ của việc triển khai CPython.

Cảm ơn trước!

(FWIW, tôi sử dụng Python 3.3.6, Ubuntu)


EDIT: tôi thêm ví dụ tối thiểu đã được yêu cầu, bạn có thể chạy nó trực tiếp và thấy lỗi trong hành động chính mình.

#!/usr/bin/env python3 


class AutoRegisteredMeta(type): 
    def __new__(metacls, name, bases, attrs): 
     new_class = super().__new__(metacls, name, bases, attrs) 
     new_class() # <--- RuntimeError can occur here 
     return new_class 


class BaseComponent(metaclass=AutoRegisteredMeta): 
    def __init__(self): 
     print("BaseComponent __init__()") 


class GoodComponent(BaseComponent): 
    def __init__(self): 
     print("GoodComponent __init__()") 


class BadComponent(BaseComponent): 
    def __init__(self): 
     print("BadComponent __init__()") 
     super().__init__() # <--- RuntimeError occurs because of this 
+2

mã số đăng ký của bạn thực sự instantiates một thể hiện của lớp trước khi metaclass đã hoàn tất. 'register_component (name, new_class)' có thể tốt hơn. –

+0

Bạn có thể tổ chức lại điều này thành [ví dụ tối thiểu] (http://stackoverflow.com/help/mcve) mà thực sự chạy (bao gồm 'register_component',' BaseComponent .__ init__', ...)? – jonrsharpe

+0

@CharlieClark Vâng, tôi biết, đó chính là vấn đề. Điều này làm tôi suy nghĩ, mặc dù, rằng việc đăng ký lớp học chứ không phải là trường hợp của nó sẽ tốt hơn, chỉ là những gì bạn, quá, đề nghị trong bình luận của bạn. Việc đăng ký một cá thể thành phần phù hợp với những gì đang được thực hiện trong phần còn lại của hệ thống, nhưng bây giờ tôi có một đối số tốt để cấu trúc lại nó. :) – plamut

Trả lời

3

có thể làm việc này:

#!/usr/bin/env python3 


class AutoRegisteredMeta(type): 
    def __new__(metacls, name, bases, attrs): 
     new_class = super().__new__(metacls, name, bases, attrs) 
     new_class() 
     return new_class 


class BaseComponent(metaclass=AutoRegisteredMeta): 
    def __init__(self): 
     print("BaseComponent __init__()") 


class GoodComponent(BaseComponent): 
    def __init__(self): 
     print("GoodComponent __init__()") 


class BadComponent(BaseComponent): 
    def __init__(self): 
     print("BadComponent __init__()") 
     super(self.__class__, self).__init__() 
+0

Tôi đã thử và nó thực sự hoạt động, cảm ơn. Thêm đối số rõ ràng vào 'super()', tức là 'self .__ class__' và' self', đã làm cho sự cố biến mất. Chấp nhận câu trả lời này. BTW, bạn có thể muốn xóa nhận xét cuối cùng trong nguồn vì nó không còn áp dụng nữa. – plamut

+0

Tôi vừa nghiên cứu nó – svs

+0

BTW, không sử dụng 'siêu (self .__ class__, self) .__ init __()' vì nó có thể dẫn đến vòng lặp vô hạn - 'self' có thể là một thể hiện của một phân lớp và sử dụng' siêu() 'như thế này thực sự có thể gọi phương thức' __init __() 'có chứa câu lệnh' super() ', lặp đi lặp lại. – plamut

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