2012-03-11 39 views
11

Vì vậy, tôi đang đọc một chút về metaclasses trong Python, và cách thay đổi ba đối số của type() được sử dụng để tạo động các lớp. Tuy nhiên, đối số thứ ba thường là dict để khởi tạo biến được tạo ra '__dict__.Metaclasses và __slots__?

Nếu tôi muốn tạo động các lớp học dựa trên metaclass sử dụng __slots__ thay vì __dict__, tôi làm cách nào để thực hiện việc này? type() vẫn được sử dụng trong một số thời trang cùng với ghi đè __new__()?

Là một FYI, tôi biết về việc sử dụng thích hợp cho __slots__, để tiết kiệm bộ nhớ khi tạo số lượng lớn một lớp so với lạm dụng nó để thực thi một dạng an toàn kiểu.


Ví dụ về một (kiểu mới) lớp bình thường mà bộ __metaclass__ và sử dụng một __dict__:

class Meta(type): 
    def __new__(cls, name, bases, dctn): 
     # Do something unique ... 
     return type.__new__(cls, name, bases, dctn) 

class Foo(object): 
    __metaclass__ = Meta 

    def __init__(self): 
     pass 


Ở phía trên, type.__new__() được gọi và đối số thứ tư (mà trở thành thứ ba khi thực tế sử dụng) tạo một __dict__ trong Foo. Nhưng nếu tôi muốn sửa đổi Meta để bao gồm __slots__, thì tôi không có từ điển nào để chuyển sang chức năng type() 's __new__() (theo như tôi biết - tôi chưa thử nghiệm bất kỳ điều nào trong số này, chỉ cân nhắc và cố gắng tìm một số loại kịch bản sử dụng).

Chỉnh sửa: Một dự đoán nhanh chóng, nhưng chưa được kiểm tra, là lấy một giá trị của các giá trị được đưa vào biến số __slots__ và chuyển nó đến type.__new__(). Sau đó, thêm __init__() vào Meta để điền các biến số __slots__ từ dict. Mặc dù, tôi không chắc chắn như thế nào dict rằng sẽ đạt __init__(), bởi vì việc kê khai của __slots__ ngăn __dict__ từ được tạo ra trừ khi __dict__ được định nghĩa trong __slots__ ...

Trả lời

15

Bạn không thể tạo một kiểu với một thuộc tính __slots__ không trống. Những gì bạn có thể làm là chèn một thuộc tính __slots__ vào dict lớp mới, như thế này:

class Meta(type): 
    def __new__(cls, name, bases, dctn): 
     dctn['__slots__'] = ('x',) 
     return type.__new__(cls, name, bases, dctn) 

class Foo(object): 
    __metaclass__ = Meta 

    def __init__(self): 
     pass 

thuộc tính Bây giờ Foo đã rãnh:

foo = Foo() 
foo.y = 1 

ném

AttributeError: 'Foo' object has no attribute 'y' 
+0

Điều này có vẻ thú vị. Tôi sẽ thử nó sau vài phút nữa. – Kumba

+0

Không bao giờ có xung quanh để thử nghiệm này, nhưng tôi nghĩ rằng điều này kỹ thuật trả lời câu hỏi. Tôi sẽ tìm ra một ngày. – Kumba

+0

Vừa mới đăng nhập trở lại SO trong độ tuổi; câu trả lời này là cuối cùng những gì làm việc. Cảm ơn! – Kumba

5

dctn trong ví dụ của bạn của một metaclass là lớp từ điển, không phải là từ điển dụ. __slots__ thay thế từ điển dụ. Nếu bạn tạo ra hai ví dụ:

class Meta(type): 
    def __new__(cls, name, bases, dctn): 
     return type.__new__(cls, name, bases, dctn) 

class Foo1(object): 
    __metaclass__ = Meta 

class Foo2(object): 
    __metaclass__ = Meta 
    __slots__ = ['a', 'b'] 

Sau đó:

