Mixin là gì và tại sao chúng hữu ích?


954

Trong " Lập trình Python ", Mark Lutz có đề cập đến "mixins". Tôi đến từ nền tảng C / C ++ / C # và tôi chưa nghe thấy thuật ngữ này trước đây. Một mixin là gì?

Đọc giữa các dòng của ví dụ này (mà tôi đã liên kết vì nó khá dài), tôi cho rằng đó là trường hợp sử dụng nhiều kế thừa để mở rộng một lớp trái ngược với phân lớp 'đúng'. Thê nay đung không?

Tại sao tôi muốn làm điều đó hơn là đưa chức năng mới vào một lớp con? Đối với vấn đề đó, tại sao một cách tiếp cận mixin / nhiều kế thừa sẽ tốt hơn so với sử dụng thành phần?

Điều gì tách một mixin từ nhiều thừa kế? Có phải chỉ là một vấn đề ngữ nghĩa?

Câu trả lời:


710

Một mixin là một loại thừa kế đặc biệt. Có hai tình huống chính được sử dụng mixin:

  1. Bạn muốn cung cấp nhiều tính năng tùy chọn cho một lớp.
  2. Bạn muốn sử dụng một tính năng cụ thể trong nhiều lớp khác nhau.

Để biết ví dụ về số một, hãy xem xét hệ thống yêu cầu và phản hồi của werkzeug . Tôi có thể tạo một đối tượng yêu cầu cũ đơn giản bằng cách nói:

from werkzeug import BaseRequest

class Request(BaseRequest):
    pass

Nếu tôi muốn thêm hỗ trợ tiêu đề chấp nhận, tôi sẽ thực hiện điều đó

from werkzeug import BaseRequest, AcceptMixin

class Request(AcceptMixin, BaseRequest):
    pass

Nếu tôi muốn tạo một đối tượng yêu cầu hỗ trợ chấp nhận tiêu đề, etags, xác thực và hỗ trợ tác nhân người dùng, tôi có thể làm điều này:

from werkzeug import BaseRequest, AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin

class Request(AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin, BaseRequest):
    pass

Sự khác biệt là tinh tế, nhưng trong các ví dụ trên, các lớp mixin không được tạo ra để tự đứng vững. Trong nhiều kế thừa truyền thống hơn, AuthenticationMixin(ví dụ) có thể sẽ là một cái gì đó giống như Authenticator. Đó là, lớp có lẽ sẽ được thiết kế để tự đứng.


123
Tình huống thứ ba là: bạn muốn cung cấp nhiều tính năng (không phải tùy chọn) cho một lớp, nhưng bạn muốn các tính năng trong các lớp riêng biệt (và trong các mô-đun riêng biệt) để mỗi mô-đun là về một tính năng (hành vi.) IOW, không phải để sử dụng lại, nhưng để ngăn cách.
bootchk

60
Có thể không phải là một vấn đề trong ví dụ này, nhưng bạn thường muốn đặt lớp cơ sở chính làm phần tử cuối cùng trong ngoặc đơn để tạo chuỗi thừa kế: Request ==> Mixin ==> ... ==> BaseRequest. Xem tại đây: ianlewis.org/en/mixins-and-python
hillel

10
@hillel điểm tốt, nhưng hãy nhớ rằng Python sẽ gọi các phương thức của siêu lớp từ trái sang phải (ví dụ khi bạn cần ghi đè hàm tạo).
Eliseu Monar dos Santos

9
Điều này nghe có vẻ giống như mẫu thiết kế Decorator.
D-Jones

4
Một tình huống thứ 4 là: Đã có một gia đình hiện tại của Parentlớp và Child1, Child2, ChildNlớp con bên trong một thư viện của bên thứ 3, và bạn muốn có một hành vi tùy biến cho cả gia đình. Lý tưởng nhất là bạn muốn thêm hành vi đó vào Parentvà hy vọng nhà phát triển thư viện bên thứ 3 sẽ thực hiện Yêu cầu kéo của bạn. Nếu không, bạn sẽ phải thực hiện của riêng bạn class NewBehaviorMixin, và sau đó xác định một tập hợp đầy đủ các lớp wrapper như class NewParent(NewBehaviorMixin, Parent): passclass NewChildN(NewBehaviorMixin, ChildN): passvv: (PS Bạn có biết một cách tốt hơn?)
RayLuo

240

Đầu tiên, bạn cần lưu ý rằng mixin chỉ tồn tại trong các ngôn ngữ đa kế thừa. Bạn không thể thực hiện một mixin trong Java hoặc C #.

Về cơ bản, mixin là một loại cơ sở độc lập cung cấp chức năng hạn chế và cộng hưởng đa hình cho một lớp con. Nếu bạn đang nghĩ về C #, hãy nghĩ đến một giao diện mà bạn không thực sự phải thực hiện vì nó đã được triển khai; bạn chỉ cần kế thừa từ nó và hưởng lợi từ chức năng của nó.

Mixins thường hẹp về phạm vi và không có nghĩa là được mở rộng.

