2008-12-10 21 views
32

En Java puede definir una nueva clase en línea utilizando clases internas anónimas. Esto es útil cuando necesita reescribir solo un método de la clase.¿Tiene Python algo así como clases internas anónimas de Java?

Supongamos que desea crear una subclase de OptionParser que anule solo un único método (por ejemplo, exit()). En Java se puede escribir algo como esto:

new OptionParser() { 

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

Esta pieza de código crea una clase anónima que se extiende OptionParser y anular sólo el método exit().

¿Hay un modismo similar en Python? ¿Qué idioma se usa en estas circunstancias?

Respuesta

31

Puede usar la función integrada type(name, bases, dict) para crear clases sobre la marcha. Por ejemplo:

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

Desde OptionParser no es una clase de nuevo estilo, que tiene que incluir explícitamente object en la lista de clases base.

+2

Esta es la forma ** correcta ** de incorporar definiciones de clase/crear clases anónimas (cita de la página del manual citada anteriormente: "Esta es esencialmente una forma dinámica de la declaración de clase"); la "respuesta aceptada" debería señalarlo. – Vlad

+0

No entiendo esto {"foo": lambda self: "foo"}. ¿Se supone que regresará? – zakdances

+0

Haga clic en el enlace a los documentos. "el diccionario dict es el espacio de nombres que contiene las definiciones para el cuerpo de la clase y se convierte en el atributo __dict__". Entonces "foo" es el nombre de un método que toma solo "self" como parámetro. Ese método pasa a devolver la cadena "foo". –

16

Java utiliza clases anónimas principalmente para imitar cierres o simplemente bloques de código. Dado que en Python se puede pasar fácilmente por métodos que no hay necesidad de una construcción tan torpe como clases internas anónimas:

def printStuff(): 
    print "hello" 

def doit(what): 
    what() 

doit(printStuff) 

Editar: Soy consciente de que esto no es lo que se necesita en este caso especial. Acabo de describir la solución python más común para el problema más comúnmente por clases internas anónimas en Java.

+3

-1 Quiero subclase optparse.OptionParser() no pasar alrededor de referencias de métodos. –

+0

En ese caso, ¿cuál es el problema de escribir una subclase real? Simplemente estaba ilustrando el uso más común de las clases internas anónimas. –

+2

+1: las clases internas anónimas son una forma de que Java se aligere con la compleja sintaxis. Python ya tiene una sintaxis simple y no necesita clases anónimas para aligerar la carga. –

2

En python tiene funciones anónimas, declaradas mediante la instrucción lambda. No me gustan mucho, no son tan legibles y tienen una funcionalidad limitada.

Sin embargo, lo que está hablando puede ser implementado en Python con un enfoque completamente diferente:

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

def meth_b(obj): 
    print "b" 

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

Pista correcta, pero esto no funciona. Archivo "t", línea 12, en b.meth_a() TypeError: meth_b() toma exactamente 1 argumento (0 dado) – gnud

+0

Derecha: para cambiar un método de un objeto, debe cambiarlo a su metaclase. He actualizado el código. –

+0

b .__ clase __. Meth_a = meth_b ¿Este código afecta a todos los objetos basados ​​en la clase "a" o solo en el objeto "b"? –

5

Python probablemente tiene mejores maneras de resolver su problema. Si pudiera proporcionar detalles más específicos de lo que quiere hacer, sería útil.

Por ejemplo, si necesita cambiar el método que se llama en un punto específico del código, puede hacerlo pasando la función como un parámetro (las funciones son objetos de primera clase en python, puede pasarlos a funciones, etc.) También puede crear funciones anónimas lambda (pero están restringidas a una sola expresión).

Además, dado que el pitón es muy dinámico, puede cambiar los métodos de un objeto después de que se ha creado object.method1 = alternative_impl1, aunque en realidad es un poco más complicado, ve gnud's answer

+0

En realidad, no puede reemplazar los métodos de esta manera; consulte mi respuesta por el camino correcto. – gnud

11

Esto se puede hacer de tres maneras:

  1. subclase apropiada (por supuesto)
  2. un método personalizado que se invoca con el objeto como un argumento
  3. (lo que es probable que desee) - la adición de un nuevo método para un objeto (o r reemplazando a uno existente).

Ejemplo de la opción 3 (editado para eliminar el uso de la "nueva" módulo - Está obsoleto, yo no sabía):

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

A falta de crear una clase interna con nombre e instanciarla (que puede ser preferible para la legibilidad), la opción 3 parece ser una buena forma de hacerlo. –

+0

sí, es por eso que escribí "Lo que probablemente quieras".Acabo de ponerlo último porque el texto del post fluye mejor de esa manera, y quería enumerar las opciones. – gnud

+0

¿por qué estás usando nuevo? está obsoleto desde hace bastante tiempo –

10

Bueno, las clases son objetos de primera clase, para que pueda crear ellos en métodos si quieres. p.ej.

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 

Pero, realmente, ¿por qué no simplemente definir la clase en el nivel normal? Si necesita personalizarlo, ponga la personalización como argumentos en __init__.

(editar: fija los errores de escritura en código)

+0

Gracias, pero no entiendo la línea custom_op = custom_op_class(). ¿Se debe eliminar? –

+0

make_custom_op() devuelve una clase. Tienes que llamar a la clase para crear una instancia, que es lo que hace esa línea. Sin embargo, había un par de errores en mi código en las últimas dos líneas, que ahora he corregido, en caso de que te estuvieran confundiendo. –

+0

Esta es una técnica simple pero poderosa que, cuando se usa correctamente (no solo como un truco), puede generar un código muy elegante. –

6

Python no soporta este (clases anónimas) directamente, sino a causa de su sintaxis concisa que no es realmente necesario:

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

p = MyOptionParser() 

El único Lo malo es que agregas MyOptionParser a tu espacio de nombres, pero como señaló John Fouhy, puedes ocultarlo dentro de una función si vas a hacerlo varias veces.

0

siempre se puede ocultar clase por variables:

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

en lugar 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""") 
Cuestiones relacionadas