2010-04-20 7 views
10

Python a l'idée de métaclasses qui, si je comprends bien, vous permettent de modifier un objet d'une classe au moment de la construction. Vous ne modifiez pas la classe, mais plutôt l'objet à créer puis initialisé.Quel est l'analogue de Ruby pour les Metaclasses Python?

Python (au moins à partir de 3.0 je crois) a aussi l'idée de décorateurs de classe. Encore une fois, si je comprends bien, les décorateurs de classe permettent la modification de la définition de classe au moment où elle est déclarée. Maintenant, je crois qu'il existe une fonctionnalité ou des fonctions équivalentes au décorateur de classe dans Ruby, mais je ne suis pas au courant de quelque chose d'équivalent aux métaclasses. Je suis sûr que vous pouvez facilement pomper n'importe quel objet Ruby à travers certaines fonctions et faire ce que vous voulez, mais y a-t-il une fonctionnalité dans le langage qui le configure comme le font les métaclasses?

Encore une fois, est-ce que Ruby a quelque chose de similaire aux métaclasses de Python?

Modifier J'étais sur les métaclasses pour Python. Une métaclasse et un décorateur de classe font des choses très similaires. Ils modifient tous deux la classe quand elle est définie mais de manières différentes. Espérons qu'un gourou Python viendra et expliquera mieux sur ces fonctionnalités en Python.

Mais une classe ou le parent d'une classe peut implémenter une fonction __new__(cls[,..]) qui personnalise la construction de l'objet avant son initialisation avec __init__(self[,..]).

Modifier Cette question est principalement pour la discussion et l'apprentissage de la façon dont les deux langues se comparent dans ces caractéristiques. Je connais Python mais pas Ruby et j'étais curieux. J'espère que quelqu'un d'autre qui a la même question sur les deux langues trouvera ce message utile et instructif.

+0

Hmm ... je peux être manque encore quelque chose, mais après avoir lu quelques liens, il commence à sonner comme Python La métaclasse et la métaclasse de Ruby sont différentes. J'espère avoir l'occasion de faire plus de recherches et de poster sur ce que j'ai appris. C'est bien sûr à moins que quelqu'un le sache. Liens: [Katz sur Métaclasses] (http://yehudakatz.com/2009/11/15/metaprogramming-in-ruby-its-all-about-the-self/), [et Ruby Internes métaclasse ] (http://www.klankboomklang.com/2007/10/05/the-metaclass/) –

Répondre

23

Ruby n'a pas de métaclasses. Il y a quelques constructions dans Ruby que certaines personnes appellent parfois à tort des métaclasses mais elles ne le sont pas (ce qui est une source de confusion). Cependant, il existe de nombreuses façons d'obtenir les mêmes résultats dans Ruby que dans les métaclasses. Mais sans nous dire exactement ce que vous voulez faire, il est impossible de dire quels pourraient être ces mécanismes.

En bref:

  • Ruby n'a pas métaclasses
  • Ruby ne dispose pas d'une construction qui correspond aux métaclasses Python
  • Tout ce que Python peut faire avec métaclasses peut également être fait en Ruby
  • Mais il n'y a pas unique construction, vous utiliserez différentes constructions en fonction de ce que vous voulez faire exactement
  • Une de ces constructions a probablement aussi d'autres caractéristiques qui ne correspondent pas aux métaclasses (bien qu'elles correspondent probablement à quelque chose sinon en Python)
  • Alors que vous pouvez faire n'importe quoi dans Ruby que vous pouvez faire avec des métaclasses en Python, il ne pourrait pas nécessairement être simple
  • Bien souvent, il y aura une solution plus Rubyish que est élégante
  • Last but not least: pendant que vous pouvez faire quelque chose en Ruby que vous pouvez faire avec métaclasses en Python, le faire pourrait pas nécessairement être The Ruby Way

Alors, que sont metaclasses exactement? Eh bien, ce sont des classes de cours. Alors, prenons un peu de recul: quelles sont classes exactement?

Classes & hellip;

  • sont des usines pour les objets
  • définissent le comportement des objets
  • DEFINE un niveau métaphysique ce que cela signifie d'être une instance de la classe

Par exemple, la classe Array produit tableau objets, définit le comportement des tableaux et définit ce que signifie "array-ness".

Retour aux métaclasses.