[chỉnh sửa - như tại sao:]

Tôi cho rằng tôi nên giải quyết tại sao, vì bạn hỏi. Lợi ích lớn là bạn không phải tự mình làm đi làm lại nhiều lần. Trong C #, nơi lớn nhất mà mixin có thể được hưởng lợi có thể là từ mẫu Xử lý . Bất cứ khi nào bạn triển khai IDis Dùng, bạn hầu như luôn muốn theo cùng một mẫu, nhưng cuối cùng bạn lại viết và viết lại cùng một mã cơ bản với các biến thể nhỏ. Nếu có một mixin Xử lý có thể mở rộng, bạn có thể tiết kiệm cho mình rất nhiều lần gõ thêm.

[chỉnh sửa 2 - để trả lời các câu hỏi khác của bạn]

Điều gì tách một mixin từ nhiều thừa kế? Có phải chỉ là một vấn đề ngữ nghĩa?

Đúng. Sự khác biệt giữa mixin và thừa kế tiêu chuẩn chỉ là vấn đề ngữ nghĩa; một lớp có nhiều kế thừa có thể sử dụng một mixin như là một phần của nhiều kế thừa đó.

Điểm của mixin là tạo ra một loại có thể được "trộn" vào bất kỳ loại nào khác thông qua kế thừa mà không ảnh hưởng đến loại kế thừa trong khi vẫn cung cấp một số chức năng có lợi cho loại đó.

Một lần nữa, hãy nghĩ về một giao diện đã được thực hiện.

Cá nhân tôi không sử dụng mixins vì tôi phát triển chủ yếu bằng ngôn ngữ không hỗ trợ họ, vì vậy tôi đang gặp khó khăn khi đưa ra một ví dụ điển hình sẽ cung cấp "ahah!" Khoảnh khắc dành cho bạn. Nhưng tôi sẽ thử lại. Tôi sẽ sử dụng một ví dụ giả định - hầu hết các ngôn ngữ đã cung cấp tính năng này bằng cách này hay cách khác - nhưng hy vọng điều đó sẽ giải thích cách mixin được tạo và sử dụng. Đây là:

Giả sử bạn có một loại mà bạn muốn có thể tuần tự hóa đến và từ XML. Bạn muốn loại cung cấp phương thức "ToXML" trả về một chuỗi chứa một đoạn XML với các giá trị dữ liệu của loại và "FromXML" cho phép loại đó tái tạo lại các giá trị dữ liệu của nó từ một đoạn XML trong một chuỗi. Một lần nữa, đây là một ví dụ giả định, vì vậy có lẽ bạn sử dụng một luồng tệp hoặc một lớp Trình ghi XML từ thư viện thời gian chạy ngôn ngữ của bạn ... bất cứ điều gì. Vấn đề là bạn muốn tuần tự hóa đối tượng của mình thành XML và lấy lại một đối tượng mới từ XML.

Điểm quan trọng khác trong ví dụ này là bạn muốn làm điều này một cách chung chung. Bạn không muốn phải thực hiện phương thức "ToXML" và "FromXML" cho mọi loại mà bạn muốn tuần tự hóa, bạn muốn một số phương tiện chung để đảm bảo rằng loại của bạn sẽ làm điều này và nó chỉ hoạt động. Bạn muốn sử dụng lại mã.

Nếu ngôn ngữ của bạn hỗ trợ nó, bạn có thể tạo mixin XmlSerializable để thực hiện công việc cho bạn. Kiểu này sẽ triển khai các phương thức ToXML và FromXML. Nó sẽ sử dụng một số cơ chế không quan trọng với ví dụ, có khả năng thu thập tất cả dữ liệu cần thiết từ bất kỳ loại nào được trộn lẫn để xây dựng đoạn XML được ToXML trả về và nó có khả năng khôi phục dữ liệu đó khi FromXML gọi là.

Và .. đó là nó. Để sử dụng nó, bạn sẽ có bất kỳ loại nào cần được tuần tự hóa để kế thừa XML từ XmlSerializable. Bất cứ khi nào bạn cần tuần tự hóa hoặc giải tuần tự loại đó, bạn chỉ cần gọi ToXML hoặc FromXML. Trên thực tế, vì XmlSerializable là loại đa hình và đa hình, bạn có thể hình dung một trình sắp xếp tài liệu mà không biết gì về loại ban đầu của mình, chỉ chấp nhận, một mảng các loại XmlSerializable.

Bây giờ hãy tưởng tượng sử dụng kịch bản này cho những thứ khác, như tạo một mixin để đảm bảo rằng mọi lớp trộn nó trong nhật ký mỗi cuộc gọi phương thức hoặc một mixin cung cấp tính giao dịch cho kiểu trộn nó. Danh sách có thể tiếp tục và bật.

Nếu bạn chỉ nghĩ rằng mixin là một loại cơ sở nhỏ được thiết kế để thêm một lượng nhỏ chức năng vào một loại mà không ảnh hưởng đến loại đó, thì bạn thật tuyệt vời.

