Verwenden Sie dieses Mal PEP 380, um "Ausbeute Keine" gleich "Rückgabe" zu machen, und der Rückgabewert sollte ein Nicht-Keine-Wert sein. Wir stellen den Dekorateur vor, um ihn zu überprüfen.
PEP 380 ist eine neue Spezifikation hinzugefügt in Python 3.3, die eine [Ausbeute von] für mehrstufige Generatoren darstellt. Der Hauptzweck ist das Hinzufügen von Syntax, diesmal jedoch, wie viele zusätzlich zum Ertrag aus der Syntax hinzugefügt wurden. Ich habe einen Dekorateur geschrieben, der mit dieser Funktion "Ausbeute keine" gleich "Rückgabe" macht.
Die meisten Leute
Wie ist die Beziehung zwischen dem Äquivalent von "Yield None" zu "Return" und der Überprüfung, ob der Rückgabewert ein Nicht-None-Wert ist?
Ich denke, Sie haben die Frage, also lassen Sie mich Ihnen eine einfache Funktion als Beispiel geben.
python
def get_version(self, config_path=None):
if config_path is None:
config_path = self.get_default_config_path()
config = self.parse_config(config_path)
program_name = config.get("program_name")
match = RE_VERSION.match(program_name)
return match.group("version")
Es ist schwer zu verstehen, da es nur ein Teil ist, aber dieses get_version ()
ruft den Pfad der Einstellungsdatei ab, analysiert die Datei und extrahiert den Versionsteil als regulären Ausdruck aus dem Element"Programmname"
. Die zurückzugebende Funktion.
Ich bin an reguläre Ausdrücke gewöhnt, daher zögere ich nicht, sie zu verwenden. Wenn ich jedoch reguläre Ausdrücke für diese Art der Verarbeitung verwende,
Ein solcher Code ist nicht Pythonic!
Bitte beachten Sie, dass Pythonista-Leute möglicherweise wütend werden.
Nun, dieser Code überprüft den Rückgabewert überhaupt nicht, wie Sie auf einen Blick sehen können.
Das ist also ein Problem
Der folgende Code wird geändert, um den Rückgabewert zu überprüfen, falls dies in der Regel erforderlich ist.
python
def get_version(self, config_path=None):
if config_path is None:
config_path = self.get_default_config_path()
if config_path is None:
return None
config = self.parse_config(config_path)
if config is None:
return None
program_name = config.get("program_name", None)
if program_name is None:
return None
match = RE_PROGRAM_VERSION.match(program_name)
if match is None:
return None
return match.group("version")
Infolgedessen ist der Code voll mit "Wenn xxx Keine ist: Keine zurückgeben", und die Funktion, die 7 Zeilen umfasste, umfasst jetzt 15 Zeilen.
Ich wollte schon immer etwas dagegen tun, deshalb habe ich einen Dekorateur namens "Yield_none_becomes_return ()" geschrieben, der die in PEP 380 hinzugefügten Funktionen verwendet, wie bereits erwähnt.
Unten ist der Code mit "ield_none_becomes_return () ", der auf die erste Version angewendet wird.
python
@yield_none_becomes_return
def get_version(self, config_path=None):
if config_path is None
config_path = yield self.get_default_config_path()
config = yield self.parse_config(config_path)
program_name = yield config.get("program_name")
match = yield RE_VERSION.match(program_name)
return match.group("version")
Der Code, der in der vorherigen Version 15 Zeilen umfasste, besteht jetzt aus 8 Zeilen.
Der einzige Unterschied zur ersten Version besteht in der Hinzufügung von Dekorateuren und "Ertrag", und nichts anderes hat sich geändert.
Der Prozess von "ield_none_becomes_return () "ist einfach. Zum Beispiel
python
config_path = yield self.get_default_config_path()
Wenn der Rückgabewert von "self.get_default_config_path ()" "None" ist, wird "yield None" festgelegt, und sofort "return", wenn es nicht "None" ist, den Wert an "config_path" zurückgeben. Ersetzen Sie die Verarbeitung und setzen Sie sie fort.
Wenn der Dekorateur kein Argument enthält, entspricht dies "return", sodass "None" zurückgegeben wird.
python
@yield_none_becomes_return("")
Durch Angabe eines Arguments wie ist es auch möglich, einen beliebigen Wert zurückzugeben, wenn "Ausbeute keine" erfüllt ist.
Wenn Sie jedoch ein Objekt zurückgeben möchten, das mit callable ()
ausgewertet wird und true ist,
python
@yield_none_becomes_return(value=function)
Bitte beschreiben Sie als. Dies ist aus verschiedenen Gründen eine Einschränkung.
Die einzige andere Sache, die zu beachten ist, ist, dass StopIteration ()
nicht an den Aufrufer der dekorierten Funktion weitergegeben wird.
Die Quelle von "yield_none_becomes_return ()" ist unten angegeben. Bitte teilen Sie uns mit, wenn Sie weitere Probleme finden.
ynbr.py
#!/usr/bin/env python3
# vim:fileencoding=utf-8
# Copyright (c) 2014 Masami HIRATA <[email protected]>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
import sys
if sys.version_info < (3, 3): # pragma: no cover
raise ImportError("Python >= 3.3 is required")
from functools import partial, wraps
from inspect import isgeneratorfunction
DEFAULT = object()
__all__ = ['yield_none_becomes_return']
def yield_none_becomes_return(function=DEFAULT, *, value=DEFAULT):
"""This decorator changes the yield statement to None checker for
avoiding "if xxx is None: return" statements
For example:
# without this decorator:
def get_version(self, config_path=None):
if config_path is None:
config_path = self.get_default_config_path()
if config_path is None:
return ""
config = self.parse_config(config_path)
if config is None:
return ""
program_name = config.get("program_name")
if program_name is None:
return ""
match = RE_PROGRAM_VERSION.match(program_name)
if match is None:
return ""
return match.group("version")
# with this decorator:
@yield_none_becomes_return("")
def get_version(self, config_path=None):
if config_path is None:
config_path = yield self.get_default_config_path()
config = yield self.parse_config(config_path)
program_name = yield config.get("program_name")
match = yield RE_VERSION.match(program_name)
return match.group("version")
"""
if not isgeneratorfunction(function):
if function is DEFAULT:
if value is DEFAULT:
# @yield_none_becomes_return() # CORRECT
value = None
else:
if callable(function):
raise TypeError("@yield_none_becomes_return is used only " +
"for generator functions")
if value is not DEFAULT:
# @yield_none_becomes_return("B", value="C") # WRONG
raise TypeError("yield_none_becomes_return() takes " +
"1 argument but 2 were given.")
# @yield_none_becomes_return("A") # CORRECT
value = function
return partial(yield_none_becomes_return, value=value)
else:
if value is DEFAULT:
value = None
@wraps(function)
def _yield_none_becomes_return(*args, **kwargs):
generator = function(*args, **kwargs)
try:
return_value = next(generator)
while True:
if return_value is not None:
return_value = generator.send(return_value)
else:
return value
except StopIteration as exception:
return exception.value
return _yield_none_becomes_return
Recommended Posts