Неэффективно ли ставить сложные оценки в состояние l oop? - PullRequest
1 голос
/ 03 марта 2020

Я пишу программу, в которой мне нужно перебрать все строки файла. Довольно стандартные вещи. Меня беспокоит то, что, поскольку я хочу убрать символ новой строки из каждой строки, я делаю что-то вроде этого:

for line in map(lambda s: s.strip("\n"), file_input.readlines()):
    do_something(line)

Оценивает ли Python каждый раз, когда значение map(lambda s: s.strip("\n"), file_input.readlines())? У меня такое ощущение, что Python умнее этого, но любое подтверждение и / или ссылка были бы очень полезны для меня!


Я также предполагаю, что ответ также применим к пониманию списка , для чего-то вроде:

results = [x for x in list(set(self.database.values())) if x.startswith(text)]

, где, я надеюсь, он не вычисляется list(set(self.database.values())) несколько раз.

Ответы [ 2 ]

2 голосов
/ 03 марта 2020

Давайте повысим уровни эффективности:

Наиболее эффективным (если не запрещено иное) было бы заранее удалить "\n" -инстанции (с использованием интеллектуальных и эффективных инструментов O / S) и затем обработать "rest" файлового ввода-вывода (где python внутренне, по определению, снова добавляет "\ n", как только используется в aFileINPUT -тераторе, как отмечено в документации, независимо от os.filesep == { "\n" | "\r\n" | "\r" | ... }, которое фактически использовалось для "строкового" разделения шаг на входном потоке итератора).


Давайте измерим Уровни эффективности - путем декодирования фактический поток операций:

при использовании map( lambda ):

############################################################# EFFICIENCY LIMITS :
#                                           - pure-[SERIAL]
#                                           - local-GIL-lock
#                                           - local-CPU
#                                           - local-RAM-I/O :

>>> def a_map_lambda_loop( aFileINPUT ):
...     for line in map( lambda s: s.strip( "\n" ), aFileINPUT ):
...         do_something( line )

>>> dis.dis( a_map_lambda_loop )
  2           0 SETUP_LOOP              36 (to 39)
              3 LOAD_GLOBAL              0 (map)
              6 LOAD_CONST               1 (<code object <lambda> at 0x7ff8fee7b930, file "<stdin>", line 2>)
              9 MAKE_FUNCTION            0
             12 LOAD_FAST                0 (aFileINPUT)
             15 CALL_FUNCTION            2
             18 GET_ITER            
        >>   19 FOR_ITER                16 (to 38)
             22 STORE_FAST               1 (line)

  3          25 LOAD_GLOBAL              1 (do_something)
             28 LOAD_FAST                1 (line)
             31 CALL_FUNCTION            1
             34 POP_TOP             
             35 JUMP_ABSOLUTE           19
        >>   38 POP_BLOCK           
        >>   39 LOAD_CONST               0 (None)
             42 RETURN_VALUE        

при использовании @ chepner -prooted loop:

############################################################# EFFICIENCY LIMITS :
#                                           - pure-[SERIAL]
#                                           - local-GIL-lock
#                                           - local-CPU
#                                           - local-RAM-I/O :

>>> def a_loop_runner( aFileINPUT ):
...     for line in aFileINPUT:
...         line = line.strip( "\n" )
...         do_something( line )

>>> dis.dis( a_loop_runner )
  2           0 SETUP_LOOP              39 (to 42)
              3 LOAD_FAST                0 (aFileINPUT)
              6 GET_ITER            
        >>    7 FOR_ITER                31 (to 41)
             10 STORE_FAST               1 (line)

  3          13 LOAD_FAST                1 (line)
             16 LOAD_ATTR                0 (strip)
             19 LOAD_CONST               1 ('\n')
             22 CALL_FUNCTION            1
             25 STORE_FAST               1 (line)

  4          28 LOAD_GLOBAL              1 (do_something)
             31 LOAD_FAST                1 (line)
             34 CALL_FUNCTION            1
             37 POP_TOP             
             38 JUMP_ABSOLUTE            7
        >>   41 POP_BLOCK           
        >>   42 LOAD_CONST               0 (None)
             45 RETURN_VALUE        

