0

まだ Python と魔法の関数型プログラミングに戸惑っているので、慣用的な Python ではなく、プログラミングの Java パラダイムに近いコードを書く傾向があります。

私の質問は、Python でカスタム クラスをコレクションにする方法に多少関連しています。

唯一の違いは、(コンポジションを使用して) ネストされたオブジェクトがあることです。VirtualPageオブジェクトは、PhysicalPage オブジェクトのリストで構成されていますPhyscialPageオブジェクトのリストを取り、すべての詳細を結合して、 PageBoundaryと呼ぶ単一の名前付きタプルにする関数があります。本質的には、物理​​ページとページ内の行番号を表す整数範囲で構成されるタプルを吐き出すことができるシリアライゼーション関数です。これから、VirtualPages を互いに簡単に並べ替えて並べ替えることができます (少なくともこれがアイデアです)。

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

また、タプルという名前のPageBoundaryを取得し、タプルを物理ページのリストに逆シリアル化または展開できる関数もあります。これら 2 つのデータ ストレージ クラスは変更しないことが望ましいです。

これは、私のカスタム 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())

テンプレート化されたコレクション (python collections モジュールから) を活用して、VirtualPageクラスのリストまたはセットとして簡単に並べ替えおよび比較したいと考えています。また、私のデータ ストレージ クラスであるVirtualPagePhysicalPageのレイアウトについてアドバイスをお願いします。

一連の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リストをループして、必要な関数 ( lt () およびeq ()) を実装する必要があるため、b/w VirtualPagesを比較できます。

1.) 現在、いくつかの比較関数の実装に苦労しています。大きな障害の 1 つは、タプルを比較する方法です。ある種のコレクションを拡張するカスタム クラスを作成して、独自のlt () 関数を作成しますか?

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クラスに存在する必要がありますか?

私がモデリングしているデータのプロパティには一意性の概念があるため、ある種の Set データ構造に傾いていました。つまり、物理ページの値は重複できず、ある程度リンクされたリストとして機能します。また、@ デコレータ関数を介して実装されたセッターまたはゲッター関数は、ここで役に立ちますか?

4

1 に答える 1

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 クラスには「プライベート」メンバーなどというものはあり_ませんが、クラス / モジュール / などのパブリック インターフェイスの一部ではないことを示すために、変数名を 1 つのアンダースコアで始めるのが慣例です。 '_' を使用したパブリック メソッドのメソッド パラメーターの命名は、正しくないようですdef __init__(self, _page_num=-1)

Python は通常、セッター / ゲッターを使用しません。属性を直接使用するだけです。属性値を計算する必要がある場合、またはその他の処理が必要な場合は、@propertyデコレーターを使用します (上記の PhysicalPage.begin() で示したように)。

デフォルトの関数引数を変更可能なオブジェクトで初期化することは、一般的には良い考えではありません。 def __init__(self, physical_pages=list())毎回新しい空のリストで physical_pages を初期化しません。むしろ、毎回同じリストを使用します。リストが変更された場合、次の関数呼び出しで、physical_pages が変更されたリストで初期化されます。別の方法については、VirtualPages 初期化子を参照してください。

于 2016-03-20T22:30:03.527 に答える