Eigenschaften wurden kürzlich in Mojo eingeführt, also dachte ich, ich probiere sie mal aus. Derzeit umfassen die integrierten Merkmale , , , , , und (das Suffix „-able“ scheint in diesen Namenskonventionen enthalten zu sein!). Merkmale funktionieren auf Strukturen. Um ein Merkmal zu implementieren, fügen Sie einfach eine Methode zu einer Struktur hinzu, die dem Merkmal entspricht. Übergeben Sie dann den Merkmalsnamen als Parameter: CollectionElement Copyable Destructable Intable Movable Sized Stringable @value struct Duck(Quackable): fn quack(self): print("Quack!") Der Dekorator in Mojo fügt die Lebenszyklusmethoden wie , und in die Struktur ein, was unser Leben ein wenig vereinfacht, da wir sie nicht selbst hinzufügen müssen. @value __init__() __copyinit__() __moveinit__() Merkmale in Mojo unterstützen noch keine Standardimplementierungen für Methoden, daher das im Hauptteil der -Methode oben. Sie können auch verwenden, was den gleichen Effekt wie in Python hat. ... Quackable pass Mojo-Eigenschaften vs. Go-Schnittstellen Trotz des anderen Namens erinnert mich der grundlegende Ansatz für Merkmale in Mojo an Go-Schnittstellen. In Go würden Sie eine Struktur definieren und eine Schnittstelle wie folgt implementieren: Duck Quackable type Quackable interface { quack() } Und um eine Struktur zu erstellen, die diese Schnittstelle erfüllt: type Duck struct {} func (d Duck) quack() { fmt.Println("Quack!") } Vergleichen Sie es mit einer Mojo-Implementierung: trait Quackable: fn quack(self): ... @value struct Duck(Quackable): fn quack(self): print("Quack!") Ich denke, die Mojo-Version ist noch prägnanter, da sie die Methodendefinition innerhalb des Strukturtyps verschachtelt. Zusätzlich zur Arbeit an Strukturen erfordern Merkmale in Mojo kein Schlüsselwort , genau wie in Go. quack() Duck implements Interessanterweise wird in den Mojo-Dokumenten darauf hingewiesen, dass Standardimplementierungen nicht zulässig sind, was bedeutet, dass sie möglicherweise in Zukunft zulässig sind. Das bedeutet, dass Mojo möglicherweise einen anderen Ansatz verfolgt als Go, der sich auf Strukturen konzentriert, die eine Schnittstelle , und nicht auf deren . noch erfüllen Implementierung Standardmethodenimplementierungen sind in Go nicht erforderlich, und ein ähnlicher Effekt wird mit der Einbettung erzielt, da die Schnittstellen-/Merkmalsimplementierung lediglich ein Konzept ist, das in Go nicht existiert. In Mojo könnte es anders sein. So verwenden Sie Mojo-Eigenschaften Der Wert von Merkmalen und Schnittstellen besteht darin, Code wiederverwendbar zu machen. In Mojo können Sie beispielsweise Funktionen schreiben, die Merkmalstypen akzeptieren ... fn make_it_quack[T: Quackable](could_be_duck: T): could_be_duck.quack() Und dann übergeben Sie verschiedene Strukturen, die das gleiche Merkmal als Eingabe implementieren – alles wird einfach funktionieren! Hier ist zum Beispiel eine Struktur, die implementiert: Goose Quackable @value struct Goose(Quackable): fn quack(self): print("Honk!") Und hier, um sowohl auf der als auch auf der aufzurufen (denken Sie daran, dass Sie die in Mojo als Einstiegspunkt in Ihr Programm benötigen): make_it_quack Goose Duck main def main(): make_it_quack(Duck()) make_it_quack(Goose()) Die Ausgabe davon wird sein Quack! Honk! Merkmalsfehler Wenn ich versuche, etwas an die Funktion zu übergeben, das das Merkmal nicht implementiert, sagen wir , lässt sich das Programm nicht kompilieren: make_it_quack Quackable StealthCow @value struct StealthCow(): pass make_it_quack(StealthCow()) Die folgende Fehlermeldung hätte aussagekräftiger sein können; Vielleicht verbessert das Mojo-Team es? error: invalid call to 'make_it_quack': callee expects 1 input parameter, but 0 were specified Das Gleiche gilt, wenn ich die Methode aus entferne. Hier erhalten wir einen schönen, beschreibenden Fehler: quack Goose struct 'Goose' does not implement all requirements for 'Quackable' required function 'quack' is not implemented Der Vorteil dieser Art der statischen Typprüfung gegenüber dem reinen Python-Ansatz besteht darin, dass wir Fehler leicht erkennen können und vermeiden, dass Fehler in die Produktion gelangen, da der Code einfach nicht kompiliert werden kann. Nachlass Merkmale in Mojo unterstützen bereits die Vererbung, daher kann unser Merkmal „Quackable“ ein Merkmal „Audible“ wie folgt erweitern: trait Audible: fn make_sound(self): ... trait Quackable(Audible): fn quack(self): ... Das bedeutet, dass eine Struktur sowohl als auch implementieren muss, um dem Merkmal zu entsprechen. Duck quack make_sound Quackable Dies ähnelt dem Konzept der „Schnittstelleneinbettung“ in Go. Um eine neue Schnittstelle zu erstellen, die von anderen Schnittstellen erbt, würden Sie übergeordnete Schnittstellen wie folgt einbetten: type Quackable interface { Audible // includes methods of Audible in Quackable's method set } Statische Methoden Traits akzeptieren auch statische Methoden, die funktionieren, ohne eine Instanz einer Struktur zu erstellen: 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() Sie rufen eine statische Methode wie folgt auf: def main(): make_it_quack(Duck()) make_it_quack(Goose()) Duck.swim() Was wird ausgeben: Quack! Honk! Swimming Beachten Sie, dass beim letzten Methodenaufruf keine Duck-Instanz erstellt wird. So funktionieren statische Methoden in Python und weichen etwas von der objektorientierten Programmierung ab. Mojo baut auf dieser Python-Funktionalität auf. Einschränkungen von Mojo-Merkmalen im Vergleich zu Go-Schnittstellen Interessanterweise funktioniert der Go-Trick mit einem leeren , der die Übergabe eines beliebigen Typs ermöglicht und vor der Einführung von Go Generics in der Go-Community beliebt war, nicht mit einer Mojo-typisierten Funktion . interface{} fn Ihre Struktur muss mindestens eine der Lebenszyklusmethoden wie oder implementieren, wodurch sie in diesem Fall oder entspricht und mit Funktionen verwendet werden kann, die Merkmalstypen akzeptieren. __len__ __str__ Sized Stringable Dies ist eigentlich keine wirkliche Einschränkung und macht in Mojo sehr viel Sinn, da Sie bei Mojo, wenn Sie dynamisches Tippen benötigen, einfach auf die flexiblere Funktion zurückgreifen und dann die übliche Python-Magie anwenden können, um mit Unbekanntem zu arbeiten Typen. def Die strengeren Mojo- Funktionen funktionieren auch mit generischen Strukturen unter Verwendung von Typen wie ; Lesen Sie mehr darüber. fn DynamicVector hier Eine weitere Einschränkung, die ich sehe, hat eher mit Strukturen und ihren Methoden als mit Merkmalen zu tun und stellt ein potenzielles Hindernis für die Implementierung von Clean Architecture bzw. die Aufteilung von Code in verschiedene Teile dar. Betrachten Sie eines der vorherigen Beispiele mit einer Go vs. Mojo-Strukturmethodendefinition: type Duck struct {} func (d Duck) quack() { fmt.Println("Quack!") } @value struct Duck(Quackable): fn quack(self): print("Quack!") Das Mojo-Beispiel folgt einer eher Python-ähnlichen Syntax und verschachtelt die Methodendefinition direkt in der Struktur, während die Go-Version es ermöglicht, sie von der Struktur selbst zu trennen. Wenn ich in dieser Version eine sehr lange Struktur mit vielen Typen und Methoden habe, ist sie etwas schwieriger zu lesen. Es handelt sich jedoch nicht um einen entscheidenden Unterschied, sondern lediglich um etwas, dessen man sich bewusst sein sollte. Mojo-Eigenschaften sind bereits ziemlich nützlich, obwohl die Sprache noch in den Kinderschuhen steckt. Während es bei der Go-Philosophie vor allem um Einfachheit geht und versucht, die Funktionen auf ein Minimum zu beschränken, ist es wahrscheinlich, dass den Mojo-Eigenschaften in Zukunft weitere Funktionen hinzugefügt werden, wodurch sie noch leistungsfähiger werden und unzählige verschiedene Anwendungsfälle möglich werden.