Usunąć kursor używany w SearchCursor w rozumieniu słownika?

12

Jeśli najlepiej jest otwierać kursory za pomocą instrukcji with, aby upewnić się, że zostało ono usunięte, tak:

with arcpy.da.UpdateCursor(fc,fields) as cursor:

Następnie, jeśli kursor jest używany jako iterowalny w takim rozumieniu:

d = {k:v for (k,v) in arcpy.da.SearchCursor(fc,fields)}

Czy konieczne jest usunięcie kursora po użyciu go w zrozumieniu?

J. Flann
źródło
1
Świetne pytanie. Czy próbujesz obsłużyć blokady schematu? Istnieje kilka wczesnych (głównie przestarzałych) postów na podobny temat, chociaż nie mogę znaleźć ostatecznego źródła na temat nowych dakursorów: sgillies.net/2011/02/01/get-with-it.html i help.arcgis.com/ pl / arcgisdesktop / 10.0 / help / index.html # //… . W szczególności spójrz na komentarze @JasonScheirer na dole pierwszego linku.
Aaron

Odpowiedzi:

13

Pytanie, czy jest to absolutnie konieczne, jest złym pytaniem. Pytanie brzmi, czy to dobry pomysł.

Z reguły w programowaniu należy unikać robienia dziwnych rzeczy i używać najlepszego narzędzia do pracy . Jeśli coś ma jawny sposób na uwalnianie zasobów, po prostu wyraź wyraźne wydanie i gotowe:

with arcpy.da.UpdateCursor(fc,fields) as cursor:
    d = {k: v for (k,v) in cursor}

To, czego możesz nie być świadomy, to fakt, że withklauzula faktycznie wywołuje dodatkową logikę. withKlauzula wymaga menedżera kontekstowe, które musi posiadać __enter__(wywoływana gdy blok jest wpisany) i __exit__(wywoływana gdy blok jest zakończony) metody. W szczególności __exit__metoda jest wywoływana niezależnie od tego, czy wystąpił wyjątek, dzięki czemu program zawsze zwalnia zasób nawet w przypadku błędu. Daje to Twojemu kodowi wyraźną dokumentację, kiedy zasób jest nabywany i kiedy jest zwalniany, i zapewnia, że ​​zasób może zostać zwolniony jak najszybciej.

Z drugiej strony, nie można tak naprawdę polegać na środowisku uruchomieniowym, aby magicznie zamknąć je natychmiast. Jest tak, ponieważ sposób, w jaki się zamyka, polega na wywołaniu destruktora obiektu, co może, ale nie musi, nastąpić natychmiast. Python nie daje żadnych gwarancji, kiedy wywoływany jest destruktor, tylko że ostatecznie będzie, kiedy obiekt zostanie wyrzucony. (Zobacz tutaj .) Obecnie Python jest zaimplementowany tak, że dzieje się tak szybko, jak tylko nie ma już odwołania do obiektu. Ale łatwo jest przypadkowo propagować odniesienia do obiektu, a środowisko wykonawcze Pythona może się zmienić.

Weź również pod uwagę długoterminową konserwację. Nie ma długoterminowe odniesienie do niej teraz, ale co się stanie w ciągu 6 miesięcy, gdy trzeba zmodyfikować kod tak, że nie jest odniesienie? Co jeśli ktoś to zrobi? Osoba dokonująca zmiany może nie myśleć o przejściu na withblok, ponieważ już go nie ma. Spraw, by sprzątanie zasobów stało się nawykiem , a będziesz miał z tym o wiele mniej problemów.

Czy naprawdę chcesz powiązać swój kod ze szczegółami implementacji odśmiecania? Czy chcesz stale zastanawiać się, czy przypadkiem nie propagujesz referencji za pośrednictwem wyjątku? Nie, ty nie. Wyobraź sobie, że tak się stało, gdy skrypt został wywołany w ArcMap. Użytkownik byłby zmuszony zamknąć cały proces tylko w celu zwolnienia pliku. Więc nie stawiaj się w tej pozycji. Zwolnij zasób jawnie. Zapisanie jednej linii kodu nie jest warte ryzyka problemów, które może powodować. Menedżery kontekstu są standardowym mechanizmem pozyskiwania i zwalniania zasobów w Pythonie i robią to bardzo dobrze.

Najważniejsze jest to, że nieudostępnianie go jawnie jest złym pomysłem.

Zakłada to oczywiście, że kod ma pewne możliwości wpływania na kogoś innego, na przykład umieszczenie go w skrypcie, który ktoś będzie musiał uruchomić lub utrzymywać, lub może opóźnić dostarczenie pracy, jeśli musisz całkowicie zamknąć ArcMap, ponieważ nie mogę zapisać twoich zmian. Jeśli jesteś jedynym, który będzie miał wpływ na problem, to na wszelki wypadek, uciekaj w obliczu dobrych praktyk, jak chcesz.