При использовании methodcaller():

############################################################# EFFICIENCY LIMITS :
#                                           - pure-[SERIAL]
#                                           - local-GIL-lock
#                                           - local-CPU
#                                           - local-RAM-I/O :

>>> def a_methodcaller_loop( aFileINPUT ):
...     for line in map( methodcaller( "strip", "\n" ), aFileINPUT ):
...         do_something( line )

>>> dis.dis( a_methodcaller_loop )
  2           0 SETUP_LOOP              42 (to 45)
              3 LOAD_GLOBAL              0 (map)
              6 LOAD_GLOBAL              1 (methodcaller)
              9 LOAD_CONST               1 ('strip')
             12 LOAD_CONST               2 ('\n')
             15 CALL_FUNCTION            2
             18 LOAD_FAST                0 (aFileINPUT)
             21 CALL_FUNCTION            2
             24 GET_ITER            
        >>   25 FOR_ITER                16 (to 44)
             28 STORE_FAST               1 (line)

  3          31 LOAD_GLOBAL              2 (do_something)
             34 LOAD_FAST                1 (line)
             37 CALL_FUNCTION            1
             40 POP_TOP             
             41 JUMP_ABSOLUTE           25
        >>   44 POP_BLOCK           
        >>   45 LOAD_CONST               0 (None)
             48 RETURN_VALUE        

При использовании ALAP .strip() вызов, если .strip() было невозможно отложить в do_something(), а возможно распределить , для получения еще более высокой эффективности o обработка f - {pure- [SERIAL] | just- [CONCURRENT]}, {local | независимый} -GIL-lock (s), {local | распределенный} -CPU, {местный | распределенный} -RAM-I / O:

############################################################# EFFICIENCY LIMITS :
#                                           - pure-[SERIAL] |+ just-[CONCURRENT]
#                                           - local-GIL-lock|+ independent-GIL-lock
#                                           - local-CPU     |+ independent-CPUs
#                                           - local-RAM-I/O |+ independent-RAM-I/O

>>> def ALAP_runner( aFileINPUT ):
...     for line in aFileINPUT:
...         do_something( line.strip( "\n" ) )

>>> dis.dis( ALAP_runner )
  2           0 SETUP_LOOP              33 (to 36)
              3 LOAD_FAST                0 (aFileINPUT)
              6 GET_ITER            
        >>    7 FOR_ITER                25 (to 35)
             10 STORE_FAST               1 (line)

  3          13 LOAD_GLOBAL              0 (do_something)
             16 LOAD_FAST                1 (line)
             19 LOAD_ATTR                1 (strip)
             22 LOAD_CONST               1 ('\n')
             25 CALL_FUNCTION            1
             28 CALL_FUNCTION            1
             31 POP_TOP             
             32 JUMP_ABSOLUTE            7
        >>   35 POP_BLOCK           
        >>   36 LOAD_CONST               0 (None)
             39 RETURN_VALUE        

Более подробная информация сильно зависит от характера do_something() и фактических служебных данных строгое переформулировано Закон Амдаля расходы (см. все дополнительные накладные расходы и добавьте к этому также расходы на связь с процессом (pickle{ .dumps() | .loads() } на основе SER / DES и IP * 1093) * - {channel | network} - задержки связи), если переход от чистого [SERIAL] к просто - [CONCURRENT], то больше, если { process | node } -распределено.


