Python ішіндегі «c» блогының ішіне кіру қауіпсіз бе (және неге)?

Сапарлар мен жинау қорларының тіркесімі кейбір болжанбаған (немесе интуитивті емес) салдары бар сияқты.

Басты мәселе мына сияқты жұмыс істейді:

 def coroutine(): with open(path, 'r') as fh: for line in fh: yield line 

Ол не істеп жатыр. (Сіз оны тексере аласыз!)

Неғұрлым баламалы нәрсе болуы керек, мұнда сіз қордың соңында ресурстың босатылуына кепілдік беретін боласыз. Сорутиналар үзілісте үзіліс жасай алады және орындауды блокпен жалғастыра алады, сондықтан да қақтығыс қалай шешіледі?

Мысалы, егер файлды әлі де қайтарылмаған болса, файлды ішкі және сыртында оқып / жазумен ашсаңыз:

 def coroutine(): with open('test.txt', 'rw+') as fh: for line in fh: yield line a = coroutine() assert a.next() # Open the filehandle inside the coroutine first. with open('test.txt', 'rw+') as fh: # Then open it outside. for line in fh: print 'Outside coroutine: %r' % repr(line) assert a.next() # Can we still use it? 

Жаңарту

Алдыңғы мысалда мен жазуды құлыптауға арналған файлдармен бәсекеге түсетінмін, бірақ көптеген операциялық жүйелер әр процестің файлдық өңдегіштерін бөлгендіктен, ешқандай бекітулер болмайды. (Мысалы, Kudos @Miles мысалды көрсете алмайды). Міне, менің нақты мысалға келтірілген мысал:

 import threading lock = threading.Lock() def coroutine(): with lock: yield 'spam' yield 'eggs' generator = coroutine() assert generator.next() with lock: # Deadlock! print 'Outside the coroutine got the lock' assert generator.next() 
38
26 марта '09 в 12:24 2009-03-26 12:24 cdleary тағайындалды 26 наурыз '09 12:24 2009-03-26 12:24
@ 4 жауаптар

Сізге қандай да бір қақтығыстың сұранысын және мысалға қатысты мәселе туралы түсінбеймін: сол файлдың екі тәуелсіз дескрипторы болу керемет.

Мен сіздердің генераторларға жаңа close () әдісі бар екеніне жауап ретінде үйренгенімді білмеген бір нәрсе:

close() жаңа GeneratorExit ішінде Итерацияны аяқтау үшін генератордың Exit, GeneratorExit көтереді. Бұл ерекшелікті алғаннан кейін генератор коды GeneratorExit немесе StopIteration көтеруі керек.

close() генераторы қоқыс жинаған кезде шақырылады, демек, генератор коды генератор жойылғанша жұмыс істеуге мүмкіндік береді. Бұл соңғы мүмкіндік қазір try...finally дегенді білдіреді try...finally генераторлардағы мәлімдемелер жұмыс істей алады; енді finally ереже әрқашан жүгіруге мүмкіндік береді. Бұл тілдің кішігірім маңыздылығына ұқсайды, бірақ генераторларды пайдалану және try...finally .

http://docs.python.org/whatsnew/2.5.html#pep-342-new-generator-features

Осылайша, жағдай генераторда қолданылған кезде өңделеді, бірақ ортасында көрсетіледі, бірақ ешқашан қайтарылмайды - __exit__ контекстті менеджер әдісі генератор қоқыс __exit__ шақырылады.


Өңдеу

Файл сипаттау мәселесіне қатысты: кейде POSIX сияқты емес платформалар бар екенін ұмытып кетемін :)

Кілттерге келетін болсақ, Рафаэль Доугрид басын шырпымен ұрып тастайды деп ойлаймын: «Сізге генератор ресурстары бар кез-келген басқа нысан сияқты дәл білу керек.» Мен мұнда өте пайдалы, себебі бұл функция сол қателіктерден зардап шегеді:

 def coroutine(): lock.acquire() yield 'spam' yield 'eggs' lock.release() generator = coroutine() generator.next() lock.acquire() # whoops! 
20
26 марта '09 в 13:12 2009-03-26 13:12 26 наурызда сағат 13:12 Майлзға жауап берді. 2009-03-26 13:12

Мен нақты қақтығыс бар деп ойлаймын. Сізге генератор ресурстарды қамтитын кез келген басқа нысанға ұқсас екендігін білуіңіз керек, сондықтан жасау үшін жауапкершілік жасаушы тарапынан қамтамасыз етілуге ​​тиіс (және объектіде сақталған ресурстармен қақтығыстар / қоқыстардан аулақ болыңыз). Мұнда көретін жалғыз (шағын) мәселе генераторлар контекстті басқару протоколын (кем дегенде Python 2.5) іске асырмайды, сондықтан сіз жай ғана мүмкін емес:

 with coroutine() as cr: doSomething(cr) 

бірақ оның орнына сізге қажет:

border=0
 cr = coroutine() try: doSomething(cr) finally: cr.close() 

Қалай болғанда да, қоқыс жинағыш close() , бірақ жаман тәжірибе ресурстарды босату үшін қолданылады.

8
26 марта '09 в 13:44 2009-03-26 13:44 жауапты Rafał Dowgird 26 наурызда сағат 13: 44-да келтіреді . 2009-03-26 13:44

yield ерікті кодты орындай алатынынан, кірістіліктің мәлімдемесін құлыптауды қорқатын болар едім. Осындай әсерді басқа әдістермен, соның ішінде алдын ала анықталуы немесе өзгертуі мүмкін әдісті немесе функцияларды шақыруға болады.

Генераторлар әрдайым (әрдайым дерлік) «жабылған» немесе жабық close() , не жай ғана қоқыс жинау арқылы нақты қоңыраумен. Генераторды жабу GeneratorExit ішінде жасайды және, демек, соңғы сөйлемдерді тазалау туралы мәлімдемелермен және т.б. StopIteration алып тастай аласыз, бірақ функцияны (мысалы, StopIteration алып тастаңыз) StopIteration немесе шығарып тастау керек. Сіз қоқысты жинаушыға жазылғандай жағдайларда генераторды өшіру үшін сенімсіздік тәжірибесі болуы мүмкін, себебі ол сізден кейінірек болуы мүмкін, ал егер біреу sys._exit () шақырса, тазалау мүлдем болмауы мүмкін.

1
26 марта '09 в 13:44 2009-03-26 13:44 жауапты Даг 26 наурыз '13 күні 13: 44-да жауап береді. 2009-03-26 13:44

Мен осылай жұмыс істемек деп ойладым. Ия, блок аяқталмайынша ресурстарды босатпайды, сондықтан, осы мағынада, ресурс лексикалық инвестициялардан қашып кетті. Алайда, бұл блокты блокта бірдей ресурсты пайдалануға тырысты функцияны шақырудан өзгеше емес - блок әлі де қандай да бір себептермен аяқталмаған болса сізге ештеңе көмектеспейді. Бұл генераторлар үшін өте маңызды емес.

Ойлануға болатын нәрсе - бұл генератор ешқашан қалпына келмейтін мінез-құлық. Мен блокпен finally блок ретінде жұмыс істейтін болады және __exit__ бөлігін аяқтайды деп __exit__ , бірақ бұл іс жүзінде көрінбейді.

0
26 марта '09 в 13:19 2009-03-26 13:19 26 наурызда сағат 13:19 Брайанға жауап берді. 2009-03-26 13:19

тегтеріне қатысты басқа сұрақтар немесе сұрақ қойыңыз