Hy vọng. :)


25
Này, bạn có thích cụm từ "cộng hưởng đa hình" không? Làm cho nó lên chính mình. Tôi nghĩ. Có lẽ tôi đã nghe thấy nó trong vật lý ở đâu đó ...
Randolpho

50
Tôi không đồng ý một chút về câu đầu tiên của bạn. Ruby là một ngôn ngữ thừa kế đơn và mixin là cách để thêm các phương thức vào một lớp nhất định với sự kế thừa từ một lớp khác.
Keltia

23
@Keltia: Tôi nghĩ rằng mixin là - theo định nghĩa - đa thừa kế. Trong trường hợp Ruby, chúng là một con khỉ (hoặc một cái gì đó khác) không phải là một hỗn hợp thích hợp. Những người Ruby có thể gọi nó là mixin, nhưng đó là một loại khác.
S.Lott

10
Trên thực tế, một mixin thực sự không thể sử dụng nhiều kế thừa. Một mixin bao gồm các phương thức, thuộc tính, v.v. từ một lớp trong một lớp khác mà không kế thừa nó. Điều này có xu hướng mang lại lợi ích của việc tái sử dụng mã có vẻ như đa hình nhưng bỏ qua các vấn đề xác định nguồn gốc (kim cương của cái chết, v.v.) Các ngôn ngữ hỗ trợ Mixin cũng có xu hướng cho phép bao gồm một phần lớp mixin (mọi thứ bắt đầu nghe hơi giống các khía cạnh bây giờ).
Trevor

8
Đối với bản ghi, Java hiện hỗ trợ mixins với các phương thức mặc định.
shmosel 17/08/2015

170

Câu trả lời này nhằm giải thích các mixin với các ví dụ :

  • khép kín : ngắn gọn, không cần biết bất kỳ thư viện nào để hiểu ví dụ.

  • trong Python , không phải trong các ngôn ngữ khác.

    Có thể hiểu rằng có các ví dụ từ các ngôn ngữ khác như Ruby vì thuật ngữ này phổ biến hơn nhiều trong các ngôn ngữ đó, nhưng đây là một chủ đề Python .

Nó cũng sẽ xem xét câu hỏi gây tranh cãi:

Là thừa kế cần thiết hay không để đặc trưng cho một mixin?

Định nghĩa

Tôi vẫn chưa thấy một trích dẫn từ một nguồn "có thẩm quyền" nói rõ một mixin trong Python là gì.

Tôi đã thấy 2 định nghĩa có thể có của một mixin (nếu chúng được coi là khác với các khái niệm tương tự khác như các lớp cơ sở trừu tượng) và mọi người không hoàn toàn đồng ý về cái nào là đúng.

Sự đồng thuận có thể khác nhau giữa các ngôn ngữ khác nhau.

Định nghĩa 1: không thừa kế nhiều

Một mixin là một lớp sao cho một số phương thức của lớp sử dụng một phương thức không được định nghĩa trong lớp.

Do đó, lớp không có nghĩa là được khởi tạo, mà là phục vụ như một lớp cơ sở. Nếu không, cá thể sẽ có các phương thức không thể được gọi mà không đưa ra một ngoại lệ.

Một ràng buộc mà một số nguồn thêm vào là lớp có thể không chứa dữ liệu, chỉ có các phương thức, nhưng tôi không hiểu tại sao điều này lại cần thiết. Tuy nhiên, trong thực tế, nhiều mixin hữu ích không có bất kỳ dữ liệu nào và các lớp cơ sở không có dữ liệu sẽ đơn giản hơn để sử dụng.

Một ví dụ kinh điển là việc triển khai tất cả các toán tử so sánh chỉ từ <===:

class ComparableMixin(object):
    """This class has methods which use `<=` and `==`,
    but this class does NOT implement those methods."""
    def __ne__(self, other):
        return not (self == other)
    def __lt__(self, other):
        return self <= other and (self != other)
    def __gt__(self, other):
        return not self <= other
    def __ge__(self, other):
        return self == other or self > other

class Integer(ComparableMixin):
    def __init__(self, i):
        self.i = i
    def __le__(self, other):
        return self.i <= other.i
    def __eq__(self, other):
        return self.i == other.i

assert Integer(0) <  Integer(1)
assert Integer(0) != Integer(1)
assert Integer(1) >  Integer(0)
assert Integer(1) >= Integer(1)

# It is possible to instantiate a mixin:
o = ComparableMixin()
# but one of its methods raise an exception:
#o != o 

Ví dụ cụ thể này có thể đã đạt được thông qua functools.total_ordering()trang trí, nhưng trò chơi ở đây là phát minh lại bánh xe:

import functools

@functools.total_ordering
class Integer(object):
    def __init__(self, i):
        self.i = i
    def __le__(self, other):
        return self.i <= other.i
    def __eq__(self, other):
        return self.i == other.i