Вкл. list -соединение с if -основанием-распределителем элементов -pure- [SERIAL], local-GIL-lock, local-CPU, local- Оперативный ввод-вывод (крайне незащищенный от несанкционированного выделения памяти конструкторами синтаксиса «на лету» MemoryError аварийно завершает работу:

############################################################# EFFICIENCY LIMITS :
#                                           - pure-[SERIAL]
#                                           - local-GIL-lock
#                                           - local-CPU
#                                           - local-RAM-I/O :

>>> def anOnTheFlyGrowingListComprehension( self ):
...     res = [x for x in list(set(self.database.values())) if x.startswith(text)]

>>> dis.dis( anOnTheFlyGrowingListComprehension )
  2           0 BUILD_LIST               0
              3 LOAD_GLOBAL              0 (list)
              6 LOAD_GLOBAL              1 (set)
              9 LOAD_FAST                0 (self)
             12 LOAD_ATTR                2 (database)
             15 LOAD_ATTR                3 (values)
             18 CALL_FUNCTION            0
             21 CALL_FUNCTION            1
             24 CALL_FUNCTION            1
             27 GET_ITER            
        >>   28 FOR_ITER                27 (to 58)
             31 STORE_FAST               1 (x)
             34 LOAD_FAST                1 (x)
             37 LOAD_ATTR                4 (startswith)
             40 LOAD_GLOBAL              5 (text)
             43 CALL_FUNCTION            1
             46 POP_JUMP_IF_FALSE       28
             49 LOAD_FAST                1 (x)
             52 LIST_APPEND              2
             55 JUMP_ABSOLUTE           28
        >>   58 STORE_FAST               2 (results)
             61 LOAD_CONST               0 (None)
             64 RETURN_VALUE        

или
еще один, более близкий взгляд на сформулированный итератором pure- [SERIAL] "front" -end .strip() -er:

############################################################# EFFICIENCY LIMITS :
#                                           - pure-[SERIAL]
#                                           - local-GIL-lock
#                                           - local-CPU
#                                           - local-RAM-I/O :

>>> dis.dis( '( do_something( line.strip( "\n" ) ) for line in aFileINPUT )' )
          0 STORE_SLICE+0  
          1 SLICE+2        
          2 LOAD_CONST      24431 (24431)
          5 POP_JUMP_IF_TRUE 28015
          8 LOAD_NAME       26740 (26740)
         11 BUILD_MAP       26478
         14 STORE_SLICE+0  
         15 SLICE+2        
         16 IMPORT_NAME     28265 (28265)
         19 LOAD_NAME       29486 (29486)
         22 LOAD_GLOBAL     26994 (26994)
         25 JUMP_IF_TRUE_OR_POP  8232
         28 <34>           
         29 UNARY_POSITIVE 
         30 <34>           
         31 SLICE+2        
         32 STORE_SLICE+1  
         33 SLICE+2        
         34 STORE_SLICE+1  
         35 SLICE+2        
         36 BUILD_TUPLE     29295
         39 SLICE+2        
         40 IMPORT_NAME     28265 (28265)
         43 LOAD_NAME       26912 (26912)
         46 JUMP_FORWARD    24864 (to 24913)
         49 PRINT_EXPR     
         50 BUILD_MAP       25964
         53 PRINT_ITEM_TO  
         54 INPLACE_XOR    
         55 BREAK_LOOP     
         56 EXEC_STMT      
         57 IMPORT_STAR    
         58 SLICE+2        
         59 STORE_SLICE+1  
2 голосов
/ 03 марта 2020

Во-первых, не нужно звонить readlines; Чтение всего файла в память - самая неэффективная часть этой команды.

Однако улучшенная версия

for line in map(lambda s: s.strip("\n"), file_input):
    do_something(line)

не намного более неэффективна, чем

for line in file_input:
    line = line.strip("\n")
    do_something(line)

Это просто более функциональный стиль, хотя есть дополнительный вызов функции: мы вызываем функцию, которая вызывает для нас s.strip("\n"). Мы можем избежать этого с помощью

for line in (x.strip("\n") for x in file_input):
    do_something(line)

, или мы можем использовать класс methodcaller из модуля operator:

for line in map(methodcaller("strip", "\n"), file_input):
    do_something(line)

, который может быть даже немного более эффективным, так как мы ПУ sh больше работы над реализацией Python, вместо того, чтобы писать чистый Python код для того же.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...