>>> f1 = Foo1() 
>>> f2 = Foo2() 
>>> f1.__dict__ is Foo1.__dict__ 
False 
>>> f2.__dict__ 
Traceback (most recent call last): 
    ... 
AttributeError: 'Foo2' object has no attribute '__dict__' 
+1

Được rồi, tôi vẫn đang học các sợi dây trên Python ở đây và bắt đầu quấn quanh câu thần chú của Python "mọi thứ là một đối tượng" (tương tự như "mọi thứ là một tệp" của UNIX). Không thể xác định '__slots__' trong Metaclass và nó có được truyền tới các lớp được tạo từ metaclass không? Hay đây là nơi mà một lớp cơ sở trở nên hữu ích hơn? Hoặc, tôi có muốn kết hợp cả hai khái niệm? – Kumba

+0

Câu hỏi hay. Bạn chắc chắn có thể định nghĩa '__slots__' trong metaclass đơn giản bằng cách thêm vào' dctn'. Tuy nhiên, phân lớp từ một lớp cơ sở thường sẽ là một cách tiếp cận tốt hơn. Nói chung, nếu bạn không chắc chắn nếu bạn cần một metaclass, sau đó bạn không. – aquavitae

+0

Đó là lý do tại sao tôi đang cố gắng tìm hiểu về metaclasses nhiều hơn một chút, vì vậy tôi có thể xác định xem tôi có thực sự cần chúng hay không. Lưu ý, tôi sắp tắt của một nền tảng trong .NET, nơi tôi đã phải đối mặt với generics, giao diện, và đơn thừa kế, và không bao giờ có đào tạo chính thức trong OOP (chỉ là một vài lớp học trên C cũ tốt). Cố gắng hiểu sự khác biệt khá độc đáo của Python được chứng minh là khá thú vị ở lần. Cũng giống như người ta tìm cách để biến một con nứa thành một con trebuchet để phóng mầm brussel trên bàn trong khi ăn tối. – Kumba

0

Thông tin thêm về khe được xác định trong metaclasses.

nếu lớp con của type xác định không trống __slots__, Python sẽ ném một số TypeError vì một số complicated implementation-related stuff.

In [1]: 

class Meta(type): 
    __slots__ = ('x') 

--------------------------------------------------------------------------- 
TypeError         Traceback (most recent call last) 
<ipython-input-38-1b16edef8eca> in <module>() 
----> 1 class Meta(type): 
     2  __slots__ = ('x') 

TypeError: nonempty __slots__ not supported for subtype of 'type' 

Rỗng __slots__ Mặt khác không tạo ra lỗi nào, nhưng đường may không có hiệu lực.

In [2]: 

class Meta(type): 
    __slots__ =() 

class Foo(metaclass=Meta): 
    pass 

type(Foo) 

Out [2]: 

__main__.Meta 

In [3]: 

Foo.y = 42 
Foo.y 

Out [3]: 

42 

In [4]: 

Foo.__dict__ 

Out [4]: 

mappingproxy({'y': 42, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__doc__': None, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__module__': '__main__'}) 

In [5]: 

foo = Meta('foo',(), {}) 
type(foo).__slots__ 

Out [5]: 

() 

In [6]: 

foo.x = 42 
foo.x 

Out [6]: 

42 

In [7]: 

foo.__dict__ 

Out [7]: 

mappingproxy({'__dict__': <attribute '__dict__' of 'foo' objects>, 'x': 42, '__module__': '__main__', '__doc__': None, '__weakref__': <attribute '__weakref__' of 'foo' objects>}) 

In [8]: 

# Testing on non-metaclasses. Just in case. 
class Bar: 
    __slots__ =() 

b = Bar() 
b.__dict__ 

--------------------------------------------------------------------------- 
AttributeError       Traceback (most recent call last) 
<ipython-input-54-843730c66f3f> in <module>() 
     3 
     4 b = Bar() 
----> 5 b.__dict__ 

AttributeError: 'Bar' object has no attribute '__dict__' 
Các vấn đề liên quan