6 votos

¿Objetos de Python herramientas herramienta no recuerdo las variables de instancia entre llamadas de función?

Estoy desarrollando una caja de herramientas de Python, y llegó a través de un comportamiento curioso: La herramienta de los objetos no recordar las variables de instancia entre llamadas a la función. Considere este código:

import arcpy

class Toolbox(object):

   def __init__(self):
      self.label = "Test"
      self.alias = "Test"
      self.tools = [Tool]

class Tool(object):

   def __init__(self):
      self.label = "Test tool"
      self.x = "INIT"

   def getParameterInfo(self):
      param0 = arcpy.Parameter(
         displayName = "Parameter",
         name = "parameter",
         datatype = "String",
         parameterType = "Required",
         direction = "Input")
      return [param0]

   def updateParameters(self, parameters):
      self.x = "UPDATE PARAMETERS"

   def updateMessages(self, parameters):
      parameters[0].setWarningMessage(self.x)
      self.x = "UPDATE MESSAGES"

   def execute(self, parameters, messages):
      arcpy.AddMessage(self.x)

La manera en que yo esperaría que este código se comportan es este:

  • Primera __init__ y x se establece en "INIT".
  • A continuación, updateParameters y x es cambiado a "UPDATE PARAMETERS".
  • A continuación, updateMessages se llama. Muestra un mensaje de alerta con el texto "UPDATE PARAMETERS" y, a continuación, cambie x a "UPDATE MESSAGES".
  • Finalmente, cuando se ejecuta la herramienta se emite "UPDATE MESSAGES".

Sin embargo, cuando ejecuto la herramienta tanto de la advertencia y la salida de la ejecución es "INIT". Mi conclusión es que ArcMap no utilizar la misma instancia de la clase a lo largo, sino que crea una nueva instancia para cada llamada de función (updateParameters, updateMessagesy execute).

Es mi conclusión correcta? Si es así, ¿por qué ArcMap se comportan de esa manera? A partir de una programación orientada a objetos perspectiva parece muy extraño.

Este comportamiento provoca grandes problemas para mí. El primer parámetro de la herramienta que estoy desarrollando es un archivo de texto. Cuando el usuario ha elegido un quiero rellenar el resto de parámetros con diferentes valores por defecto en función del contenido del archivo. Dado que el archivo podría ser bastante grande, yo no quiero tener que analizar cada vez que los cambios en los parámetros, pero ahora parece que tengo que ya no hay manera de saber si el parámetro ha cambiado o no.

5voto

steveax Puntos 316

Una cosa que he notado (con complementos personalizados o herramientas de secuencia de comandos/PYT) es que cuando ArcMap o Catálogo carga, va a crear instancias de las clases contenidas dentro de estas herramientas personalizadas como se carga la aplicación. No creo que estas iniciar una nueva instancia de la clase cuando se corrió la herramienta (a menos que se actualice el PYT), pero tal vez estoy equivocado. No he probado esta plenamente. Es interesante si se crea una nueva instancia de la clase siempre que cualquier método que se realiza la llamada.

Para evitar esto, puede que desee incluir una función que puede ser llamado para analizar el archivo de texto si un parámetro es alterado de modo que usted puede controlar de forma explícita cuando el archivo de texto que se vuelve a cargar. Un ejemplo sencillo:

def parseText(txt):
    with open(txt, 'r') as f:
        return f.readlines() 

Así, en su PYT usted puede llamar a esta función si el parámetro ha sido modificado por el usuario:

   def updateParameters(self, parameters):
      if parameters[0].altered:
          text_content = parseText(r'C:\path\to\your_file.txt')
          self.x = "UPDATE PARAMETERS"

También puede llamar a esta en la __init__ de la clase para obtener una caché inicial de los contenidos. Python es bastante rápido al analizar los archivos de texto, incluso si son grandes. Si se carga cada línea de texto en una lista y usted está preocupado acerca de la memoria, tal vez sería mejor tener la función de devolución de un generador.

Por desgracia, no creo que usted puede conseguir alrededor de no volver a cargar el archivo de texto si el usuario cambia un parámetro. Tal vez usted puede hacer todo el control para el archivo de texto en la UpdateParameters con algunas declaraciones, como if parameters[0].value == 'this' and parameters[1].value == 'another thing' y manejar el archivo de texto de forma diferente para cualquier combinación de parámetros de entrada que recibe.

Yo estoy con usted, sin embargo, no me gusta el comportamiento predeterminado de los PYTs.

EDITAR:

Wow, estás en lo correcto. Es el hilado hasta una TONELADA de instancias! Aquí está una manera simple de pista, número de instancias:

>>> class Test(object):
    i = 0
    def __init__(self):
        Test.i += 1


>>> t = Test()
>>> t2 = Test()
>>> t3 = Test()
>>> t3.i
3

Y ahora, si aplicamos la misma lógica a un PYT, intente ejecutar este en ArcMap:

import arcpy
import pythonaddins

class Toolbox(object):

    def __init__(self):
      self.label = "Test"
      self.alias = "Test"
      self.tools = [Tool]

class Tool(object):
    i = 0
    def __init__(self):
        self.label = "Test tool"
        self.x = "INIT"
        Tool.i += 1

    def getParameterInfo(self):
        param0 = arcpy.Parameter(
        displayName = "Parameter",
        name = "parameter",
        datatype = "String",
        parameterType = "Required",
        direction = "Input")
        return [param0]

    def updateParameters(self, parameters):
        if parameters[0].altered:
            self.x = "UPDATE PARAMETERS"
            pythonaddins.MessageBox('# of instances: {}, x = {}'.format(self.i, self.x), 'test')

    def updateMessages(self, parameters):
        parameters[0].setWarningMessage(self.x)
        self.x = "UPDATE MESSAGES"
        pythonaddins.MessageBox('# of instances: {}, x = {}'.format(self.i, self.x), 'test')

    def execute(self, parameters, messages):

        arcpy.AddMessage('# of instances: {}, x = {}'.format(self.i, self.x))

Tengo 9 instancias de inmediato! Se gira hacia arriba de otros nuevos cada vez que puedo cambiar un parámetro, y se mantiene en ascenso!

enter image description here

3voto

Travis Puntos 61

crmackey tiene razón, que está haciendo un montón de instancias del objeto. Lo que debes hacer para evitar esto es crear una variable global en a getParameterInfo (porque sólo se llama una vez) y luego actualizar o hacer referencia a ella en updateParameters o updateMessages.

Por ejemplo:

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X