0

По-прежнему несколько озадачен python и магическим функциональным программированием, поэтому я, как правило, нахожусь в написании кода, который больше относится к парадигме программирования Java, а не к Idiomatic Python.Как использовать коллекции python для пользовательских классов

Мой вопрос несколько связан с: How do I make a custom class a collection in Python

Единственное различие я вложенных объектов (с использованием композиции). Объект VirtualPage состоит из списка PhysicalPage объектов. У меня есть функция, которая может принять список объектов PhyscialPage и объединить все детали в один и тот же набор. Я звоню PageBoundary. По сути, это функция сериализации, которая может выплевывать кортеж, состоящий из целочисленного диапазона, который представляет физическую страницу и номер строки на странице. Из этого я могу легко сортировать и заказать VirtualPages среди друг друга (это идея, по крайней мере):

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

У меня также есть функция, которая может принять PageBoundary namedtuple и десериализации или расширить кортеж в список Физические данные. Предпочтительно, чтобы эти два класса хранения данных не менялись, так как он нарушал любой последующий код.

Вот фрагмент моего пользовательского класса python2.7. Она состоит из лота вещей один список, который содержит в объект PhysicalPage:

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()) 

Я хотел бы использовать некоторые шаблонный набор (из модуля питона коллекций) легко сортировать и сравнивать в виде списка или набора VirtualPage классов. Также хотелось бы посоветоваться с макетами моих классов хранения данных: VirtualPage и PhysicalPage.

Учитывая либо последовательность VirtualPages или как в примере ниже:

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

Я хочу, чтобы легко ответить на такие вопросы:

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

Сразу кажется очевидным, что класс VirtualPage должен вызывать get_cannonical_page_boundaries или даже реализовать саму функцию. Как минимум он должен цикл по его PhysicalPage список для реализации требуемых функций (л() и экв()), так что я могу сравнить B/W VirtualPages.

1.) В настоящее время я борюсь с реализацией некоторых функций сравнения. Одно большое препятствие - как сравнить кортеж? Должен ли я создать свою собственную функцию л() путем создания пользовательского класса, который расширяет некоторый тип коллекции:

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.) Если реализация функции сравнения существует в классе VirtualPage?

Я склонялся к некоторому типу структуры данных заданий, поскольку свойства данных, которые я моделирую, имеет концепцию уникальности: то есть физические значения страниц не могут пересекаться и в какой-то мере выступать в качестве связанного списка. Также были бы полезны функции setter или getter, реализованные через функции @ decorator?

+0

Что значит сказать 'vp_2 in vp_1'? Что у них есть хотя бы одна общая физическая страница, или все страницы в vp_2 также находятся в vp_1? Аналогично для '<', '==' и т. Д. Являются ли эти операции определенными в терминах физических страниц или PageBoundaries? Я думаю, что 'get_cannonical_begin()' может быть упрощено для 'return self.page_num * 10 ** PhysicalPage._LINE_PAD + tmp_line_num' – RootTwo

+0

Извините, продолжайте нажимать enter hehe Скажем, что я начинаю с начального набора PhysicalPages в томе: ** pp_init_set * *. Моя цель состоит в том, чтобы разбить ** pp_init_set ** на отдельные ** объекты VirtualPage **, определяя их разрывы на странице + строки в виде ** PhysicalPages **. Когда я закончил, мне нужно было установить/список уникальных объектов ** VirtualPage **. Когда я суммирую эту настройку, я должен получить оригинал ** pp_init_set **. Это означает, что отсутствуют ни отсутствующие страницы (отверстия), ни перекрывающиеся страницы. Также это идентификатор содержит: VirtualPage [i] .physical_pages [-1] .end_line_num == VirtualPage [i + 1] .physical_pages [0] .begin_line_num – Dave

+0

Возможно, vp_2 в vp_1 не так уж и полезен. Я действительно хотел бы определить достаточно функции в https://docs.python.org/2/library/collections.html?highlight=collections#collections-abstract-base-classes , чтобы иметь свой собственный MutableSet из ** VirtualPages **, который я могу, на высоком уровне, сделать vp_2 - vp_1, чтобы получить разницу b/w, касающуюся ** VirtualPages **. В этом случае я ожидал бы, что импликация получит новый ** VirtualPage **, где все два правила, о которых я говорил выше, остаются в силе. ** PageBoundaries ** - просто функция перевода, представляющая список ** PhysicalPages **. Спасибо за ответ кстати! – Dave

ответ

0

Я думаю, вы хотите что-то вроде кода ниже. Не испытано; конечно, не тестируются для вашего приложения или с вашими данными, YMMV и т.д.

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 

И несколько наблюдений:

Хотя нет такого понятия, как «частные» члены в классах Python, это соглашение чтобы начать имя переменной с единственным подчеркиванием, _, чтобы указать, что оно не является частью открытого интерфейса класса/модуля/и т. д. Таким образом, параметры метода имен методов public с «_» не кажутся правильными, например, def __init__(self, _page_num=-1).

Python обычно не использует сеттеры/геттеры; просто используйте атрибуты напрямую. Если значения атрибутов необходимо вычислить или требуется какая-либо другая обработка, используйте декоратор @property (как показано выше для PhysicalPage.begin()).

Как правило, не рекомендуется инициализировать аргумент функции по умолчанию с изменяемым объектом. def __init__(self, physical_pages=list()) не инициализирует физические_страницы с новым пустым списком каждый раз; скорее, он использует один и тот же список каждый раз. Если список изменен, при следующем вызове функции физические_файлы будут инициализированы измененным списком. См. Инициализатор VirtualPages для альтернативы.

+0

Спасибо! Я определенно могу использовать многие из этих идей. А также спасибо за аргумент по умолчанию и изменяемые объекты. Звучит как действительно хороший способ застрелить себя в ноге и не знать. Цените свое время! – Dave

Смежные вопросы