Posso essere avvisato quando utilizzo accidentalmente la funzione generatore
-
23-08-2019 - |
Domanda
Stavo lavorando con funzioni di generatore e funzioni private di una classe.mi chiedo
- Perché quando si cede (che nel mio caso è stato per sbaglio) in __someFunc questa funzione sembra non essere chiamata dall'interno di __someGenerator.Inoltre, qual è la terminologia che voglio usare quando mi riferisco a questi aspetti della lingua?
- L'interprete Python può avvisare di tali casi?
Di seguito è riportato un frammento di esempio del mio scenario.
class someClass():
def __init__(self):
pass
#Copy and paste mistake where yield ended up in a regular function
def __someFunc(self):
print "hello"
#yield True #if yielding in this function it isn't called
def __someGenerator (self):
for i in range(0, 10):
self.__someFunc()
yield True
yield False
def someMethod(self):
func = self.__someGenerator()
while func.next():
print "next"
sc = someClass()
sc.someMethod()
Mi sono scottato su questo e ho passato un po' di tempo cercando di capire perché una funzione non veniva chiamata.Alla fine ho scoperto che stavo cedendo in una funzione che non volevo svolgere.
Soluzione
Provo a rispondere alla prima delle tue domande.
Una funzione regolare, quando chiamata in questo modo:
val = func()
esegue le sue istruzioni interne finché non termina o a return
viene raggiunta la dichiarazione.Quindi viene assegnato il valore restituito della funzione val
.
Se un compilatore riconosce che la funzione è effettivamente un generatore e non una funzione regolare (lo fa cercando yield
istruzioni all'interno della funzione -- se ce n'è almeno una, è un generatore), lo scenario quando lo si chiama nello stesso modo di sopra ha conseguenze diverse.Alla chiamata func()
, nessun codice all'interno della funzione viene eseguito, e uno speciale <generator>
viene assegnato il valore val
.Poi, la prima volta che chiami val.next()
, le dichiarazioni effettive di func
vengono eseguiti fino a quando a yield
O return
viene incontrato, dopodiché l'esecuzione della funzione si interrompe, viene restituito il valore restituito e il generatore attende un'altra chiamata a val.next()
.
Ecco perché, nel tuo esempio, function __someFunc
non ha scritto "ciao" -- le sue istruzioni non sono state eseguite perché non hai chiamato self.__someFunc().next()
, ma solo self.__someFunc()
.
Sfortunatamente, sono abbastanza sicuro che non esista un meccanismo di avviso integrato per errori di programmazione come il tuo.
Altri suggerimenti
A "generatore" non è tanto una caratteristica del linguaggio, come un nome per le funzioni che "resa". Cedendo è praticamente sempre legale. Non c'è davvero nessun modo per Python per sapere che non hai "significa" a cedere da qualche funzione.
Questa PEP http://www.python.org/dev/peps/pep -0255 / parla di generatori, e possono aiutare a capire meglio lo sfondo.
Sono solidale con la vostra esperienza, ma i compilatori non riesco a capire quello che "significava per loro di fare", solo ciò che in realtà ha detto loro di fare.
Python non sa se si desidera creare un oggetto generatore per la successiva iterazione o chiamare una funzione. Ma python non è il vostro unico strumento per vedere cosa sta succedendo con il codice. Se stai usando un editor o IDE che permette la sintassi personalizzato evidenziazione, si può dire che per dare la parola resa di un colore diverso, o anche uno sfondo luminoso, che vi aiuterà a trovare i vostri errori più rapidamente, almeno. In vim, ad esempio, si potrebbe fare:
:syntax keyword Yield yield
:highlight yield ctermbg=yellow guibg=yellow ctermfg=blue guifg=blue
Questi sono i colori orrendi, tra l'altro. Mi consiglia di scegliere qualcosa di meglio. Un'altra opzione, se il vostro editor o IDE non collaboreranno, è quello di creare una regola personalizzata in un correttore codice come pylint. Un esempio dal tarball fonte di pylint:
from pylint.interfaces import IRawChecker
from pylint.checkers import BaseChecker
class MyRawChecker(BaseChecker):
"""check for line continuations with '\' instead of using triple
quoted string or parenthesis
"""
__implements__ = IRawChecker
name = 'custom_raw'
msgs = {'W9901': ('use \\ for line continuation',
('Used when a \\ is used for a line continuation instead'
' of using triple quoted string or parenthesis.')),
}
options = ()
def process_module(self, stream):
"""process a module
the module's content is accessible via the stream object
"""
for (lineno, line) in enumerate(stream):
if line.rstrip().endswith('\\'):
self.add_message('W9901', line=lineno)
def register(linter):
"""required method to auto register this checker"""
linter.register_checker(MyRawChecker(linter))
Il manuale pylint è disponibile qui: http://www.logilab.org/card/pylint_manual E la documentazione della sintassi di vim è qui: http://www.vim.org/htmldoc/syntax.html
Perché la parola return
è applicabile in entrambe le funzioni del generatore e delle funzioni regolari, non c'è niente che si possa controllare (come cita @Christopher). La parola chiave return
in un generatore indica che un'eccezione StopIteration dovrebbe essere aumentata.
Se si tenta di return
con un valore all'interno di un generatore (che non ha senso, dal momento che return
significa solo "stop iterazione"), il compilatore si lamenterà a tempo di compilazione - questo può prendere un po 'di copia e errori -paste:
>>> def foo():
... yield 12
... return 15
...
File "<stdin>", line 3
SyntaxError: 'return' with argument inside generator
Io personalmente sconsiglio solo copia e incolla di programmazione. : -)
Dal PEP:
Si noti che il ritorno significa "ho finito, e non hanno nulla interessante ritorno", sia per le funzioni del generatore e funzioni non-generatore.
Lo facciamo.
I generatori hanno nomi con "generare" o "gen" nel loro nome. Avrà una dichiarazione resa nel corpo. Abbastanza facile da controllare visivamente, dal momento che nessun metodo è molto più di 20 righe di codice.
Altri metodi non hanno "gen" nel loro nome.
Inoltre, non lo facciamo ogni uso __
(doppia sottolineatura) nomi in nessun caso. 32.000 righe di codice. nomi non __
.
La funzione metodo "generatore vs non-generatore" è interamente una questione di progettazione. Che cosa ha fatto il programmatore "intendere" accada. Il compilatore non può facilmente convalidare il vostro intento, si può convalidare solo quello che effettivamente digitato.