jpmc26
źródło
3

Nie, nie trzeba usuwać cursorpo użyciu go w zrozumieniu. A cursorjest instancją klasy, która jest obiektem (wszystko w Pythonie jest obiektem). Każda sesja Pythona namespacezawiera odwołania do wszystkich obiektów w sesji - pomyśl o tym jak o słowniku, w którym klucze są odwołaniami do każdego obiektu, a wartościami są same obiekty. Kiedy „liczba referencji” - liczba kluczy odnoszących się do tego obiektu - spada do zera, obiekt jest usuwany, a pamięć ponownie przydzielana . Kiedy używasz cursorw rozumieniu, nie ma odniesienia do tego obiektu w przestrzeni nazw. Po zakończeniu zrozumienia obiekt zostanie usunięty.

W przestrzeni nazw nie ma wpisu i dlatego nie trzeba niczego usuwać. ESRI ilustruje również tę składnię w przykładzie 2 tutaj .

W celu dalszego wyjaśnienia, jeśli uruchomisz:

>>> import arcpy
>>> f = r'C:\Workspace\study_area.shp'
>>> a = arcpy.da.SearchCursor(f, ['*'])

Zobaczysz plik .lock pojawi się w katalogu (sprawdź eksplorator plików). Odniesieniem do kursora jest to a, co spowoduje, że cursor(a zatem i blokada) pozostanie do momentu ausunięcia. Kiedy więc uruchomisz:

>>> del(a)

Wpis w przestrzeni nazw zostanie usunięty, a zamek zwolni się (plik .lock zniknie). Jeśli uruchomisz:

>>> t = [i for i in arcpy.da.SearchCursor(f, ['*'])]

Plik blokady albo nie zobaczysz, albo zniknie po zakończeniu wykonywania polecenia. Bez wpisu w przestrzeni nazw cursornie jest trwały. todnosi się do właśnie utworzonej listy, a nie cursordo jej utworzenia.

Podsumowując, musisz się martwić tylko o usunięcie, cursorsgdy mają odwołanie w przestrzeni nazw (tj. Kiedy przypisałeś je do zmiennej, jak aw powyższym przykładzie).

Chris
źródło
2
Jest to bardzo słaba praktyka programowania. Jeśli coś ma wyraźny sposób na uwolnienie zasobów, użyj tego .
jpmc26
@ jpmc26, Która część to „bardzo słaba praktyka programowania”? Zrozumienie w ogóle? Czy tylko wtedy, gdy iterowalność jest tworzona w ramach zrozumienia? Pomyślałem, że jednym silnym argumentem za tym ostatnim jest to, że natychmiast uwalnia on zasób.
Tom
@Tom Nie zwalnia zasobów bezpośrednio. Rozumienia są fantastycznymi narzędziami, a tworzenie w nich normalnych iteracji jest całkowicie normalne. Złe jest to, że obiekty kursora uzyskują blokady plików i nie ma ich jawnego zwolnienia. Zobacz moją odpowiedź po więcej szczegółów.
jpmc26,
2

Nie można utworzyć kursorów aktualizacji i wstawiania dla tabeli lub klasy obiektów, jeśli dla tego zestawu danych istnieje blokada wyłączna. Funkcje UpdateCursor lub InsertCursor zawodzą z powodu wyłącznej blokady zestawu danych. Jeśli te funkcje z powodzeniem utworzą kursor, zastosują wyłączną blokadę zestawu danych, aby dwa skrypty nie mogły utworzyć aktualizacji ani wstawić kursora do tego samego zestawu danych.

W Pythonie blokada trwa do momentu zwolnienia kursora. W przeciwnym razie wszystkie inne aplikacje lub skrypty mogłyby niepotrzebnie uniemożliwić dostęp do zestawu danych. Kursor można zwolnić w jeden z następujących sposobów:

Umieszczenie kursora wewnątrz instrukcji with, co zagwarantuje zwolnienie blokad niezależnie od tego, czy kursor zostanie pomyślnie ukończony;

Wywołanie reset () na kursorze;

Zakończenie kursora;

Jawne usuwanie kursora za pomocą instrukcji del Pythona - ESRI

Blokowanie za pomocą kursorów arcpy.da jest prawie takie samo jak blokowanie za pomocą oryginalnych kursorów arcpy.da.

Po przetestowaniu kodu i, jak zauważył Gberard, po zakończeniu czytania nie ma odniesienia do kursora.
Ponadto nie ma żadnych blokad klasy obiektów po zakończeniu zrozumienia.

jbalk
źródło
1
Kasujesz co? Po zakończeniu rozumienia nie ma odniesienia do obiektu kursora, więc teoretycznie powinien on zostać zamknięty. To, czy implementacja ESRI zachowuje się tak, jak można się spodziewać, to kolejne pytanie i nie sądzę, że doktorzy tak naprawdę na to odpowiadają.
mikewatt