2011-02-15 11 views
6

que estoy tratando de hacer una clase ImageLoader manejar la carga y procesamiento de recursos de imagen así: Sin embargoclase llamada/método estático de la variable de clase en Python

class ImageLoader: 
    TileTable = __loadTileTable('image path', some other variable) 

    @staticmethod 
    def _loadTileTable(arg1, arg2): 
     blah blah 

, en la compilación me sale: NameError: name '_loadTileTable' is not defined

Si se sustituye la segunda línea con TileTable = ImageLoader.__loadTileTable('image path', some other variable) entonces consigo NameError: name 'ImageLoader' is not defined

como estoy pasando de C# para Python, clases estáticas con métodos estáticos es lo que yo utilizo para implementar esto. Sin embargo, estoy abierto a cómo haría esto en general en python (es decir, llamar a funciones de biblioteca estáticas que solo están agrupadas por su funcionalidad).

ACTUALIZACIÓN: Después de leer las dos respuestas, estoy recibiendo una imagen que lo que estoy tratando de hacer, probablemente no está bien. ¿Cómo hago para imlementing ImageLoader para que pueda hacer esto:

Suponiendo que la tabla de baldosas devuelve una matriz

module1.py

aTile = ImageLoader.TileTable[1] 

module2.py

anotherTile = ImageLoader.TileTable[2] 

idealmente, llenaría TileTable solo en ce.

Actualización:

Gracias por todas las respuestas, me encontré con mi última respuesta a poblar TileTable sólo una vez en los módulos de Python mana

"A module can contain executable statements as well as function definitions. These statements are intended to initialize the module. They are executed only the first time the module is imported somewhere"

En cuanto a la clase estática, voy a renunciar a las clases y simplemente hacer una variable de nivel de módulo.

+2

Cuando está usando 'staticmethod' en Python, generalmente está haciendo algo mal. Si no necesita variables de instancia ni variables de clase (campos estáticos), solo use una función de nivel de módulo. – delnan

+1

@delnan: No estoy totalmente de acuerdo. Caso en el punto 'dict.fromkeys()' por defecto de Python. Hay casos de uso legítimos para estas cosas. – jathanism

+0

@delnan: No estoy de acuerdo. La encapsulación es el inquilino principal de la programación orientada a objetos, que Joe intenta usar. Mantener una función que solo se relaciona con un ImageLoader dentro de la clase ImageLoader es una forma efectiva de codificar programáticamente esa relación (en lugar de asociarlas implícitamente porque están en el mismo módulo). –

Respuesta

4

Respondiendo solo la pregunta actualizada, lo que haría en Python es hacer TileTable una variable llamada tile_table en un módulo llamado imageloader. No hay ninguna razón para poner algo de esto dentro de una clase.

Así pues, tienes:

module1.py

import imageloader 
aTile = imageloader.tile_table[1] 

module2.py

import imageloader 
anotherTile = imageloader.tile_table[2] 

y imageload.py se ve algo como:

def _loadTileTable(arg1, arg2): 
    pass # blah blah 
tile_table = _loadTileTable('image path', other_var) 

Piense en un módulo de Python como una instancia única en otros idiomas (que de hecho lo es) y podrá reconciliar esto con cualquier preconceptos de OO que haya heredado de otros idiomas.

+0

me gusta la simplicidad de su respuesta, pero ¿no significa esto que cada vez que llamo tile_table, ejecutaría de nuevo la función _loadTileTable que innecesariamente cargaría la misma cosa? Entonces, lo que probablemente querría en este caso es una función para cargarlo en una variable de nivel de módulo y luego usar esa variable. – Joe

+0

No, el código en imageload.py solo se ejecuta una vez, la primera vez que se importa (¡oops, acabo de notar que debe ser imageloader.py!). Las importaciones posteriores no vuelven a ejecutar el código, simplemente reutilizan el módulo existente. – Duncan

3

En Python, el código en el bloque de clases primero se ejecuta, luego el espacio de nombres resultante se pasa al inicializador de clase. El código que escribió también podría haber sido escrita como:

TileTable = _loadTileTable(arg1, arg2) 
@staticmethod 
def _loadTileTable(arg1, arg2): 
    pass # blah blah 
ImageLoader = type('ImageLoader',(), {'TileTable': TileTable, '_loadTileTable': _loadTileTable}) 
del TileTable 
del _loadTileTable 

Como se puede ver, la llamada de _loadTileTable aparece antes de la definición de la misma. En su ejemplo, dentro de la definición de clase, la llamada a _loadTileTable debe venir después de la definición de _loadTileTable.

Una posible solución es simplemente reorganizar la definición de la clase.

class ImageLoader: 
    def _loadTileTable(arg1, arg2): 
     pass # blah, blah 

    TileTable = _loadTileTable('image path', other_var) 

Tenga en cuenta que me quita el 'métodoestático', porque en el punto donde se llama _loadTileTable, que está siendo llamado como una función y no un método. Si realmente desea que esté disponible después de la inicialización de clase, puede definirlo como un método estático después del hecho.

class ImageLoader: 
    def _loadTileTable(arg1, arg2): 
     pass # blah, blah 

    TileTable = _loadTileTable('image path', other_var) 

    _loadTileTable = staticmethod(_loadTileTable) 
+0

Su código de ejemplo no funcionará realmente debido a la llamada al método dentro de la clase que requiere argumentos que no se pueden pasar. – jathanism

+0

jathanism: Actualicé la respuesta para que coincida con los cambios a la pregunta (que asume que los parámetros a _loadTileTable están en 'globals()') –

+0