assert Integer(0) < Integer(1)
assert Integer(0) != Integer(1)
assert Integer(1) > Integer(0)
assert Integer(1) >= Integer(1)

Định nghĩa 2: đa thừa kế

Mixin là một mẫu thiết kế trong đó một số phương thức của lớp cơ sở sử dụng một phương thức mà nó không định nghĩa và phương thức đó có nghĩa là được thực hiện bởi một lớp cơ sở khác , không phải do dẫn xuất như trong Định nghĩa 1.

Thuật ngữ lớp mixin dùng để chỉ các lớp cơ sở được dự định sử dụng trong mẫu thiết kế đó (TODO những lớp sử dụng phương thức này hay các lớp thực hiện nó?)

Thật không dễ để quyết định xem một lớp nhất định có phải là một mixin hay không: phương thức có thể được thực hiện trên lớp dẫn xuất, trong trường hợp đó chúng ta quay lại Định nghĩa 1. Bạn phải xem xét ý định của tác giả.

Mẫu này rất thú vị vì có thể kết hợp lại các chức năng với các lựa chọn khác nhau của các lớp cơ sở:

class HasMethod1(object):
    def method(self):
        return 1

class HasMethod2(object):
    def method(self):
        return 2

class UsesMethod10(object):
    def usesMethod(self):
        return self.method() + 10

class UsesMethod20(object):
    def usesMethod(self):
        return self.method() + 20

class C1_10(HasMethod1, UsesMethod10): pass
class C1_20(HasMethod1, UsesMethod20): pass
class C2_10(HasMethod2, UsesMethod10): pass
class C2_20(HasMethod2, UsesMethod20): pass

assert C1_10().usesMethod() == 11
assert C1_20().usesMethod() == 21
assert C2_10().usesMethod() == 12
assert C2_20().usesMethod() == 22

# Nothing prevents implementing the method
# on the base class like in Definition 1:

class C3_10(UsesMethod10):
    def method(self):
        return 3

assert C3_10().usesMethod() == 13

Sự xuất hiện của Python có thẩm quyền

Tại tài liệu chính thức cho các bộ sưu tập.abc tài liệu sử dụng rõ ràng thuật ngữ Phương thức Mixin .

Nó nói rằng nếu một lớp:

  • thực hiện __next__
  • kế thừa từ một lớp duy nhất Iterator

sau đó lớp học được một __iter__ phương thức mixin miễn phí.

Do đó, ít nhất là về điểm này của tài liệu, mixin không yêu cầu nhiều kế thừa và phù hợp với Định nghĩa 1.

Tất nhiên, tài liệu này có thể trái ngược nhau ở những điểm khác nhau và các thư viện Python quan trọng khác có thể đang sử dụng định nghĩa khác trong tài liệu của họ.

Trang này cũng sử dụng thuật ngữ Set mixin, trong đó gợi ý rõ ràng rằng các lớp thích SetIteratorcó thể được gọi là các lớp Mixin.

Trong các ngôn ngữ khác

  • Ruby: Rõ ràng không yêu cầu nhiều kế thừa cho mixin, như được đề cập trong các sách tham khảo chính như Lập trình Ruby và Ngôn ngữ lập trình Ruby

  • C ++: Một phương thức không được triển khai là một phương thức ảo thuần túy.

    Định nghĩa 1 trùng với định nghĩa của một lớp trừu tượng (một lớp có một phương thức ảo thuần túy). Lớp học đó không thể được khởi tạo.

    Định nghĩa 2 là có thể với thừa kế ảo: Nhiều kế thừa từ hai lớp dẫn xuất


37

Tôi nghĩ về chúng như một cách có kỷ luật trong việc sử dụng nhiều kế thừa - bởi vì cuối cùng, một mixin chỉ là một lớp python khác (có thể) tuân theo các quy ước về các lớp được gọi là mixin.

Sự hiểu biết của tôi về các quy ước chi phối một thứ mà bạn sẽ gọi là Mixin là Mixin:

  • thêm các phương thức nhưng không phải là các biến thể hiện (hằng số lớp là OK)
  • chỉ kế thừa từ object(trong Python)

Bằng cách đó, nó hạn chế sự phức tạp tiềm tàng của nhiều kế thừa và giúp dễ dàng theo dõi luồng chương trình của bạn bằng cách giới hạn nơi bạn phải tìm (so với nhiều kế thừa hoàn toàn). Chúng tương tự như các mô-đun ruby .

Nếu tôi muốn thêm các biến thể hiện (với tính linh hoạt cao hơn mức cho phép của kế thừa đơn) thì tôi có xu hướng đi theo thành phần.

Phải nói rằng, tôi đã thấy các lớp được gọi là XYZMixin có các biến thể hiện.


30

Mixins là một khái niệm trong Lập trình, trong đó lớp cung cấp các chức năng nhưng nó không có nghĩa là được sử dụng để khởi tạo. Mục đích chính của Mixins là cung cấp các chức năng độc lập và sẽ tốt nhất nếu bản thân các mixin không có sự kế thừa với các mixin khác và cũng tránh trạng thái. Trong các ngôn ngữ như Ruby, có một số hỗ trợ ngôn ngữ trực tiếp nhưng đối với Python thì không có. Tuy nhiên, bạn có thể sử dụng kế thừa nhiều lớp để thực thi chức năng được cung cấp trong Python.

