¿Ejecuta scripts de Python (con parámetros) dentro de otro script de Python con ArcPy?

23

Un patrón de codificación común utilizado en AML era ejecutar un AML (con parámetros) dentro de otro AML.

Una aplicación que estoy desarrollando actualmente se beneficiaría de poder ejecutar un script Python (con parámetros) dentro de otro script Python.

Sin embargo, esto no parece ser del todo sencillo.

Usando ArcGIS 10, estoy experimentando con envolver el script Python "interno" en una herramienta ArcGIS que tiene los parámetros. Pensé que sería simple hacer que el script Python "externo" use arcpy.ImportToolbox para importar la caja de herramientas y luego ejecutar las herramientas dentro de ella. Sin embargo, en las pruebas realizadas hasta ahora, todos mis intentos de ejecutar la herramienta "interna" desde el script "externo" parecen omitir simplemente la herramienta "interna" (no se produce ningún error).

Aquí hay un código de prueba para tratar de ilustrar mejor lo que estoy tratando de describir.

Mi script testinner.py es:

inputString = arcpy.GetParameterAsText(0)

newFC = "C:\\Temp\\test.gdb\\" + inputString
arcpy.Copy_management("C:\\Temp\\test.gdb\\test",newFC)

Mi script testouter.py es:

import arcpy

inputString1 = arcpy.GetParameterAsText(0)
inputString2 = arcpy.GetParameterAsText(1)

arcpy.ImportToolbox("C:\\Temp\\test.tbx")

arcpy.testinner_test(inputString1)

arcpy.testinner_test(inputString2)

Para testinner.py su herramienta necesita un único parámetro de cadena.

Para testouter.py su herramienta necesita dos parámetros de cadena

Las dos herramientas se colocan en un test.tbx.

Test.gdb solo necesita una única clase de entidad vacía llamada test.

Una vez que haya ensamblado lo anterior, ejecute la herramienta testinner con una cadena como 'abc' ya que su parámetro debería hacer que la clase de entidad 'prueba' se copie en una llamada 'abc' OK.

Pero cuando intenta ejecutar la herramienta Testouter con dos cadenas como 'uvw' y 'xyz' como sus parámetros, la herramienta testinner dentro de testouter.py parece ejecutarse una vez, pero envía ArcMap 10 SP2 en Vista SP2 a un error de aplicación grave cuando tratando de usarlo la segunda vez.

La misma prueba con Windows XP SP3 y ArcGIS Desktop 10 SP2 también produce un error grave de la aplicación en el mismo punto.

PolyGeo
fuente
2
De acuerdo con la respuesta de @ Dan sobre esto ... no piense en los archivos .py solo como "scripts", piense en ellos como módulos que puede reutilizar y reciclar importando las funciones y clases que necesita de esos módulos. Extraiga esos parámetros GP anidados mediante el uso de un script para leer en un conjunto de parámetros y luego llame a las funciones en sus otros módulos según sea necesario. Use el truco if name __ == '__ main ' para hacer que sus módulos sean tanto importantes como aún independientes.
blah238
Tengo el ejemplo de Dan trabajando para generar: C: \ Temp \ Main_program.py ('suma algunos números:', 55) ('suma de cuadrados:', 385) ('hola desde 8:', [1, 2, 3 , 4, 5, 6, 7, 8, 9, 10]) pero estoy luchando por adaptarlo a un ejemplo de ArcPy como di anteriormente. Cualquier ayuda adicional sobre cómo se vería un ejemplo de ArcPy sería muy apreciada.
PolyGeo
Vea la respuesta que agregué: debería ayudar a explicar mejor las cosas en el contexto de su ejemplo.
blah238
Acabo de encontrar una excelente publicación de blog de Jason Pardy que proporciona una plantilla ArcPy que incorpora el patrón de codificación para los módulos de Python en blogs.esri.com/Dev/blogs/geoprocessing/archive/2011/07/21/…
PolyGeo
Este enlace se ha movido desde entonces, y creo que ahora descansa aquí: blogs.esri.com/esri/arcgis/2011/08/04/pythontemplate
ndimhypervol

Respuestas:

15

Aquí está su ejemplo de prueba modificado para importar un módulo de "utilidad" dentro del script principal y llamar a una función usando los parámetros leídos por la herramienta de script:


CopyFeaturesTool.py : herramienta de script que lee parámetros y llama a una función en otro módulo

import CopyFeaturesUtility
import arcpy

inputFC = arcpy.GetParameterAsText(0)
outputFCName = arcpy.GetParameterAsText(1)
CopyFeaturesUtility.copyFeaturesToTempGDB(inputFC, outputFCName)

CopyFeaturesUtility.py - Módulo que tiene una sola función copyFeaturesToTempGDB. Se puede importar o ejecutar de forma independiente. Si se ejecuta de forma independiente, se ejecuta el código debajo if __name__ == '__main__'.

import arcpy
import os

def copyFeaturesToTempGDB(inputFeatures, outputName):
    """Copies the input features to a temporary file geodatabase.
    inputFeatures: The input feature class or layer.
    outputName: The name to give the output feature class."""

    tempGDB = r"c:\temp\test.gdb"
    newFC = os.path.join(tempGDB, outputName)
    arcpy.env.overwriteOutput = True
    arcpy.CopyFeatures_management(inputFeatures, newFC)

