Uruchamianie skryptów Python (z parametrami) w innym skrypcie Python za pomocą ArcPy?

23

Powszechnym wzorcem kodowania stosowanym w AML było uruchamianie AML (z parametrami) wewnątrz innej AML.

Aplikacja, którą obecnie tworzę, skorzystałaby na możliwości uruchomienia skryptu Python (z parametrami) w innym skrypcie Python.

Nie wydaje się to jednak wcale proste.

Korzystając z ArcGIS 10, eksperymentuję z opakowaniem „wewnętrznego” skryptu Python w narzędzie ArcGIS, które ma parametry. Pomyślałem, że prostą sprawą jest, aby „zewnętrzny” skrypt Pythona używał arcpy.ImportToolbox do importowania zestawu narzędzi, a następnie uruchamiania w nim narzędzi. Jednak podczas testowania do tej pory wszystkie moje próby uruchomienia narzędzia „wewnętrznego” ze skryptu „zewnętrznego” wydają się po prostu pomijać narzędzie „wewnętrzne” (nie pojawia się żaden błąd).

Oto kod testowy, aby lepiej zilustrować to, co próbuję opisać.

Mój skrypt testinner.py to:

inputString = arcpy.GetParameterAsText(0)

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

Mój skrypt testouter.py to:

import arcpy

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

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

arcpy.testinner_test(inputString1)

arcpy.testinner_test(inputString2)

Do testinner.py jego narzędzie potrzebuje pojedynczego parametru String.

Do testouter.py jego narzędzie potrzebuje dwóch parametrów String

Oba narzędzia są umieszczone w pliku test.tbx.

Test.gdb potrzebuje tylko jednej pustej klasy funkcji o nazwie test.

Po skompletowaniu powyższego uruchomienia narzędzia testinner z przekazanym ciągiem takim jak „abc”, ponieważ jego parametr powinien spowodować skopiowanie klasy funkcji „test” do pliku o nazwie „abc” OK.

Ale gdy spróbujesz uruchomić narzędzie testouter z dwoma ciągami, takimi jak „uvw” i „xyz” jako parametrami, narzędzie testinner w testouter.py wydaje się działać raz, ale wysyła ArcMap 10 SP2 na Vista SP2 do poważnego błędu aplikacji, gdy próbuję użyć go po raz drugi.

Ten sam test z użyciem Windows XP SP3 i ArcGIS Desktop 10 SP2 również powoduje poważny błąd aplikacji w tym samym punkcie.

PolyGeo
źródło
2
Korzystając z odpowiedzi @ Dan na ten temat ... nie myśl o plikach .py po prostu jako o „skryptach”, myśl o nich jako o modułach, które można ponownie wykorzystać i poddać recyklingowi, importując potrzebne funkcje i klasy z tych modułów. Wyodrębnij te zagnieżdżone parametry GP za pomocą jednego skryptu, aby odczytać jeden zestaw parametrów, a następnie w razie potrzeby wywołać funkcje w innych modułach. Użyj sztuczki if name __ == '__ main ', aby Twoje moduły były zarówno importowalne, jak i nadal samodzielne.
blah238,
Mam przykład Dana pracujący z danymi wyjściowymi: 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]), ale staram się dostosować go do przykładu ArcPy, jak podałem powyżej. Każda dodatkowa pomoc dotycząca tego, jak wyglądałby przykład ArcPy, byłaby bardzo mile widziana.
PolyGeo
Zobacz dodaną przeze mnie odpowiedź - powinna pomóc lepiej wyjaśnić rzeczy w kontekście Twojego przykładu.
blah238
Właśnie natrafiłem na świetnego posta na blogu Jason Pardy, który zawiera szablon ArcPy zawierający wzorzec kodowania modułów Python na blogs.esri.com/Dev/blogs/geoprocessing/archive/2011/07/21/...
PolyGeo
ten link został przeniesiony i uważam, że teraz znajduje się tutaj: blogs.esri.com/esri/arcgis/2011/08/04/pythontemplate
ndimhypervol

Odpowiedzi:

15

Oto przykład testu zmodyfikowany w celu zaimportowania modułu „narzędziowego” do głównego skryptu i wywołania funkcji przy użyciu parametrów odczytanych przez narzędzie skryptowe:


CopyFeaturesTool.py - Narzędzie skryptowe, które odczytuje parametry i wywołuje funkcję w innym module

import CopyFeaturesUtility
import arcpy

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

CopyFeaturesUtility.py - Moduł posiadający jedną funkcję copyFeaturesToTempGDB. Można go zaimportować lub uruchomić samodzielnie. Jeśli jest uruchamiany autonomicznie, uruchamiany jest kod pod 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)

Myślę, że przekonasz się, że to modułowe podejście jest o wiele bardziej wydajne i logiczne, gdy już się przyzwyczaisz. Sekcja Moduły w standardowym samouczku w języku Python jest również dobrym źródłem informacji o tym, jak działa importowanie.

Aby uzyskać przykłady bardziej specyficzne dla Arcpy, spójrz na wbudowane skrypty w twoim C:\Program Files\ArcGIS\Desktop10.0\ArcToolbox\Scriptsfolderze.

blah238
źródło
13

Możesz to zrobić, importując moduł (tj. Skrypt) do głównego skryptu i wywołując jego funkcje. Proste demo znajduje się w dwóch dołączonych skryptach.

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

dla programu głównego i wywoływanych funkcji

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

musisz tylko upewnić się, że moduł główny i moduł potomny znajdują się w tym samym folderze. Możesz łatwo przekazać parametry do modułu potomnego, a jeśli moduł potomny potrzebuje dostępu do arcpy (zakładając, że używasz wersji 10 arcmap), po prostu przekaż mu odniesienie.


źródło
6

Importowanie i uruchamianie funkcji to czystszy sposób, aby to zrobić, ale dla zapewnienia kompletności istnieje również execfilewbudowana funkcja ( dokumentacja ), która pozwoli ci uruchomić dowolny plik w bieżącym kontekście.

Jason Scheirer
źródło
0

Metoda execfile opisał @JasonScheirer pozwoliło mi zmienić mój kod do dołu i dostarcza rozwiązania mojego problemu z badania:

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

Może to jednak okazać się kłopotliwe, gdy zastosuje się je do skryptów nie testowych, które są znacznie dłuższe, więc skorzystałem z pracy @ blah238, która poparła podejście @ DanPatterson i wymyśliłem następujący końcowy (testowy) kod, który robi dokładnie to, czego potrzebuję.

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

i

# 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
źródło