Tôi đã xem video này http://www.youtube.com/watch?v=v_uKI2NOLEM để hiểu những điều cơ bản của mixins. Nó khá hữu ích cho người mới bắt đầu hiểu những điều cơ bản của mixin và cách chúng hoạt động cũng như những vấn đề bạn có thể gặp phải khi thực hiện chúng.

Wikipedia vẫn là tốt nhất: http://en.wikipedia.org/wiki/Mixin


29

Điều gì tách một mixin từ nhiều thừa kế? Có phải chỉ là một vấn đề ngữ nghĩa?

Một mixin là một hình thức hạn chế của nhiều kế thừa. Trong một số ngôn ngữ, cơ chế thêm mixin vào một lớp hơi khác (về cú pháp) so với kế thừa.

Trong ngữ cảnh của Python đặc biệt, mixin là lớp cha cung cấp chức năng cho các lớp con nhưng không có ý định tự khởi tạo.

Điều có thể khiến bạn phải nói, "đó chỉ là sự thừa kế nhiều lần, không thực sự là một mixin" là nếu lớp có thể bị nhầm lẫn với một mixin thực sự có thể được khởi tạo và sử dụng - vì vậy thực sự đó là một sự khác biệt về ngữ nghĩa và rất thực tế.

Ví dụ về đa kế thừa

Ví dụ này, từ tài liệu , là một OrderedCorer:

class OrderedCounter(Counter, OrderedDict):
     'Counter that remembers the order elements are first encountered'

     def __repr__(self):
         return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))

     def __reduce__(self):
         return self.__class__, (OrderedDict(self),)

Nó phân lớp cả CounterOrderedDicttừ collectionsmô-đun.

Cả hai CounterOrderedDictđược dự định để được khởi tạo và sử dụng riêng của họ. Tuy nhiên, bằng cách phân lớp cả hai, chúng ta có thể có một bộ đếm được đặt hàng và sử dụng lại mã trong mỗi đối tượng.

Đây là một cách mạnh mẽ để sử dụng lại mã, nhưng nó cũng có thể có vấn đề. Nếu hóa ra có một lỗi trong một trong các đối tượng, việc sửa nó mà không cần quan tâm có thể tạo ra lỗi trong lớp con.

Ví dụ về Mixin

Mixins thường được quảng bá là cách để sử dụng lại mã mà không gặp phải các vấn đề ghép nối tiềm năng mà nhiều kế thừa hợp tác, như OrderedCorer, có thể có. Khi bạn sử dụng mixins, bạn sử dụng chức năng không được kết hợp chặt chẽ với dữ liệu.

Không giống như ví dụ trên, một mixin không có ý định sử dụng riêng. Nó cung cấp chức năng mới hoặc khác nhau.

Ví dụ, thư viện tiêu chuẩn có một vài mixin trong socketserverthư viện .

Các phiên bản phân luồng và phân luồng của từng loại máy chủ có thể được tạo bằng các lớp trộn này. Chẳng hạn, ThreadingUDPServer được tạo như sau:

class ThreadingUDPServer(ThreadingMixIn, UDPServer):
    pass

Lớp trộn vào trước, vì nó ghi đè một phương thức được định nghĩa trong UDPServer. Đặt các thuộc tính khác nhau cũng thay đổi hành vi của cơ chế máy chủ cơ bản.

Trong trường hợp này, các phương thức mixin ghi đè các phương thức trong UDPServerđịnh nghĩa đối tượng để cho phép đồng thời.

Phương thức ghi đè xuất hiện process_requestvà nó cũng cung cấp một phương thức khác , process_request_thread. Đây là từ mã nguồn :

class ThreadingMixIn:
        """Mix-in class to handle each request in a new thread."""

        # Decides how threads will act upon termination of the
        # main process
        daemon_threads = False

        def process_request_thread(self, request, client_address):
            """Same as in BaseServer but as a thread.
            In addition, exception handling is done here.
            """
            try:
                self.finish_request(request, client_address)
            except Exception:
                self.handle_error(request, client_address)
            finally:
                self.shutdown_request(request)

        def process_request(self, request, client_address):
            """Start a new thread to process the request."""
            t = threading.Thread(target = self.process_request_thread,
                                 args = (request, client_address))
            t.daemon = self.daemon_threads
            t.start()

Một ví dụ có sẵn

Đây là một mixin chủ yếu dành cho mục đích trình diễn - hầu hết các đối tượng sẽ phát triển vượt ra ngoài tính hữu ích của repr này:

class SimpleInitReprMixin(object):
    """mixin, don't instantiate - useful for classes instantiable
    by keyword arguments to their __init__ method.
    """
    __slots__ = () # allow subclasses to use __slots__ to prevent __dict__
    def __repr__(self):
        kwarg_strings = []
        d = getattr(self, '__dict__', None)
        if d is not None:
            for k, v in d.items():
                kwarg_strings.append('{k}={v}'.format(k=k, v=repr(v)))
        slots = getattr(self, '__slots__', None)
        if slots is not None:
            for k in slots:
                v = getattr(self, k, None)
                kwarg_strings.append('{k}={v}'.format(k=k, v=repr(v)))
        return '{name}({kwargs})'.format(
          name=type(self).__name__,
          kwargs=', '.join(kwarg_strings)
          )

và cách sử dụng sẽ là:

class Foo(SimpleInitReprMixin): # add other mixins and/or extend another class here
    __slots__ = 'foo',
    def __init__(self, foo=None):
        self.foo = foo
        super(Foo, self).__init__()

Và cách sử dụng:

>>> f1 = Foo('bar')
>>> f2 = Foo()
>>> f1
Foo(foo='bar')
>>> f2
Foo(foo=None)

11

Tôi nghĩ rằng đã có một số giải thích tốt ở đây nhưng tôi muốn cung cấp một quan điểm khác.

Trong Scala, bạn có thể thực hiện mixin như đã được mô tả ở đây nhưng điều rất thú vị là các mixin thực sự được 'hợp nhất' với nhau để tạo ra một loại lớp mới để kế thừa. Về bản chất, bạn không kế thừa từ nhiều lớp / mixin, mà thay vào đó, tạo ra một loại lớp mới với tất cả các thuộc tính của mixin để kế thừa từ đó. Điều này có ý nghĩa vì Scala dựa trên JVM, nơi đa kế thừa hiện không được hỗ trợ (kể từ Java 8). Nhân tiện, loại lớp mixin này là một loại đặc biệt gọi là Trait in Scala.

Nó được gợi ý theo cách định nghĩa một lớp: lớp NewClass mở rộng FirstMixin với SecondMixin với ThirdMixin ...

Tôi không chắc liệu trình thông dịch CPython có làm như vậy không (thành phần lớp mixin) nhưng tôi sẽ không ngạc nhiên. Ngoài ra, đến từ nền C ++, tôi sẽ không gọi ABC hoặc 'giao diện' tương đương với mixin - đó là một khái niệm tương tự nhưng khác nhau trong sử dụng và triển khai.


9

Tôi khuyên bạn nên chống lại các hỗn hợp trong mã Python mới, nếu bạn có thể tìm thấy bất kỳ cách nào khác xung quanh nó (chẳng hạn như thành phần thay vì kế thừa, hoặc chỉ phương pháp vá khỉ vào các lớp của riêng bạn) không còn nhiều nữa cố gắng.

Trong các lớp kiểu cũ, bạn có thể sử dụng các hỗn hợp như một cách lấy một vài phương thức từ một lớp khác. Nhưng trong thế giới kiểu mới, mọi thứ, thậm chí cả hỗn hợp, đều thừa hưởng từ object. Điều đó có nghĩa là bất kỳ việc sử dụng nhiều kế thừa đều tự nhiên đưa ra các vấn đề MRO .

Có nhiều cách để làm cho MRO đa kế thừa hoạt động trong Python, đáng chú ý nhất là hàm super (), nhưng điều đó có nghĩa là bạn phải thực hiện phân cấp toàn bộ lớp bằng cách sử dụng super () và khó hiểu hơn về luồng điều khiển.


3
Do Phiên bản 2.3 Python sử dụng "Độ phân giải phương thức C3" được giải thích trong Thứ tự phân giải phương thức Python 2.3 hoặc Thứ tự phân giải phương thức .
webwurst

11
Cá nhân, tôi sẽ dùng mixin trong việc vá khỉ trong hầu hết các trường hợp; lý do dễ dàng hơn để làm theo và thông qua mã.
tdammers

5
Bị hạ bệ. Mặc dù câu trả lời của bạn thể hiện ý kiến ​​hợp lệ về phong cách phát triển, bạn không thực sự giải quyết câu hỏi thực tế.
Ryan B. Lynch

8

Có lẽ một vài ví dụ sẽ giúp.

Nếu bạn đang xây dựng một lớp và bạn muốn nó hoạt động như một từ điển, bạn có thể định nghĩa tất cả các __ __phương thức khác nhau cần thiết. Nhưng đó là một chút đau đớn. Thay thế, bạn chỉ có thể xác định một số ít và kế thừa (ngoài bất kỳ thừa kế nào khác) từ UserDict.DictMixin(chuyển sangcollections.DictMixin trong py3k). Điều này sẽ có tác dụng tự động xác định tất cả phần còn lại của api từ điển.

Một ví dụ thứ hai: bộ công cụ GUI wxPython cho phép bạn tạo các điều khiển danh sách với nhiều cột (như giả sử, hiển thị tệp trong Windows Explorer). Theo mặc định, các danh sách này là khá cơ bản. Bạn có thể thêm chức năng bổ sung, chẳng hạn như khả năng sắp xếp danh sách theo một cột cụ thể bằng cách nhấp vào tiêu đề cột, bằng cách kế thừa từ ListCtrl và thêm các mixin thích hợp.


