2008-12-10 4 views
32

En Java, vous pouvez définir une nouvelle classe en ligne en utilisant des classes internes anonymes. Ceci est utile lorsque vous devez réécrire une seule méthode de la classe. Supposons que vous vouliez créer une sous-classe OptionParser qui ne remplace qu'une seule méthode (par exemple exit()). En Java, vous pouvez écrire quelque chose comme ceci:Est-ce que Python a quelque chose comme les classes internes anonymes de Java?

new OptionParser() { 

    public void exit() { 
     // body of the method 
    } 
}; 

Ce morceau de code crée une classe anonyme qui va OptionParser et passer outre que la méthode exit().

Il existe un idiome similaire en Python? Quel idiome est utilisé dans ces circonstances?

Répondre

31

Vous pouvez utiliser la fonction intégrée type(name, bases, dict) pour créer des classes à la volée. Par exemple:

op = type("MyOptionParser", (OptionParser,object), {"foo": lambda self: "foo" }) 
op().foo() 

Depuis OptionParser n'est pas une nouvelle classe de style, vous devez inclure explicitement object dans la liste des classes de base.

+2

Ceci est le moyen ** approprié ** pour les définitions de classe en ligne/créer des classes anonymes (citation de la page de manuel citée ci-dessus: "Ceci est essentiellement une forme dynamique de l'instruction de classe"); la "réponse acceptée" devrait l'indiquer. – Vlad

+0

Je ne comprends pas cela {"foo": lambda self: "foo"}. Est-ce censé revenir? – zakdances

+0

Cliquez sur le lien vers les documents. "le dictionnaire de dict est l'espace de noms contenant les définitions pour le corps de la classe et devient l'attribut __dict__". Donc "foo" est le nom d'une méthode qui ne prend que "self" comme paramètre. Cette méthode arrive à retourner la chaîne "foo". –

16

Java utilise des classes anonymes principalement pour imiter des fermetures ou simplement des blocs de code. Depuis en Python, vous pouvez facilement passer autour des méthodes il n'y a pas besoin d'une construction en tant que maladroites classes internes anonymes:

def printStuff(): 
    print "hello" 

def doit(what): 
    what() 

doit(printStuff) 

Edit: Je suis conscient que ce n'est pas ce qui est nécessaire dans ce cas particulier. Je viens de décrire la solution python la plus courante au problème le plus souvent par des classes internes anonymes en Java.

+3

-1 Je veux sous-classe optparse.OptionParser() passent pas autour de références de la méthode. –

+0

Dans ce cas, quel est le problème avec l'écriture d'une vraie sous-classe? Je ne faisais qu'illustrer l'usage le plus commun des classes internes anonymes. –

+2

+1: Les classes internes anonymes sont un moyen pour Java d'alléger la syntaxe complexe. Python a déjà une syntaxe simple et n'a pas besoin de classes anonymes pour alléger la charge. –

2

En python vous avez des fonctions anonymes, déclarées en utilisant l'instruction lambda. Je ne les aime pas beaucoup - ils ne sont pas si lisibles, et ont des fonctionnalités limitées.

Cependant, ce que vous parlez peut être mis en œuvre en python avec une approche complètement différente:

class a(object): 
    def meth_a(self): 
    print "a" 

def meth_b(obj): 
    print "b" 

b = a() 
b.__class__.meth_a = meth_b 
+0

La bonne piste, mais cela ne fonctionne pas. Fichier "t", ligne 12, dans b.meth_a() TypeError: meth_b() prend exactement 1 argument (0 donné) – gnud

+0

Droite: pour changer une méthode d'un objet, vous devez le changer en métaclasse. J'ai mis à jour le code. –

+0

b .__ classe __. Meth_a = meth_b Est-ce que ce code affecte tous les objets basés sur une classe "a" ou seulement l'objet "b"? –

5

Python a probablement de meilleures façons de résoudre votre problème. Si vous pouviez fournir des détails plus précis sur ce que vous voulez faire, cela aiderait. Par exemple, si vous avez besoin de changer la méthode appelée dans un point spécifique du code, vous pouvez le faire en passant la fonction en paramètre (les fonctions sont des objets de première classe en python, vous pouvez les passer aux fonctions, etc). Vous pouvez également créer des fonctions anonymes lambda (mais elles sont limitées à une seule expression).