No estoy seguro de que lo entiendo completamente. Su segundo bloque de código funciona, no había intentado eliminar el decorador de métodos estáticos la primera vez. Sin embargo, esto significa que tendré que preocuparme por el orden de declaración de mis variables/métodos. ¿Hay alguna manera como en C# que puedo declarar una variable y usar un método dentro de la clase y no tener que preocuparme por el orden de invocación? – Joe

2

¿Hay algún motivo de diseño por el que esté utilizando un método estático? De ser así, debido a que no está sobrecargando la inicialización de clase, deberá declarar la variable después de la definición del método.

Pero, si lo hace, you'lll conseguir un nuevo error:

NameError: name 'arg1' is not defined 

La razón de esto se debe a que está ejecutando el método dentro de la clase antes de la clase, incluso se crea una instancia, por lo tanto, nunca tener la oportunidad de pasar los argumentos al método.

Por lo tanto, la forma correcta de hacerlo es sobrecargar el método __init__() de manera que la asignación a TileTable sólo ocurre cuando la clase se construye:

class ImageLoader(object): 
    def __init__(self, arg1, arg2): 
     self.TileTable = self._loadTileTable(arg1, arg2) 

    @staticmethod 
    def _loadTileTable(arg1, arg2): 
     print arg1, arg2 

Esto le da la posibilidad de llamar a ImageLoader._loadTileTable() sin tener una instancia , pero también le permite crear la variable de instancia TileTable al crear una instancia.

El uso de un método de la clase
En respuesta a mi comentario acerca de la posible necesidad de un classmethod, aquí hay un ejemplo que cubre lo siguiente:

class ImageLoader: 
    @classmethod 
    def _loadTileTable(cls, arg1, arg2): 
     return arg1, arg2 

# We're creating the class variable outside of the class definition. If you're doing 
# this in a module, no one will ever notice. 
ImageLoader.TileTable = ImageLoader._loadTileTable('foo', 'bar') 

Puede haber una mejor manera de hacer esto, No lo sé. Pero yo creo que esto cubre lo que busca:

>>> i = ImageLoader() 
>>> i 
<__main__.ImageLoader instance at 0x100488f80> 
>>> ImageLoader.TileTable 
('foo', 'bar') 
>>> i.TileTable 
('foo', 'bar') 

Allí tienen una instancia i que tiene acceso a la variable de clase, pero ImageLoader.TileTable aún está disponible desde el objeto de clase sin la necesidad de una instancia.

+0

Creo que debería haber señalado que mi objetivo era crear una TileTable estática para que otras clases puedan acceder a ella. No quiero instancias separadas de ImageLoader flotando. el loadTileTable está destinado a ser una función privada que hace algún trabajo para cargar teselas cuando se utiliza por primera vez TileTable o cuando la clase estática se inicializa por primera vez. Entonces una vez cuando la aplicación comience. – Joe

+0

Parece que puede necesitar un 'classmethod', que usa el objeto de clase como primer argumento (donde un método de instancia utiliza la instancia como primer argumento). Los métodos estáticos no están vinculados a la clase y, por lo tanto, no tienen acceso a las variables de clase. – jathanism

+0

intenté cambiar el decorado para classmethod pero aún así me dijo que el método no estaba definido. – Joe

3

Las variables de nivel de clase que se actualizan son malas y malas. Nuestra expectativa predeterminada es que las instancias del objeto son con estado y las clases son sin estado.

En este caso, estamos tratando de inicializar "mágicamente" una colección como una variable de clase, lo cual es una idea tremendamente mala. Una colección es simplemente un objeto con atributos simples de nivel de instancia.

La tabla de mosaico mágico no debe ser una parte oculta y estática del ImageLoader. No hay una razón posible para eso.Debería ser un argumento para el ImageLoader si desea evitar cargarlo más de una vez.

La separación de estos promueve la capacidad de prueba. No es arbitrario. Así es como se realizan las pruebas unitarias.

Lo que quiere es esto.

class ImageLoader(object): 
    def __init__(self, theTileTable): 
     self.tile_table= theTileTable 

class TileTable(object): 
    def __init__(self, path, some_other_arg): 
     self.tileTable= self._loadTileTable(path, some_other_arg) 
    def _loadTileTable(arg1, arg2): 
     blah blah 

No static anything. Unidades independientes Más fácilmente comprobable. Sin dependencias extrañas. Sin magia

+1

La variable de nivel de clase nunca se actualizará, solo se recuperará. Como su nombre indica, es una tabla de mosaicos de imágenes (recuperada de un archivo una vez). La clase de imageloader está allí únicamente para administrar la creación y el almacenamiento de los recursos de los mosaicos, no veo la sensación de crear otra clase para contener un método que lógicamente pertenecería a la clase de imageloader. Por último, para usar esto en dos módulos separados, tendría que crear dos instancias o pasar una referencia de cargador de imágenes, de las cuales mencioné en mi pregunta que no quería hacer. – Joe

+0

De todos modos, gracias por su contribución, voy a probar lo que sugirió Duncan y ver cómo voy. – Joe

+0

@Joe: "No veo la sensación de crear otra clase para contener un método que lógicamente pertenecería a la clase de cargador de imágenes". Testabilidad. Creo que la respuesta ya dijo eso. "para usar esto en dos módulos separados, tendría que crear dos instancias o pasar una referencia de cargador de imágenes alrededor,". Lamentablemente, pasar la instancia es la respuesta correcta, y es la más comprobable. Simplemente negarse a escribir un código comprobable no es realmente la mejor idea. –

Cuestiones relacionadas