8

Đây không phải là một ví dụ Python nhưng trong ngôn ngữ lập trình D , thuật ngữ mixinnày được sử dụng để chỉ một cấu trúc được sử dụng theo cùng một cách; thêm một đống thứ vào một lớp

Trong D (bằng cách này không làm MI), điều này được thực hiện bằng cách chèn một mẫu (nghĩ rằng các macro an toàn và nhận biết cú pháp và bạn sẽ đóng) vào một phạm vi. Điều này cho phép một dòng mã duy nhất trong một lớp, cấu trúc, chức năng, mô-đun hoặc bất cứ điều gì để mở rộng đến bất kỳ số lượng khai báo nào.


2
Mixin là một thuật ngữ chung, được sử dụng trong D, Ruby, v.v. Theo Wikipedia, chúng bắt nguồn từ các hệ thống lisp trường học cũ và được ghi nhận lần đầu tiên vào năm 1983: en.wikipedia.org/wiki/ Lỗi
Lee B

7

OP đã đề cập rằng anh ấy / cô ấy chưa bao giờ nghe nói về mixin trong C ++, có lẽ đó là vì chúng được gọi là Mẫu mẫu định kỳ tò mò (CRTP) trong C ++. Ngoài ra, @Ciro Santilli đã đề cập rằng mixin được triển khai thông qua lớp cơ sở trừu tượng trong C ++. Mặc dù lớp cơ sở trừu tượng có thể được sử dụng để triển khai mixin, nhưng nó quá mức vì chức năng của chức năng ảo trong thời gian chạy có thể đạt được bằng cách sử dụng khuôn mẫu trong thời gian biên dịch mà không cần phải tìm kiếm bảng ảo trong thời gian chạy.

Mẫu CRTP được mô tả chi tiết tại đây

Tôi đã chuyển đổi ví dụ python trong câu trả lời của @Ciro Santilli thành C ++ bằng lớp mẫu bên dưới:

    #include <iostream>
    #include <assert.h>

    template <class T>
    class ComparableMixin {
    public:
        bool operator !=(ComparableMixin &other) {
            return ~(*static_cast<T*>(this) == static_cast<T&>(other));
        }
        bool operator <(ComparableMixin &other) {
            return ((*(this) != other) && (*static_cast<T*>(this) <= static_cast<T&>(other)));
        }
        bool operator >(ComparableMixin &other) {
            return ~(*static_cast<T*>(this) <= static_cast<T&>(other));
        }
        bool operator >=(ComparableMixin &other) {
            return ((*static_cast<T*>(this) == static_cast<T&>(other)) || (*(this) > other));
        }
        protected:
            ComparableMixin() {}
    };

    class Integer: public ComparableMixin<Integer> {
    public:
     Integer(int i) {
         this->i = i;
     }
     int i;
     bool operator <=(Integer &other) {
         return (this->i <= other.i);
     }
     bool operator ==(Integer &other) {
         return (this->i == other.i);
     }
    };

int main() {

    Integer i(0) ;
    Integer j(1) ;
    //ComparableMixin<Integer> c; // this will cause compilation error because constructor is protected.
    assert (i < j );
    assert (i != j);
    assert (j >  i);
    assert (j >= i);

    return 0;
}

EDIT: Đã thêm hàm tạo được bảo vệ trong so sánhMixin để nó chỉ có thể được kế thừa và không được khởi tạo. Đã cập nhật ví dụ để hiển thị cách xây dựng được bảo vệ sẽ gây ra lỗi biên dịch khi một đối tượng của so sánhMixin được tạo.


Mixins và CRTP không phải là điều tương tự trong C ++.
ashrasmun

6

Có lẽ một ví dụ từ ruby ​​có thể giúp:

Bạn có thể bao gồm mixin Comparablevà xác định một hàm "<=>(other)", mixin cung cấp tất cả các hàm đó:

<(other)
>(other)
==(other)
<=(other)
>=(other)
between?(other)

Nó thực hiện điều này bằng cách gọi <=>(other)và trả lại kết quả đúng.

"instance <=> other"trả về 0 nếu cả hai đối tượng bằng nhau, nhỏ hơn 0 nếu instancelớn hơn othervà lớn hơn 0 nếu otherlớn hơn.


Đây là một bài đăng cung cấp một mixin tương tự cho Python. Mặc dù đề xuất được xác định __lt__là cơ sở thay vì __cmp__, nhưng sau đó thực sự không được khuyến khích và không khuyến khích sử dụng. Đối với tôi có vẻ đơn giản hơn khi sử dụng mixin đó thay vì trang trí khá phức tạp (một phần của funcools ) - mặc dù điều này có thể có thể phản ứng linh hoạt hơn khi so sánh được cung cấp ...
Tobias Kienzler

