2017-12-26 4 views
2

Ich bin mir nicht ganz sicher, wie ich diese Frage stellen soll, geschweige denn, die Antwort zu finden, zum Teil, weil ich bei der Lösung dieses Problems völlig falsch liegen kann.In Python, was ist der beste Weg für mich, wiederverwendbaren Code zu schreiben, der mir Methoden zur Interaktion mit einer Liste von Objekten zur Verfügung stellt?

ich einige Python ich schreibe, und ich habe eine Klasse (Users), die im Grunde verwendet, um eine Anzahl von Objekten eines bestimmten Typs (User), zu instanziiert und dann eine Reihe von Methoden zur Verfügung stellen, um mich mit denen zu helfen, arbeiten Objekte in einer einfacheren Art und Weise. Der Code, den ich habe sieht wie folgt aus:

from defusedxml.ElementTree import parse 

class Users: 

    def __init__(self, path): 

     self.path = path 
     self.users = [] 

     users = parse(path).getroot() 

     for user in users: 
      u = User.user_from_xml(user) 
      self.users.append(u) 

    def __iter__(self): 

     self.i = 0 
     return self 

    def __next__(self): 

     if self.i < len(self.users): 
      self.i += 1 
      return self.users[(self.i - 1)] 
     else: 
      raise StopIteration 

    def get_user_by_id(self, user_id): 
     return next((user for user in self.users if user.id == user_id), None) 

    def search_attribute(self, attribute, value): 

     return [user for user in self.users if 
      getattr(user, attribute, None) != None and 
      value.lower() in str(getattr(user, attribute).lower())] 



class User: 

    def __init__(self, user_id, username, email, first_name, last_name): 

     self.id = int(user_id) 
     self.username = username 
     self.email = email 
     self.first_name = first_name 
     self.last_name = last_name 

    def __repr__(self): 

     if self.first_name == None or self.last_name == None: 
      return "%s (User Id: %s)" % (self.username, self.id) 

     return "%s %s (%s)" % (self.first_name, self.last_name, self.username) 

    @staticmethod 
    def user_from_xml(user): 

     return User(
      user.get("id"), 
      element.find("username").text, 
      element.find("email").text, 
      element.find("firstname").text, 
      element.find("lastname").text 
     ) 

Ich habe eine Reihe von anderen Objekten in XML in ähnlicher Weise gespeichert - zum Beispiel Events. Ich kann die Notwendigkeit sehen, die gleichen Methoden zu verwenden, die in Users definiert werden, mit dem einzigen wirklichen Unterschied, der der Typ des Gegenstandes ist, der in der Liste enthalten wird, die in __init__ hergestellt wird.

Also die Frage ist: Was ist der beste Weg für mich, diesen Code wiederverwendbar zu machen, während die Lesbarkeit, etc. erhalten? Oder vielleicht bin ich völlig auf der falschen Spur.

+0

Willst du sagen, NUR der Konstruktor wechselt zwischen User, Events, etc.? – ubadub

+1

Was auch immer Sie tun, 'User' sollten mit ziemlicher Sicherheit kein Iterator sein, entweder definieren Sie Ihre eigene Klasse oder machen' __iter__' zu einer Generatorfunktion. Oder schau dir deine '__iter__' und' __next__' Implementierungen an, einfach 'def __iter __ (self): return iter (self.users)' –

Antwort

1

Wenn diese Klasse Methoden wirklich identisch sein werden, glaube ich, die einfachste Methode nur eine allgemeinere Klasse machen würde zu Users ersetzen, die einem anderen Klasse (zum Beispiel User oder Event) als Argument in seiner __init__ Methode verwendet. Ihre Klasse könnte so aussehen:

Eine robustere/skalierbare Lösung könnte die Verwendung der Vererbung sein.

Sie können eine abstrakte Basisklasse mit allen Methoden erstellen und dann die __init__-Methode der Basisklasse innerhalb jeder untergeordneten Klasse überschreiben. Ich werde ein Beispiel dafür ziehen:

class AbstractBaseClass(object): 

    def __init__(self, PATH): 
     self.PATH = PATH 
     self.things = [] 

    def methods... 



class Users(AbstractBaseClass): 

    def __init__(self, PATH): 
     super(Users, self).__init__() # calls the parent __init__ method 

     users = parse(PATH).getroot() 

     for user in users: 
      u = User.user_from_xml(user) 
      self.things.append(u) 


    #no need to define methods, as they were already defined in parent class 
    #but you can override methods or add new ones if you want 

Ihre Events Klasse wäre auch AbstractBaseClass erben und damit haben alle die gleichen Methoden der Users. Sie sollten sich über die Vererbung informieren, es ist ein großartiges Werkzeug.

EDIT DEIN KOMMENTAR ZU RICHTEN: dieses Attribut zu erhalten users zurück in auf Ihre Users Klasse

Eigenschaften könnte ein guter Weg sein. Ändern things-_things zu lassen vermuten, dass es privat ist, und erstellen Sie dann eine users Eigenschaft, etwa so:

class Users(AbstractBaseClass): 

    @property 
    def users(self): 
     return self._things 

Auf diese Weise können Users.users und Users._things erhalten aufrufen können.

Wenn Sie wirklich, wirklich über die Wiederverwendung von Code kümmern, könnten Sie sogar in __init__ etwas dynamisch wie folgt tun:

class AbstractBaseClass(object): 

    def __init__(self, PATH): 
     self._things = [] 
     self.PATH = PATH 

     setattr(self, self.__class__.__name__.lower(), self._things) 
     #This creates an attribute that is the lowercase version of the 
     #class name and assigns self._things to it 

Anmerkung: Ich denke, das ist ein wenig hässlich und unnötig ist. Da Sie zwei Attribute haben, die dasselbe sind, kann es dazu führen, dass sich Ihr Objekt in einem inkohärenten Zustand befindet.

Das sagte mir Users.users scheint überflüssig.Ich bin mir des Zusammenhangs Ihres Problems nicht völlig bewusst, aber ich denke, dass ich meine Users Objekte einfach wie die Liste users verhalten würde, aber mit zusätzlichen Methoden (die Sie definiert haben).

In AbstractBaseClass könnten Sie __iter__ definieren die __iter__ des _things Attribut zu sein.

class AbstractBaseClass(object): 

    def __init__(self, PATH): 
     self._things = [] 
     self.PATH = PATH 

    def __iter__(self): 
     return self._things.__iter__() 

    #You might also want this - it lets you do list-like indexing 
    def __getitem__(self, i): 
     return self._things.__getitem__(i) 

ich denke, die oben tut im Wesentlichen, was Sie mit __iter__ und __next__ in Ihrem ursprünglichen Code zu tun wurden, sondern in eine sauberere Art und Weise. Auf diese Weise müssen Sie nicht direkt auf _things oder users zugreifen, um mit einer Liste Ihrer Benutzerobjekte zu spielen. Sie können mit einer Liste von Benutzern durch Ihre Users Klasse spielen, die, wie sein Name scheint, der Zweck der Klasse.

+0

Ich dachte auch über Vererbung nach, aber die Sache, die ich nicht mag, ist diese Ich werde mit 'Users.things' statt mit' Users.users' enden - mit anderen Worten, der Name der Liste beschreibt nicht wirklich, was sie enthält. Dies kann wahrscheinlich durch den Namen der Klasse abgeleitet werden, die die Basisklasse erbt; aber immer noch nicht ideal. Beide sind bessere Lösungen als das, was ich bisher geschrieben habe. –

+1

@JamesLambeth, Ich habe meine Antwort bearbeitet, um auf Ihren Kommentar zu antworten –

+0

Sie machen einen ausgezeichneten Punkt über die Behandlung der 'Benutzer' Klasse eher wie die 'Benutzer' Liste; anstatt "User.things" oder "User.users" anzugeben. Ich mag diesen Ansatz! Vielen Dank. –

Verwandte Themen