if __name__ == '__main__':
    inputFC = r"c:\temp\test.gdb\test"
    outputFCName = "testCopy"
    copyFeaturesToTempGDB(inputFC, outputFCName)

Creo que este enfoque modular será mucho más eficiente y lógico una vez que se haya acostumbrado. La sección Módulos en el tutorial estándar de Python también es un buen recurso para comprender cómo funciona la importación.

Para obtener ejemplos más específicos de arcpy, eche un vistazo a los scripts integrados en su C:\Program Files\ArcGIS\Desktop10.0\ArcToolbox\Scriptscarpeta.

blah238
fuente
13

Puede lograr esto importando un módulo (es decir, un script) en su script principal y llamando a sus funciones. Una demostración simple está contenida en los dos scripts que la acompañan.

    '''
Main_program.py

demonstrates how to import and call functions from another module
'''
import sys
import CallingFunctions

a_list = [1,2,3,4,5,6,7,8,9,10]
print sys.argv[0]
print CallingFunctions.func1(a_list)
print CallingFunctions.func5(a_list)
print CallingFunctions.func8(a_list)

para el programa principal y para las funciones que se llaman

'''
Callingfunctions.py

imported into another program giving it access to the functions
'''

def func1(inputs=None):
  x = sum(inputs)
  return "sum some numbers: ", x
'''
more functions
'''
def func5(inputs=None):
  x_sq = 0
  for x in inputs:
    x_sq += x**2
  return "sum of squares: ", x_sq
'''
more functions
'''
def func8(inputs=None):
  return "hello from 8: ", inputs

'''
more functions
'''
if __name__ == "__main__":
  a_list = [1,2,3,4,5,6,7,8,9,10]
  inputs = "test inputs"
  a_dict = {1:[func1([1,2,3]) ],
            5:[func5([1,2,3])],
            8:[func8("inputs to 8")]}
  needed = [1,5,8]
  for akey in needed:
    if akey in a_list:
      action = a_dict[akey]
      print "\naction: ", action

solo tiene que asegurarse de que el módulo principal y el módulo secundario estén en la misma carpeta. Puede pasar parámetros al módulo secundario fácilmente y si el módulo secundario necesita acceso a arcpy (suponiendo que esté utilizando la versión 10 de arcmap) simplemente pase una referencia al mismo.


fuente
6

Importar y ejecutar una función es la forma más limpia de hacerlo, pero en aras de la exhaustividad, también existe la execfilefunción incorporada ( documentación ) que le permitirá ejecutar un archivo arbitrario en el contexto actual.

Jason Scheirer
fuente
0

El método execfile descrito por @JasonScheirer me permitió reorganizar mi código a continuación y proporciona una solución a mi problema de prueba:

import arcpy

inputString1 = arcpy.GetParameterAsText(0)
inputString2 = arcpy.GetParameterAsText(1)

arcpy.ImportToolbox("H:/Temp/test.tbx")

# Write second Python script to an ASCII file for first parameter & execute it
f = open("H:/Temp/string1.py","w")
f.write('newFC = "H:/Temp/test.gdb/' + inputString1 + '"' + "\n")
f.write('arcpy.Copy_management("H:/Temp/test.gdb/test"' + ',newFC)')
f.close()
execfile("H:/Temp/string1.py")

# Write third Python script to an ASCII file for second parameter & execute it
f = open("H:/Temp/string2.py","w")
f.write('newFC = "H:/Temp/test.gdb/' + inputString2 + '"' + "\n")
f.write('arcpy.Copy_management("H:/Temp/test.gdb/test"' + ',newFC)')
f.close()
execfile("H:/Temp/string2.py")

Sin embargo, esto puede resultar engorroso cuando se aplica a scripts que no son de prueba, que son mucho más largos, por lo que utilicé el trabajo de @ blah238 que avaló el enfoque de @ DanPatterson y obtuve el siguiente código final (prueba) que hace exactamente lo que necesito.

# CopyFeaturesTool.py

import CopyFeaturesUtility
import arcpy
outputFCName = arcpy.GetParameterAsText(0)
outputFCName2 = arcpy.GetParameterAsText(1)

CopyFeaturesUtility.copyFeaturesToTempGDB("C:\\Temp\\test.gdb\\test", outputFCName)
CopyFeaturesUtility.copyFeaturesToTempGDB("C:\\Temp\\test.gdb\\test", outputFCName2)

y

# CopyFeaturesUtility.py

import arcpy
import os

def copyFeaturesToTempGDB(inputFeatures, outputName):
    """Copies the input features to a temporary file geodatabase.
    inputFeatures: The input feature class or layer.
    outputName: The name to give the output feature class."""

    tempGDB = r"C:\Temp\test.gdb"
    newFC = os.path.join(tempGDB, outputName)
    arcpy.env.overwriteOutput = True
    arcpy.Copy_management(inputFeatures, newFC)

if __name__ == '__main__':
    inputFC = r"C:\Temp\test.gdb\test"
    outputFCName = arcpy.GetParameterAsText(0)
    copyFeaturesToTempGDB(inputFC, outputFCName)
PolyGeo
fuente