Metaclasses & hellip;

  • sont des usines pour les classes
  • définissent le comportement des classes
  • DEFINE un niveau métaphysique ce que cela signifie d'être une classe

Dans Ruby, ces trois responsabilités sont réparties entre trois différents places:

  • la classe Class crée des classes et définit un peu de la b ehavior
  • les eigenclass de classe individuelle définit un peu du comportement de la classe
  • le concept de « classness » est cablé dans l'interpréteur, qui met également en œuvre la plus grande partie du comportement (par exemple, vous ne pouvez pas hériter de Class pour créer un nouveau type de classe qui recherche des méthodes différentes, ou quelque chose comme ça   – l'algorithme de recherche de méthode est cablé dans l'interpréteur)

ainsi, ces trois choses jouent ensemble le rôle de métaclasses, mais ni l'un de ceux-ci est une métaclasse (chacun ne met en œuvre qu'une petite partie de ce que fait une métaclasse), ni le somme de ceux de la métaclasse (parce qu'ils font beaucoup plus que cela).

Malheureusement, certaines personnes appellent des classes propres de classes métaclasses. (Jusqu'à récemment, j'étais l'une de ces âmes égarées, jusqu'à ce que j'ai finalement vu la lumière.) D'autres personnes appellent tous métaclasses eigenclasses. (Malheureusement, une de ces personnes est l'auteur de l'un des didacticiels les plus populaires sur la métaprogrammation Ruby et le modèle d'objet Ruby.) Certaines bibliothèques populaires ajoutent une méthode metaclass à Object qui renvoie la classe propre de l'objet (par exemple ActiveSupport, Facets, metaid). Certaines personnes appellent toutes les classes virtuelles (c'est-à-dire les classes propres et incluent les classes) les métaclasses. Certaines personnes appellent Class la métaclasse. Même dans le code source Ruby lui-même, le mot «métaclasse» est utilisé pour désigner des choses qui ne sont pas des métaclasses.

+0

Merci pour le post. C'est exactement exactement ce que je cherchais. Maintenant, nous espérons que quelqu'un avec plus de connaissances Python peut commenter ou poster sur les différentes fonctionnalités de Python. –

+2

La question a changé depuis que j'ai répondu à l'origine, mais j'ai décidé de créer une nouvelle réponse au lieu d'éditer celle-ci, et de laisser celle-ci en haut, dans l'espoir qu'elle sera utile à quelqu'un. ruby python metaclass "ou quelque chose comme ça. –

+0

Merci pour vos réponses. L'objectif de la question était d'en apprendre davantage sur les métaclasses dans les deux langues. J'ai ajouté la première édition quand j'ai réalisé que ma compréhension d'eux en Python était fausse. Désolé si cela vous a dérouté. Vos deux réponses ont été très utiles pour découvrir les caractéristiques de Ruby. –

12

Votre question mise à jour semble assez différente maintenant. Si je vous comprends bien, vous voulez vous accrocher à l'allocation d'objets et à l'initialisation, ce qui n'a absolument rien à voir avec les métaclasses. (Mais n'écrit toujours pas ce que vous voulez réellement faire, donc je pourrais toujours être éteint.)

Dans certains langages orientés objet, les objets sont créés par les constructeurs. Cependant, Ruby n'a pas de constructeur. Les constructeurs ne sont que des méthodes d'usine (avec des restrictions stupides); il n'y a aucune raison de les avoir dans un langage bien conçu, si vous pouvez simplement utiliser une méthode d'usine (plus puissante) à la place.

construction d'objets Ruby fonctionne comme ceci: la construction de l'objet est divisé en deux phases, allocation et initialisation. L'allocation est effectuée par une méthode de classe publique appelée allocate, qui est définie comme une méthode d'instance de la classe Class et est généralement jamais remplacée par. (En fait, je ne pense pas que vous pouvez le remplacer.Il alloue juste l'espace mémoire pour l'objet et met en place quelques pointeurs, cependant, l'objet n'est pas vraiment utilisable à ce stade.

C'est là qu'intervient l'initialiseur: il s'agit d'une méthode d'instance appelée initialize, qui définit l'état interne de l'objet et l'amène dans un état cohérent et entièrement défini pouvant être utilisé par d'autres objets.

Ainsi, afin de créer entièrement un nouvel objet, ce que vous devez faire est la suivante:

x = X.allocate 
x.initialize 

[Note:. Programmeurs Objective-C peuvent reconnaître]

Cependant, parce qu'il est trop facile à oublier d'appeler initialize et en règle générale un objet devrait être entièrement valide après la construction, il y a une méthode usine commode appelée Class#new, qui fait tout ce qui fonctionne pour vous et ressemble à ceci:

class Class 
    def new(*args, &block) 
    obj = allocate 
    obj.initialize(*args, &block) 

    return obj 
    end 
end 

[Note: En fait, initialize est privé, donc la réflexion doit être utilisé pour contourner les restrictions d'accès comme celui-ci: obj.send(:initialize, *args, &block)]

Ce, par ailleurs, est la raison pour laquelle de construire un objet que vous appel une méthode de classe publique Foo.new mais mettre en œuvre une méthode d'instance privée Foo#initialize, qui semble trébucher beaucoup de nouveaux arrivants.

Cependant, aucun n'est cuit dans le langage. Le fait que la méthode d'usine primaire pour une classe est généralement appelé new est juste une convention (et parfois je souhaite qu'il était différent, car il ressemble à des constructeurs en Java, mais est complètement différent). Dans d'autres langues, le constructeur doit avoir un nom spécifique. En Java, il doit avoir le même nom que la classe, ce qui signifie que a) il ne peut y avoir qu'un seul constructeur et b) les classes anonymes ne peuvent pas avoir de constructeur car elles n'ont pas de noms. En Python, la méthode d'usine doit être appelée __new__, ce qui signifie encore une fois qu'il peut y en avoir un seul. (Dans Java et Python, vous pouvez bien sûr avoir différentes méthodes d'usine, mais les appeler est différent d'appeler la valeur par défaut, tandis que dans Ruby (et Smalltalk d'où ce modèle est originaire), il semble identique.)

In Ruby, il peut y avoir autant de méthodes d'usine que vous le souhaitez, avec n'importe quel nom que vous aimez, et une méthode d'usine peut avoir plusieurs noms différents. (Pour les classes de collecte, par exemple, la méthode de fabrication est souvent aliasé à [], qui vous permet d'écrire List[1, 2, 3] au lieu de List.new(1, 2, 3) qui se termine à la recherche plus comme un tableau, soulignant ainsi la collection ish nature des listes.)

En bref:

  • la méthode d'usine normalisée est Foo.new, mais il peut être quelque chose
  • Foo.new appels allocate à allouer de la mémoire pour un objet vide foo
  • Foo.new appelle alors foo.initialize, c'est-à-direla méthode d'instance Foo#initialize
  • les trois personnes ne sont que des méthodes comme les autres, que vous pouvez annuler la définition, redéfinir, override, wrap, alias et ainsi de suite
  • bien
  • , à l'exception allocate qui doit allouer de la mémoire à l'intérieur du moteur d'exécution Ruby qui vous ne peut pas vraiment faire de Ruby

en Python, __new__ correspond à peu près à la fois new et allocate en Ruby, et __init__exactement correspond à initialize en Ruby. La principale différence est que dans Ruby, new appelle initialize alors qu'en Python, le runtime appelle automatiquement __init__ après __new__.

Par exemple, voici une classe qui permet seulement un maximum de 2 instances créées:

class Foo 
    def self.new(*args, &block) 
    @instances ||= 0 
    raise 'Too many instances!' if @instances >= 2 

    obj = allocate 
    obj.send(:initialize, *args, &block) 

    @instances += 1 

    return obj 
    end 

    attr_reader :name 

    def initialize(name) 
    @name = name 
    end 
end 

one = Foo.new('#1') 
two = Foo.new('#2') 
puts two.name   # => #2 
three = Foo.new('#3') # => RuntimeError: Too many instances! 
+1

n'avez-vous pas besoin de 'obj = super'? :) – horseyguy

+0

@banister: Oui, vous avez raison. Je l'ai inversé à la dernière minute pour m'assurer que je n'incrémente pas '@ instances' avant que l'objet ne soit effectivement construit et oublié de le tester. Je l'ai juste inversé, car c'est probablement trop pour un petit échantillon de code. Dans un système réel, vous le placerez probablement dans un bloc d'exception 'else'. –

+0

@banister: En fait, tout était mal pensé. –

Questions connexes