Executando scripts Python (com parâmetros) em outro script Python com ArcPy?

23

Um padrão de codificação comum usado na AML era executar uma AML (com parâmetros) dentro de outra AML.

Um aplicativo que estou desenvolvendo atualmente se beneficiaria da capacidade de executar um script Python (com parâmetros) dentro de outro script Python.

No entanto, isso não parece ser nada simples.

Usando o ArcGIS 10, estou experimentando envolver o script Python "interno" em uma ferramenta do ArcGIS que possui os parâmetros. Eu pensei que seria uma questão simples fazer com que o script Python "externo" usasse o arcpy.ImportToolbox para importar a caixa de ferramentas e, em seguida, execute as ferramentas nela. No entanto, nos testes realizados até agora, todas as minhas tentativas de executar a ferramenta "interna" a partir do script "externo" parecem simplesmente ignorar a ferramenta "interna" (nenhum erro é gerado).

Aqui está um código de teste para tentar ilustrar melhor o que estou tentando descrever.

Meu script testinner.py é:

inputString = arcpy.GetParameterAsText(0)

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

Meu script testouter.py é:

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, sua ferramenta precisa de um único parâmetro String.

Para testouter.py, sua ferramenta precisa de dois parâmetros String

As duas ferramentas são colocadas em um test.tbx.

O test.gdb precisa apenas de uma única classe de recurso vazia chamada test.

Após a montagem acima, a execução da ferramenta testinner com uma string como 'abc' transmitida como parâmetro deve resultar na cópia da classe de recurso 'test' para uma chamada OK 'abc'.

Mas quando você tenta executar a ferramenta testouter com duas sequências como 'uvw' e 'xyz' como parâmetros, a ferramenta testinner no testouter.py parece funcionar OK uma vez, mas envia o ArcMap 10 SP2 no Vista SP2 para um erro grave de aplicativo quando tentando usá-lo pela segunda vez.

O mesmo teste usando o Windows XP SP3 e o ArcGIS Desktop 10 SP2 também produz um erro grave de aplicativo no mesmo ponto.

PolyGeo
fonte
2
Seguindo a resposta de @ Dan sobre isso ... não pense nos arquivos .py apenas como "scripts", pense neles como módulos que você pode reutilizar e reciclar importando as funções e classes necessárias desses módulos. Abstraia esses parâmetros GP aninhados usando um script para ler em um conjunto de parâmetros e, em seguida, chame funções em seus outros módulos, conforme necessário. Use o truque if name __ == '__ main ' para tornar seus módulos importáveis ​​e ainda utilizáveis.
blah238
Eu tenho o exemplo de Dan trabalhando para produzir: C: \ Temp \ Main_program.py ('sum some numbers:', 55) ('sum of squares:', 385) ('hello from 8:', [1, 2, 3 , 4, 5, 6, 7, 8, 9, 10]), mas estou lutando para adaptá-lo a um exemplo do ArcPy como eu dei acima. Qualquer ajuda adicional sobre como seria um exemplo do ArcPy seria muito apreciada.
PolyGeo
Veja a resposta que eu adicionei - deve ajudar a explicar melhor as coisas no contexto do seu exemplo.
blah238
Acabei de encontrar um excelente post de Jason Pardy que fornece um modelo ArcPy que incorpora o padrão de codificação para módulos Python em blogs.esri.com/Dev/blogs/geoprocessing/archive/2011/07/21/…
PolyGeo
este link se mudou, e eu acredito que repousa aqui agora: blogs.esri.com/esri/arcgis/2011/08/04/pythontemplate
ndimhypervol

Respostas:

15

Aqui está seu exemplo de teste modificado para importar um módulo "utilitário" dentro do script principal e chamar uma função usando os parâmetros lidos pela ferramenta de script:


CopyFeaturesTool.py - Ferramenta de script que lê parâmetros e chama uma função em outro módulo

import CopyFeaturesUtility
import arcpy

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

CopyFeaturesUtility.py - Módulo que possui uma única função copyFeaturesToTempGDB. Pode ser importado ou executado de forma independente. Se for executado de forma independente, o código abaixo if __name__ == '__main__'será executado.

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)

Acho que você achará essa abordagem modular muito mais eficiente e lógica depois de se acostumar. A seção Módulos no tutorial padrão do Python também é um bom recurso para entender como a importação funciona.

Para obter exemplos mais específicos do arco-íris, dê uma olhada nos scripts internos da sua C:\Program Files\ArcGIS\Desktop10.0\ArcToolbox\Scriptspasta.

blah238
fonte
13

Você pode fazer isso importando um módulo (isto é, script) para o script principal e chamando suas funções. Uma demonstração simples está contida nos dois scripts anexos.

    '''
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 o programa principal e para as funções que estão sendo chamadas

'''
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

você só precisa garantir que o módulo principal e o módulo filho estejam na mesma pasta. Você pode passar parâmetros para o módulo filho facilmente e, se o módulo filho precisar acessar o arcpy (supondo que você esteja usando a versão 10 do arcmap), basta passar uma referência a ele.


fonte
6

Importar e executar uma função é a maneira mais limpa de fazê-lo, mas por uma questão de integridade, também existe a execfilefunção interna ( documentação ) que permite executar um arquivo arbitrário no contexto atual.

Jason Scheirer
fonte
0

O método execfile descrito por @JasonScheirer me permitiu reorganizar meu código para isso abaixo e fornece uma solução para o meu problema de teste:

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")

No entanto, isso pode ser complicado quando aplicado a scripts que não são de teste, que são muito mais longos, então usei o trabalho de @ blah238 que endossava a abordagem de @ DanPatterson e surgiu o seguinte código final (teste) que faz exatamente o que eu preciso.

# 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)

e

# 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
fonte