0

Noch etwas perplex von Python und es ist magische funktionale Programmierung, so tendiere ich dazu, Code zu schreiben, der eher auf das Java-Paradigma der Programmierung im Gegensatz zu Idiomatic Python ist.Wie Python-Sammlungen für benutzerdefinierte Klassen verwenden

Meine Frage ist etwas im Zusammenhang mit: How do I make a custom class a collection in Python

Der einzige Unterschied ist, ich verschachtelte Objekte haben (unter Verwendung von Zusammensetzung). Das Objekt VirtualPage besteht aus einer Liste von Objekten PhysicalPage. Ich habe eine Funktion, die eine Liste von PhyscialPage Objekte nehmen und alle Details in ein einzelnes benanntes Tupel zusammenfügen kann, das ich PageBoundary anruft. Im Wesentlichen ist es eine Serialisierungsfunktion, die ein Tupel ausgeben kann, das aus einem Ganzzahlbereich besteht, der die physikalische Seite und die Zeilennummer auf der Seite darstellt. Daraus kann ich leicht zu sortieren und zu bestellen VirtualPages untereinander (das ist die Idee, zumindest):

PageBoundary = collections.namedtuple('PageBoundary', 'begin end') 

Ich habe auch eine Funktion, die einen PageBoundary namedtuple und Deserialisieren oder erweitern Sie den Tupels in eine nehmen kann Liste der PhysicalPages. Es ist vorzuziehen, dass diese beiden Datenspeicherklassen sich nicht ändern, da dadurch der Downstream-Code unterbrochen wird.

Hier ist ein Ausschnitt meiner benutzerdefinierten Python2.7-Klasse. Es besteht aus vielen Dingen ist eine Liste, die eine das Objekt PhysicalPage enthält:

class VirtualPage(object): 
    def __init__(self, _physical_pages=list()): 
     self.physcial_pages = _physcial_pages 


class PhysicalPage(object): 
    # class variables: number of digits each attribute gets 
    _PAGE_PAD, _LINE_PAD = 10, 12 

    def __init__(self, _page_num=-1): 
     self.page_num = _page_num 
     self.begin_line_num = -1 
     self.end_line_num = -1 

    def get_cannonical_begin(self): 
     return int(''.join([str(self.page_num).zfill(PhysicalPage._PAGE_PAD), 
        str(tmp_line_num).zfill(PhysicalPage._LINE_PAD) ])) 

    def get_cannonical_end(self): 
     pass # see get_cannonical_begin() implementation 

    def get_canonical_page_boundaries(self): 
     return PageBoundary(self.get_canonical_begin(), self.get_canonical_end()) 

ich einige Templat-Sammlung nutzen möchten (aus dem Python-Sammlungen Modul) leicht zu sortieren und zu vergleichen, als Liste oder einen Satz von VirtualPage Klassen. Weitere Informationen zum Layout meiner Datenspeicherklassen: VirtualPage und PhysicalPage.

Gegeben entweder eine Folge von VirtualPages oder wie im folgenden Beispiel:

vp_1 = VirtualPage(list_of_physical_pages) 
vp_1_copy = VirtualPage(list_of_physical_pages) 
vp_2 = VirtualPage(list_of_other_physical_pages) 

Ich möchte Fragen leicht beantworten wie folgt aus:

>>> vp_2 in vp_1 
False 
>>> vp_2 < vp_1 
True 
>>> vp_1 == vp_1_copy 
True 

Rechts von der Fledermaus scheint es offensichtlich, dass Die Klasse VirtualPage muss get_cannonical_page_boundaries aufrufen oder die Funktion selbst implementieren. Zumindest sollte es eine Schleife über es PhysicalPage Liste die gewünschten Funktionen zu implementieren (lt() und eq()), so kann ich vergleichen b/w VirtualPages.

1.) Derzeit habe ich Probleme mit der Implementierung einiger Vergleichsfunktionen. Ein großes Hindernis ist, wie man ein Tupel vergleicht. Muss ich meine eigene lt() Funktion, indem Sie eine benutzerdefinierte Klasse erstellen, die irgendeine Art von Sammlung erweitert:

import collections as col 
import functools 

@total_ordering 
class AbstractVirtualPageContainer(col.MutableSet): 

    def __lt__(self, other): 
     '''What type would other be? 
     Make comparison by first normalizing to a comparable type: PageBoundary 
     ''' 
     pass 

2.) Sollte die Vergleichsfunktionsimplementierung stattdessen in der Klasse VirtualPage vorhanden sein?

Ich lehnte mich an irgendeine Art von Set-Datenstruktur, da die Eigenschaften der Daten, die ich modelliere, das Konzept der Eindeutigkeit hat: d.h. physikalische Seitenwerte können sich nicht überlappen und bis zu einem gewissen Grad als verkettete Liste fungieren. Würden auch Setzer- oder Getterfunktionen, die über @ Decorator-Funktionen implementiert werden, hier von Nutzen sein?

+0

Was bedeutet es, 'vp_2 in vp_1' zu sagen? Dass sie mindestens eine physische Seite gemeinsam haben oder alle Seiten in vp_2 sind auch in vp_1? Ähnlich für '<', '==' usw. Sind diese Operationen in Form von physischen Seiten oder PageBoundaries definiert? Ich denke, 'get_cannonical_begin()' kann vereinfacht werden, um 'return self.page_num * 10 ** PhysicalPage._LINE_PAD + tmp_line_num' – RootTwo

+0

