2012-05-11 37 views
5

He visto estas preguntas "Crear dinámicamente una clase" que se responden diciendo: "use la función tipo()". Estoy seguro de que tendré que hacerlo en algún momento, pero sé que no tengo ni idea. Pero por lo que he visto, debes saber algo sobre la clase, como un nombre.Creación dinámica de una clase desde un archivo en Python

Lo que estoy tratando de hacer es analizar un tipo de archivo idl y crear una clase que tendrá métodos y atributos. Así que NO tengo ningún conocimiento al frente de lo que será el nombre de clase, funciones, argumentos o cualquier cosa hasta que analice la cadena.

¿Alguna idea?

+0

¿Podría proporcionar un pequeño ejemplo de archivo de entrada y clase creada? – Simon

Respuesta

14

http://docs.python.org/library/functions.html#type

Es un poco difícil de Google para, pero se puede buscar python type(name, bases, dict) function examples para obtener:

http://www.voidspace.org.uk/python/articles/metaclasses.shtml

Un extracto de lo anterior, que llega al corazón de su pregunta:


Los siguientes son básicamente equivalentes:

def __init__(self, x): 
    self.x = x 

def printX(self): 
    print self.x 

Test = type('Test', (object,), {'__init__': __init__, 'printX': printX}) 

y:

class Test(object): 
    def __init__(self, x): 
     self.x = x 

    def printX(self): 
     print self.x 

Hay dos maneras de crear funciones sobre la marcha que se me ocurre. La forma generalmente mala es escribir el código y volver a analizarlo (aunque hecho correctamente, esto puede aumentar enormemente el rendimiento). La manera correcta es implementar una función que interprete su IDL. Esto se llama una función de orden superior: http://effbot.org/pyfaq/how-do-you-make-a-higher-order-function-in-python.htm

Un ejemplo de lo que iba a escribir, si no puede encontrar un intérprete para su IDL (de si se trata de una costumbre IDL) es algo así como el enlace anterior, tales como:

def makeMethod(idlCode): 
    syntax = MyIDL.parse(idlCode) 

    def newMethod(*args, **kw): 
     if syntax.statementType == MyIDL.IF_STATEMENT: 
      if secureLookup(mySyntaxTree.IF): 
       return secureLookup(args[0]) 
      else: 
       return secureLookup(args[1]) 
     ... 

    return (syntax.methodName, newMethod) 

Hay muchas maneras más elegantes de expandir este método, si se establece una correspondencia entre las construcciones de su IDL y la sintaxis de args * y ** kw, pero esto le da la mayor flexibilidad y es el más manera directa y básica en la que podría pensar.

Entonces será qué pasa en:

class DynamicIdlClass(object): 
    ... 

for idlObject in idlCode: 
    methods = dict(makeMethod(clause) for clause in idlObject.clauses}) 
    methods['__init__'] = makeInitMethod(idlObject.initClause) 
    idlObject = type('Test', (DynamicIdlClass,), methods) 

    yield idlObject # or idlObjectsList.push(idlObject), etc. 
+0

El código aquí todavía requiere que tenga conocimiento de que el usuario define '__init__' y' printX'. El enlace de voidspace.org fue bastante interesante, un poco confuso, pero después de que lo confundí, estuvo bastante cerca. – jiveturkey

+0

jnbbender: No requiere que el usuario defina estas funciones (bueno, tal vez en el sentido en que está usando la frase ...). Hay dos formas de crear funciones sobre la marcha que se me ocurren. La mala forma es escribir el código y volver a analizarlo. La manera correcta es implementar una función que interprete su IDL. Esto se llama una función de orden superior: http://effbot.org/pyfaq/how-do-you-make-a-higher-order-function-in-python.htm – ninjagecko

1

En nuestro proyecto se crea un objeto que expone un método concreto que tiene la capacidad de agregar atributos exigibles. Analizamos una gran cantidad de archivos yaml para el esquema y montamos estos objetos dinámicamente.

Algo similar a

def add_method(name, callable): 
    setattr(self,name,callable) 

Pero este es un ejemplo muy ingenua, pero se entiende la idea. El esquema se almacena dentro de cada clase dinámica bajo algo como __schema, que es un diccionario que contiene prototipos ksgg y cadenas de documentos.

Para los callables reales crea un esqueleto como

def skel(self, *args, **kwargs): 
    if '_schema' in kwargs: 
     parse_yml, add control statements 
    handle args and kwargs based on schema 

some_instance.add_method('blah',skel) 

Una vez más, estos son dos ejemplos muy ingenuos y no cubren la mayoría de los temas y casos extremos que surgen cuando se trabaja con este tipo de problema. Este es también uno de varios métodos para encontrar una solución.

Ejemplos:

IonObject

Object Model Generator

+0

Esto no crea atributos invocables. Es un método conveniente para llamar a 'setattr'. Esto no le permite, en sí mismo, definir métodos (que tienen un primer argumento implícito "self"), o establecer arbitrariamente las clases base. Solo le permite hacer lo que es sintácticamente equivalente a 'myObject.fieldName = value'. La única razón por la que esto funciona en el código IonObject es porque está extrayendo valores ya creados de una clase idéntica, por lo que los métodos se conservan (aunque pueden funcionar incorrectamente si no se implementan con __class__, tal vez incluso entonces), y las clases base ya están lo mismo. – ninjagecko

+0

Un método para realizar este truco, que * * funcionará en los métodos (y si no necesita cambiar las clases base), es http://code.activestate.com/recipes/81732-dynamically-added-methods- to-a-class/# c3 - aunque parece ser bastante frágil si haces cualquier reflexión/inspección. – ninjagecko

+0

Vengo de C++, creo que querrías agregar callable a self (que es otro objeto), qué nombre es que no sé. es decir, 'def add_method (self, invocable, valor): setattr (self, invocable, value)'. ¿Puedes hacer esto? – jiveturkey

1

en lugar de pensar en esto como '¿cómo crear dinámicamente una clase', lo veo como una oportunidad de utilizar las capacidades de metaprogramación de Python en lugar .

El primer lugar en el que buscaría ideas es en la fuente de biblioteca estándar para xmlrpclib - mire específicamente cómo usa __getattr__ para manejar llamadas de métodos que de otro modo no existirían sobre la marcha.

Sé que realmente no entendía por qué llaman a Python un lenguaje dinámico hasta que llegué a aceptar estas posibilidades.

Cuestiones relacionadas