Aussi, depuis python est très dynamique, vous pouvez modifier les méthodes d'un objet après qu'il a été créé object.method1 = alternative_impl1, bien qu'il soit en fait un peu plus compliqué, voir gnud's answer

+0

Vous ne pouvez pas remplacer les méthodes de cette façon - voir ma réponse pour la bonne façon. – gnud

11

Vous pouvez accomplir cela de trois façons:

  1. sous-classe appropriée (bien sûr)
  2. une méthode personnalisée que vous invoquez avec l'objet comme argument
  3. (ce que vous voulez sans doute) - l'ajout d'une nouvelle méthode pour un objet (o r remplaçant un existant).

Exemple de l'option 3 (modifiée pour supprimer l'utilisation du « nouveau » Module - Il est dépréciée, je ne savais pas):

import types 
class someclass(object): 
    val = "Value" 
    def some_method(self): 
     print self.val 

def some_method_upper(self): 
    print self.val.upper() 

obj = someclass() 
obj.some_method() 

obj.some_method = types.MethodType(some_method_upper, obj) 
obj.some_method() 
+0

À court de créer une classe interne nommée et de l'instancier (ce qui peut être préférable pour la lisibilité), l'option 3 semble être un bon moyen de le faire. –

+0

oui - c'est pourquoi j'ai écrit "Ce que vous voulez probablement".Je l'ai juste mis en dernier parce que le texte du texte circule mieux de cette façon - et je voulais énumérer les options. – gnud

+0

pourquoi utilisez-vous nouveau? il est obsolète depuis un certain temps –

10

Eh bien, les classes sont des objets de première classe, de sorte que vous pouvez créer les dans les méthodes si vous voulez. par exemple.

from optparse import OptionParser 
def make_custom_op(i): 
    class MyOP(OptionParser): 
    def exit(self): 
     print 'custom exit called', i 
    return MyOP 

custom_op_class = make_custom_op(3) 
custom_op = custom_op_class() 

custom_op.exit()   # prints 'custom exit called 3' 
dir(custom_op)   # shows all the regular attributes of an OptionParser 

Mais, vraiment, pourquoi ne pas simplement définir la classe au niveau normal? Si vous devez le personnaliser, placez la personnalisation en tant qu'arguments à __init__.

(modifier: Correction d'erreurs de frappe dans le code)

+0

Merci, mais je ne comprends pas la ligne custom_op = custom_op_class(). Devrait-il être retiré? –

+0

make_custom_op() retourne une classe. Vous devez appeler la classe pour créer une instance, ce que fait cette ligne. Cependant, il y avait quelques bugs dans mon code sur les deux dernières lignes, que j'ai maintenant réparé, au cas où ils vous confondraient. –

+0

Ceci est une technique simple mais puissante qui, lorsqu'elle est utilisée correctement (pas seulement comme un hack), peut rendre le code très élégant. –

6

Python ne supporte pas ce directement (classes anonymes) mais à cause de sa syntaxe laconique, il est pas vraiment nécessaire:

class MyOptionParser(OptionParser): 
    def exit(self, status=0, msg=None): 
     # body of method 

p = MyOptionParser() 

La seule inconvénient est que vous ajoutez MyOptionParser à votre espace de noms, mais comme l'a souligné John Fouhy, vous pouvez le cacher dans une fonction si vous allez le faire plusieurs fois.

0

Vous pouvez toujours cacher la classe par des variables:

class var(...): 
    pass 
var = var() 

au lieu de

var = new ...() {}; 
-5
#!/usr/bin/env python 
import os;os.system("""ruby << 'GOZER' 
anonymous_class = Class.new do 
    def hi(name) 
    puts "Hello: #{name}." 
    end 

    def too_late(message) 
    exit 1 
    end 
end 

anonymous_ghoul = anonymous_class.new 
anonymous_ghoul.hi("Egon Spengler") 
anonymous_ghoul.too_late <<-quote 

There's something very import I forgot to tell you. 

Don't cross the streams. 

It would be bad. 

quote 
GOZER""") 
Questions connexes