Entschuldigen Sie, drücken Sie die Eingabetaste hehe Angenommen, ich beginne mit einem ersten Satz von PhysicalPages in einem Volumen: ** pp_init_set * *. Mein Ziel ist es, ** pp_init_set ** in verschiedene ** VirtualPage ** Objekte zu zerlegen, indem ich ihre Seiten + Zeilenumbrüche in Form von ** PhysicalPages ** definiere. Wenn ich fertig bin, sollte ich eine Liste von einzigartigen ** VirtualPage ** Objekten gesetzt haben. Wenn ich diese Einstellung summiere, sollte ich das Original ** pp_init_set ** bekommen. Dies bedeutet, dass weder Seiten (Löcher) noch überlappende Seiten fehlen. Auch diese Identität gilt: VirtualPage [i] .physical_pages [-1] .end_line_num == VirtuellePage [i + 1] .physical_pages [0] .begin_line_num – Dave

+0

Vielleicht vp_2 in vp_1 ist nicht so nützlich. Ich würde wirklich gerne genug von der Funktion in https://docs.python.org/2/library/collections.html?highlight=collections#collections-abstract-base-classes definieren, um mein eigenes MutableSet von ** VirtualPages zu haben ** was kann ich dann, auf einer hohen Ebene, vp_2 - vp_1, um den Unterschied s/w zwei berühren ** VirtualPages ** zu bekommen. In diesem Fall würde ich erwarten, dass ich eine neue ** VirtualPage ** bekomme, bei der die beiden oben genannten Regeln noch gültig sind. ** PageBoundaries ** ist nur eine einfache Übersetzungsfunktion zur Darstellung einer Liste von ** PhysicalPages **. Danke für die Antwort BTW! – Dave

Antwort

0

Ich denke, Sie möchten etwas wie der Code unten. Nicht getestet; schon gar nicht für Ihre Anwendung getestet oder mit Ihren Daten, YMMV usw.

from collections import namedtuple 

# PageBoundary is a subclass of named tuple with special relational 
# operators. __le__ and __ge__ are left undefined because they don't 
# make sense for this class. 
class PageBoundary(namedtuple('PageBoundary', 'begin end')): 
    # to prevent making an instance dict (See namedtuple docs) 
    __slots__ =() 

    def __lt__(self, other): 
     return self.end < other.begin 

    def __eq__(self, other): 
     # you can put in an assertion if you are concerned the 
     # method might be called with the wrong type object 
     assert isinstance(other, PageBoundary), "Wrong type for other" 

     return self.begin == other.begin and self.end == other.end 

    def __ne__(self, other): 
     return not self == other 

    def __gt__(self, other): 
     return other < self 


class PhysicalPage(object): 
    # class variables: number of digits each attribute gets 
    _PAGE_PAD, _LINE_PAD = 10, 12 

    def __init__(self, page_num): 
     self.page_num = page_num 

     # single leading underscore is 'private' by convention 
     # not enforced by the language 
     self._begin = self.page_num * 10**PhysicalPage._LINE_PAD + tmp_line_num 
     #self._end = ...however you calculate this...     ^not defined yet 

     self.begin_line_num = -1 
     self.end_line_num = -1 

    # this serves the purpose of a `getter`, but looks just like 
    # a normal class member access. used like x = page.begin 
    @property 
    def begin(self): 
     return self._begin 

    @property 
    def end(self): 
     return self._end 

    def __lt__(self, other): 
     assert(isinstance(other, PhysicalPage)) 
     return self._end < other._begin 

    def __eq__(self, other): 
     assert(isinstance(other, PhysicalPage)) 
     return self._begin, self._end == other._begin, other._end 

    def __ne__(self, other): 
     return not self == other 

    def __gt__(self, other): 
     return other < self 


class VirtualPage(object): 
    def __init__(self, physical_pages=None): 
     self.physcial_pages = sorted(physcial_pages) if physical_pages else [] 

    def __lt__(self, other): 
     if self.physical_pages and other.physical_pages: 
      return self.physical_pages[-1].end < other.physical_pages[0].begin 

     else: 
      raise ValueError 

    def __eq__(self, other): 
     if self.physical_pages and other.physical_pages: 
      return self.physical_pages == other.physical_pages 

     else: 
      raise ValueError 

    def __gt__(self, other): 
     return other < self 

Und ein paar Beobachtungen:

Obwohl es nicht so etwas wie „private“ Mitglieder in Python-Klassen ist, ist es eine Konvention einen Variablennamen mit einem einzelnen Unterstrich, _, zu beginnen, um anzuzeigen, dass es nicht Teil der öffentlichen Schnittstelle der Klasse/des Moduls/usw. ist. So, die Methodenparameter von öffentlichen Methoden mit einem '_' zu benennen, scheint nicht korrekt zu sein. zB def __init__(self, _page_num=-1).

Python verwendet im Allgemeinen keine Setter/Getter; Verwenden Sie einfach die Attribute direkt. Wenn Attributwerte berechnet werden müssen oder eine andere Verarbeitung erforderlich ist, verwenden Sie den Dekorator (wie oben für PhysicalPage.begin() gezeigt).

Es ist im Allgemeinen keine gute Idee, ein Standardfunktionsargument mit einem veränderbaren Objekt zu initialisieren. def __init__(self, physical_pages=list()) initialisiert physical_pages nicht jedes Mal mit einer neuen leeren Liste; Stattdessen verwendet es jedes Mal dieselbe Liste. Wenn die Liste geändert wird, werden physikalische_Seiten beim nächsten Funktionsaufruf mit der geänderten Liste initialisiert. Eine Alternative finden Sie unter VirtualPages-Initialisierer.

+0

Danke! Ich kann definitiv viele dieser Ideen verwenden. Und auch danke für das Standardargument und veränderbare Objekte. Klingt nach einer wirklich guten Möglichkeit, sich in den Fuß zu schießen und es nicht zu wissen. Schätzen Sie Ihre Zeit! – Dave