Gần đây, các đặc tính đã được giới thiệu trong Mojo nên tôi nghĩ mình sẽ thử chúng. Hiện tại, các đặc điểm tích hợp bao gồm , , , , , và (có vẻ như hậu tố "-able" là một thứ trong các quy ước đặt tên này!). Đặc điểm hoạt động trên cấu trúc. Để triển khai một đặc điểm, bạn chỉ cần thêm một phương thức vào cấu trúc phù hợp với đặc điểm đó; sau đó chuyển tên đặc điểm làm tham số: CollectionElement Copyable Destructable Intable Movable Sized Stringable @value struct Duck(Quackable): fn quack(self): print("Quack!") Trình trang trí trong Mojo chèn các phương thức vòng đời như , và vào cấu trúc, đơn giản hóa cuộc sống của chúng ta một chút vì chúng ta không phải tự thêm chúng. @value __init__() __copyinit__() __moveinit__() Các đặc điểm trong Mojo chưa hỗ trợ triển khai mặc định cho các phương thức, do đó, nằm trong phần nội dung của phương thức ở trên. Bạn cũng có thể sử dụng , điều này sẽ có tác dụng tương tự như trong Python. ... Quackable pass Đặc điểm Mojo so với giao diện cờ vây Mặc dù có tên khác nhưng cách tiếp cận cơ bản về các đặc điểm trong Mojo khiến tôi nhớ đến giao diện Go. Trong Go, bạn sẽ xác định cấu trúc và triển khai giao diện như thế này: Duck Quackable type Quackable interface { quack() } Và để tạo một cấu trúc thỏa mãn giao diện này: type Duck struct {} func (d Duck) quack() { fmt.Println("Quack!") } So sánh nó với việc triển khai Mojo: trait Quackable: fn quack(self): ... @value struct Duck(Quackable): fn quack(self): print("Quack!") Tôi nghĩ phiên bản Mojo thậm chí còn ngắn gọn hơn, lồng định nghĩa phương thức bên trong kiểu cấu trúc . Ngoài việc làm việc trên các cấu trúc, các đặc điểm trong Mojo không yêu cầu từ khóa , giống như trong Go. quack() Duck implements Điều thú vị là, tài liệu Mojo đã nêu rõ rằng việc triển khai mặc định được phép, nghĩa là chúng có thể được cho phép trong tương lai. Điều này có nghĩa là Mojo có thể theo đuổi một cách tiếp cận khác với Go, tập trung vào các cấu trúc giao diện chứ không phải nó. chưa thỏa mãn triển khai Việc triển khai phương thức mặc định là không cần thiết trong Go và đạt được hiệu quả tương tự khi nhúng, vì việc triển khai giao diện/đặc điểm đơn giản là một khái niệm không tồn tại trong Go. Mọi thứ có thể khác ở Mojo. Cách sử dụng đặc điểm Mojo Giá trị của các đặc điểm và giao diện là làm cho mã có thể tái sử dụng được. Ví dụ: trong Mojo, bạn có thể viết các hàm chấp nhận các loại đặc điểm… fn make_it_quack[T: Quackable](could_be_duck: T): could_be_duck.quack() Và sau đó chuyển các cấu trúc khác nhau triển khai đặc điểm giống như đầu vào - mọi thứ sẽ hoạt động bình thường! Ví dụ: đây là cấu trúc triển khai : Goose Quackable @value struct Goose(Quackable): fn quack(self): print("Honk!") Và ở đây, để gọi trên cả và (hãy nhớ rằng, bạn cần hàm trong Mojo làm điểm vào chương trình của mình): make_it_quack Goose Duck main def main(): make_it_quack(Duck()) make_it_quack(Goose()) Đầu ra của điều này sẽ là Quack! Honk! Lỗi đặc điểm Nếu tôi cố gắng chuyển thứ gì đó không triển khai đặc điểm vào hàm , giả sử , chương trình sẽ không biên dịch: Quackable make_it_quack StealthCow @value struct StealthCow(): pass make_it_quack(StealthCow()) Thông báo lỗi bên dưới có thể mang tính mô tả nhiều hơn; có lẽ nhóm Mojo sẽ cải thiện nó? error: invalid call to 'make_it_quack': callee expects 1 input parameter, but 0 were specified Tương tự nếu tôi loại bỏ phương thức khỏi ; ở đây, chúng tôi nhận được một lỗi mô tả hay: quack Goose struct 'Goose' does not implement all requirements for 'Quackable' required function 'quack' is not implemented Ưu điểm của loại kiểm tra kiểu tĩnh này so với phương pháp Python thuần túy là chúng ta có thể dễ dàng phát hiện lỗi và tránh chuyển bất kỳ lỗi nào sang quá trình sản xuất vì mã sẽ không được biên dịch. Di sản Các đặc điểm trong Mojo đã hỗ trợ tính kế thừa, vì vậy đặc điểm `Quackable` của chúng tôi có thể mở rộng đặc điểm `Audible` như sau: trait Audible: fn make_sound(self): ... trait Quackable(Audible): fn quack(self): ... Điều này có nghĩa là cấu trúc sẽ phải triển khai cả và để phù hợp với đặc điểm . Duck quack make_sound Quackable Điều này tương tự như khái niệm "Nhúng giao diện" trong Go, để tạo giao diện mới kế thừa từ các giao diện khác, bạn sẽ nhúng giao diện chính như thế này: type Quackable interface { Audible // includes methods of Audible in Quackable's method set } Phương thức tĩnh Các đặc điểm cũng chấp nhận các phương thức tĩnh hoạt động mà không cần tạo phiên bản của cấu trúc: trait Swims: @staticmethod fn swim(): ... @value struct Duck(Quackable, Swims): fn quack(self): print("Quack!") @staticmethod fn swim(): print("Swimming") fn make_it_swim[T: Swims](): T.swim() Bạn gọi một phương thức tĩnh như thế này: def main(): make_it_quack(Duck()) make_it_quack(Goose()) Duck.swim() Cái nào sẽ xuất ra: Quack! Honk! Swimming Lưu ý rằng trong lệnh gọi phương thức cuối cùng, phiên bản Duck không được tạo. Đây là cách các phương thức tĩnh trong Python hoạt động, khác xa với lập trình hướng đối tượng. Mojo được xây dựng dựa trên chức năng Python này. Hạn chế của đặc điểm Mojo so với giao diện cờ vây Điều thú vị là thủ thuật cờ vây có cho phép vượt qua bất kỳ loại nào và phổ biến với cộng đồng cờ vây trước khi Go Generics được giới thiệu, sẽ không hoạt động với hàm được gõ Mojo . interface{} fn Cấu trúc của bạn phải triển khai ít nhất một trong các phương thức vòng đời như hoặc , trong trường hợp này, sẽ làm cho nó tuân theo hoặc , để được sử dụng với các hàm chấp nhận các loại đặc điểm. __len__ __str__ Sized Stringable Đây thực sự không phải là một hạn chế thực sự và rất có ý nghĩa trong Mojo, vì với Mojo, nếu bạn cần gõ động, bạn có thể quay lại hàm linh hoạt hơn, sau đó áp dụng phép thuật Python thông thường để làm việc với ẩn số. các loại. def Các hàm Mojo nghiêm ngặt hơn cũng hoạt động trên các cấu trúc chung sử dụng các kiểu như ; đọc thêm về phần đó . fn DynamicVector ở đay Một hạn chế khác mà tôi thấy liên quan đến các cấu trúc và phương thức của chúng hơn là các đặc điểm và là một trở ngại tiềm ẩn trong việc triển khai Kiến trúc sạch/tách mã thành các phần khác nhau. Hãy xem xét một trong những ví dụ trước đây với định nghĩa phương thức cấu trúc Go và Mojo: type Duck struct {} func (d Duck) quack() { fmt.Println("Quack!") } @value struct Duck(Quackable): fn quack(self): print("Quack!") Ví dụ Mojo, theo cú pháp giống Python hơn, lồng định nghĩa phương thức trực tiếp bên trong cấu trúc, trong khi phiên bản Go cho phép nó tách nó khỏi chính cấu trúc đó. Trong phiên bản này, nếu tôi có một cấu trúc rất dài với nhiều loại và phương thức thì sẽ khó đọc hơn một chút. Tuy nhiên, đó không phải là sự khác biệt quan trọng mà chỉ là điều cần lưu ý. Các đặc điểm Mojo đã khá hữu ích, mặc dù ngôn ngữ này mới ở giai đoạn đầu. Mặc dù triết lý của Go là sự đơn giản và cố gắng giữ các tính năng ở mức tối thiểu, nhưng có khả năng chúng ta sẽ thấy nhiều chức năng hơn được thêm vào các đặc điểm của Mojo trong tương lai, khiến chúng thậm chí còn mạnh mẽ hơn và cho phép thực hiện nhiều trường hợp sử dụng khác nhau.