6

mixin cung cấp một cách để thêm chức năng trong một lớp, tức là bạn có thể tương tác với các phương thức được xác định trong một mô-đun bằng cách bao gồm mô-đun bên trong lớp mong muốn. Mặc dù ruby ​​không hỗ trợ nhiều kế thừa nhưng cung cấp mixin như một cách thay thế để đạt được điều đó.

đây là một ví dụ giải thích cách đạt được nhiều sự kế thừa bằng cách sử dụng mixin.

module A    # you create a module
    def a1  # lets have a method 'a1' in it
    end
    def a2  # Another method 'a2'
    end
end

module B    # let's say we have another module
    def b1  # A method 'b1'
    end
    def b2  #another method b2
    end
end

class Sample    # we create a class 'Sample'
    include A   # including module 'A' in the class 'Sample' (mixin)
    include B   # including module B as well

    def S1      #class 'Sample' contains a method 's1'
    end
end

samp = Sample.new    # creating an instance object 'samp'

# we can access methods from module A and B in our class(power of mixin)

samp.a1     # accessing method 'a1' from module A
samp.a2     # accessing method 'a2' from module A
samp.b1     # accessing method 'b1' from module B
samp.b2     # accessing method 'a2' from module B
samp.s1     # accessing method 's1' inside the class Sample

4
Sự khác biệt giữa điều này và đa thừa kế nói chung là gì?
Ciro Santilli 冠状 病毒 审查 事件

Sự khác biệt là bạn không thể tạo phiên bản từ các mô-đun, nhưng nếu không có sự khác biệt giữa các lớp chung và mô-đun thì mixin không phải là điều rõ ràng và khó hiểu đâu là lớp chung và đâu là mixin
ka8725

Vậy trong Ruby mixins chỉ là các lớp không thể khởi tạo mà phải được sử dụng cho nhiều kế thừa?
Trilarion

6

Tôi chỉ sử dụng một mixin python để thực hiện thử nghiệm đơn vị cho milters python. Thông thường, một người vắt sữa nói chuyện với một MTA, làm cho việc kiểm tra đơn vị trở nên khó khăn. Mixin thử nghiệm ghi đè các phương thức nói chuyện với MTA và tạo ra một môi trường mô phỏng được điều khiển bởi các trường hợp thử nghiệm thay thế.

Vì vậy, bạn có một ứng dụng vắt sữa chưa sửa đổi, như spfmilter và mixin TestBase, như thế này:

class TestMilter(TestBase,spfmilter.spfMilter):
  def __init__(self):
    TestBase.__init__(self)
    spfmilter.config = spfmilter.Config()
    spfmilter.config.access_file = 'test/access.db'
    spfmilter.spfMilter.__init__(self)

Sau đó, sử dụng TestMilter trong các trường hợp thử nghiệm cho ứng dụng vắt sữa:

def testPass(self):
  milter = TestMilter()
  rc = milter.connect('mail.example.com',ip='192.0.2.1')
  self.assertEqual(rc,Milter.CONTINUE)
  rc = milter.feedMsg('test1',sender='good@example.com')
  self.assertEqual(rc,Milter.CONTINUE)
  milter.close()

http://pymilter.cvs.sourceforge.net/viewvc/pymilter/pymilter/Milter/test Shikrevision=1.6&view=markup


4

Tôi nghĩ rằng các phản hồi trước đó đã xác định rất rõ MixIns là gì . Tuy nhiên, để hiểu rõ hơn về chúng, có thể hữu ích khi so sánh MixIns với các lớpgiao diện trừu tượng từ góc độ mã / triển khai:

1. Lớp trừu tượng

  • Lớp cần chứa một hoặc nhiều phương thức trừu tượng

  • Lớp trừu tượng có thể chứa trạng thái (biến thể hiện) và phương thức không trừu tượng

2. Giao diện

  • Giao diện chứa phương pháp trừu tượng chỉ (không có phương pháp phi trừu tượng và không có trạng thái nội bộ)

3. MixIns

  • MixIns (như Giao diện) không chứa trạng thái bên trong (biến thể hiện)
  • MixIns chứa một hoặc nhiều phương thức không trừu tượng (chúng có thể chứa các phương thức không trừu tượng không giống như giao diện)

Trong ví dụ Python, đây chỉ là các quy ước, bởi vì tất cả các điều trên được định nghĩa là classes. Tuy nhiên, đặc điểm chung của cả Lớp trừu tượng, Giao diệnMixIn là chúng không nên tự tồn tại, tức là không nên khởi tạo.


3

Tôi đọc rằng bạn có ac # nền. Vì vậy, một điểm khởi đầu tốt có thể là một triển khai mixin cho .NET.

Bạn có thể muốn kiểm tra dự án codeplex tại http://remix.codeplex.com/

Xem liên kết Hội nghị chuyên đề lang.net để có cái nhìn tổng quan. Vẫn còn nhiều hơn nữa về tài liệu trên trang codeplex.

liên quan đến Stefan

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.