ich argumentieren, dass das, was Sie haben daran gedacht ist nicht eine gute API. Es ist einfacher, robuster und weniger fehleranfällig, dass die Funktion einen bestimmten Typ zurückgibt und der Benutzer die eventuelle Konvertierung übernimmt.
Insbesondere würde ich es vorziehen, die Funktion faul machen einen Generator mit:
def find_obj(ids, objs):
try:
for id in ids:
yield next(o for o in objs if o.id == id)
except TypeError:
# ids not iterable? assume it is a plain id
yield next(o for o in objs if o.id == ids)
Jetzt kann ein Benutzer einfach tun:
list(find_obj(...))
set(find_obj(...))
next(find_obj(...)) # 1 element
und erhalten die Sache, die er will.
Weitere Vorteile:
- Explicit ist besser als implizite: hier die Typumwandlung ist explizit. Bildcode, wo die Anrufe von der Art
find_obj(some_var_defined_elsewhere, objects)
sind Nun, woher wissen Sie, welcher Typ zurückgegeben wird, wenn die Definition der Eingabe nicht in der Nähe ist?
- können Sie einen Typ
X
als Eingabe übergeben und konvertieren Y
ohne Verschwendung Zwischenraum und dabei eine unnötige Umwandlung
- Keine besonderen Fällen erforderlich einzugeben.Der Anrufer kann eine Eingabe zur Verfügung stellen, die nicht die übliche Art und Weise folgt Behälter zu konstruieren (beachten Sie, dass es keine Standardmethode ist es, einen Behälter zu bauen)
Alternative, dass spezielle Fälle der einzige ID Fall:
def find_obj(ids, objs):
try:
return (next(o for o in objs if o.id == id) for id in ids)
except TypeError:
for o in objs:
if o.id == id:
return o
Die oben gibt einen Generator, wenn eine Folge von ids
gegeben und gibt ein einfaches Objekt (anstelle eines 1-Element-Generator), wenn in einem einzigen id
weitergegeben.
Schließlich: meiste Zeit (aber nicht immer) Sequenzen einen Konstruktor, der eine iterable und baut den Behälter mit diesen Elementen annimmt. Dies bedeutet, dass:
type(some_iterable)(something for el in some_iterable)
wird, einen Behälter der gleichen Art, wie some_iterable
erzeugen.
Beachten Sie, dass einige Klassen ein list
anstelle eines generischen iterable erfordern, so dass Sie type(some_iterable)([<as-before>])
und andere Behälter tun verwenden müssten nicht haben so einen Konstruktor. In diesem letzten Fall konnte nur der Aufrufer die Konvertierung durchführen. Die erste Lösung geht das ohne Sonderfälle gut an.
Sie könnten mehr die Funktion verallgemeinern, indem man einen Parameter Hinzufügen der Umwandlung auszuführen:
def find_obj(ids, objs, converter=None):
if converter is None:
converter = type(ids)
try:
return converter(next(o for o in objs if o.id == id) for id in ids)
except TypeError:
return next(o for o in objs if o.id == ids)
auf diese Weise der Anrufer die Konvertierung anpassen kann, wenn er mit fremden Arten zu tun ist.
Ein zusätzlicher Hinweis: in Python wir „Duck Typing“ verwenden, das heißt verwenden nur das Objekt, als ob es den richtigen Typ war und wenn es eine Ausnahme Rückfall wirft andere Sachen zu tun. In einigen Fällen ist es einfacher, zunächst bestimmter Operationen für die Unterstützung zu überprüfen, dass Fälle, die Sie isinstance
mit den abstrakten Basisklassen in collections.abc
gefunden verwenden könnten, um zu sehen, ob ein Objekt Iterable
ist, ein Sequence
, ein Mapping
usw.
Was Rückgabetyp (idlist) ([next (o für o in objs wenn id == o.id) für id in idlist))? –
'type (idlist)' gibt Ihnen den Typ des Objekts, das an die Funktion übergeben wird. Nun müssen Sie diesen Typkonstruktor aufrufen ['t = type (idlist); t (input_arguments) '] und alle Eckfälle herausfinden (wie nicht Sequenz, aber Objekt etc.) –