0

クラスのいくつかのパラメーターに関して、オブジェクト (一連の数字) の作成について助けが必要です。Python IDLE シェルに次のように入力したとします。

SuperLotto = make_lottery_set_type('SuperLotto', 6, (1,50))
   #means user can create a 'SuperLotto' with 6 numbers in range of 1 to 50

「SuperLotto」を「LotteryGameType」というクラスの新しいクラス インスタンスとして作成します。

これはこれまでのコードを使用しています:

class LotterySetError(Exception):
     pass               

def make_lottery_set_type(name:str, size:int, minmax:tuple):
    if minmax[0] > minmax[1]:
        raise LotterySetError('Illegal range for tuple')
    else:
        name = LotteryGameType(name, size, minmax[0], minmax[1])
    return name

class LotteryGameType:
    def __init__(self, name, set_size, min_set_number, max_set_number):
        self.name = name
        self.set_size = set_size
        self.min_set_number = min_set_number
        self.max_set_number = max_set_number

一連の数値を作成し、後で使用するために保存できるようにしたいので、オーバーロード演算子 ( eqneなど) などで使用できます。

Python IDLE シェルに入力できるようにしたい:

SuperLotto([3, 4, 19, 23, 46, 27])

これにより、SuperLotto のパラメーターの下にオブジェクトが作成されますが、'SuperLotto' のパラメーターの下にない場合 (6 つ以上の数字など)、エラーが発生します。どんなアプローチでも構いません。これにアプローチする方法について誰かアイデアがありますか?

4

1 に答える 1

0

そのタイプのインスタンスを返すのではなく、おそらく のサブクラスでmake_lottery_set_typeある new を返すことを望んでいるようです。classLotteryGameType

これは実際、Python で行うのは非常に簡単です。クラス定義は、関数の途中であっても、どこでも実行できる通常のコードです。また、実行中にローカル環境にアクセスできます。クラス自体は「ファーストクラスの値」です。つまり、クラスを渡したり、関数から返したりすることができます。そう:

def make_lottery_set_type(name:str, size:int, minmax:tuple):
    if minmax[0] > minmax[1]:
        raise LotterySetError('Illegal range for tuple')
    else:
        class NewLotteryGameType(LotteryGameType):
            def __init__(self, numbers):
                super().__init__(name, size, minmax[0], minmax[1])
                self.numbers = numbers
        return NewLotteryGameType

他のメソッドを追加する場合は、他のクラスにメソッドを追加するのと同じです。例えば:

def make_lottery_set_type(name:str, size:int, minmax:tuple):
    if minmax[0] > minmax[1]:
        raise LotterySetError('Illegal range for tuple')
    else:
        class NewLotteryGameType(LotteryGameType):
            def __init__(self, numbers):
                super().__init__(name, size, minmax[0], minmax[1])
                self.numbers = numbers
            def __eq__(self, rhs):
                return set(self.numbers) == set(rhs.numbers)
        return NewLotteryGameType

そう:

>>> SuperLotto = make_lottery_set_type('SuperLotto', 6, (1,50))
>>> super1 = SuperLotto([1,2,3,4,5,6])
>>> super2 = SuperLotto([6,5,4,3,2,1])
>>> super3 = SuperLotto([7,8,9,10,11,12])
>>> super1 == super2
True
>>> super1 == super3
False

(明らかに__eq__、set-equality が使用に適したルールでない場合は、必要に応じて定義できます。)


生成している値を調べようとすると、思ったほどきれいに見えません。たとえば、次のような場所ではSuperLottoなく、おそらく見たいと思うでしょう。NewLotteryGameType

>>> super1
<__main__.NewLotteryGameType at 0x10259e490>
>>> SuperLotto.__name__
'NewLotteryGameType'

そのためには、 を追加するだけNewLotteryGameType.__name__ = nameです。親クラスから docstring をコピーしたり、その他のさまざまなものをコピーしたりすることもできます。

より一般的にはfunctools.update_wrapper、インスピレーションを得るために (クラスではなく関数をラップするように設計されていますが、詳細の多くは同じです) を参照し、inspectクラスが持つことができるすべての属性については、Python バージョンのモジュール ドキュメントを参照してください。


コメントでは、次のように尋ねます。

唯一の問題は、NewLotteryGameType に LotteryGameType から name、set_size、min_set_number、max_set_number などのパラメーターを継承させたいことです。では、NewLotteryGameType.set_size を Python シェルに入力したいとしましょう。私に返してほしい 6.

それは矛盾しています。LotteryGameType…のインスタンス属性を継承したい場合は、既に行っています。例えば:

>>> super1.set_size
6

クラスからアクセスできるようにする場合は、インスタンス属性にすることはできません。クラス属性にする必要があります。set_sizeまた、クラス属性に変更してLotteryGameType継承するだけでは機能しません。クラス属性の全体的なポイントは、クラスのすべてのインスタンスまたはそのサブクラスのいずれかによって同じ値が共有され、サブクラスはすべて異なる値を必要とするためです。

しかし、次のようなことができます。

class LotteryGameType:
    def __init__(self, min_set_number, max_set_number):
        self.min_set_number = min_set_number
        self.max_set_number = max_set_number

def make_lottery_set_type(lottery_name:str, size:int, minmax:tuple):
    if minmax[0] > minmax[1]:
        raise LotterySetError('Illegal range for tuple')
    else:
        class NewLotteryGameType(LotteryGameType):
            name = lottery_name
            set_size = size
            def __init__(self, numbers):
                super().__init__(minmax[0], minmax[1])
                self.numbers = numbers
            def __eq__(self, rhs):
                return set(self.numbers) == set(rhs.numbers)
        return NewLotteryGameType

(最初のmake_パラメーターの名前を に変更しなければならなかったことに注意してください。これは、スコープが機能する方法のためlottery_name、 class 属性とは異なります。)とはインスタンス属性ではなく、のクラス属性でもありませんが、のクラス属性です。それぞれ。そう:namenameset_sizeLotteryGameTypeNewLotteryGameType

>>> SuperLotto = make_lottery_set_type('SuperLotto', 6, (1,50))
>>> SuperDuperLotto = make_lottery_set_type('SuperDuperLotto', 8, (1,100))
>>> SuperLotto.set_size
6
>>> SuperDuperLotto.set_size
8

これらのタイプのインスタンスを作成するとどうなるでしょうか? さて、Python はインスタンスで属性を検索し、次に最も派生したクラスで、次に基本クラスで検索します。したがって、同じ名前のインスタンス属性を作成しない限り (LotteryGameType.__init__メソッドから余分なパラメーターとインスタンス属性を設定するコードを削除したことに注意してください)、必要なことだけが実行されます。

>>> super1 = SuperLotto([1,2,3,4,5,6])
>>> super1.set_size
6
>>> duper1 = SuperDuperLotto([1,2,3,4,5,6,7,8])
>>> duper1.set_size
8

もちろん、これはLotteryGameTypeそれ自体ではもはや使用可能な型ではないことを意味します。そのサブクラスのみが使用可能です。しかし、それはおそらくあなたが望んでいたことですよね?直接インスタンスを誤って使用しようとする人がいないことを確認するために、明示的に抽象基本クラスにすることを検討することもできます。LotteryGameType

勇気があれば、メタクラスを読んで、この設計全体を を使用するように適応させる方法を確認したいと思うかもしれませんLotteryGameMetaclass。そのため、新しいクラスはそれぞれ、(抽象) 基本クラスのサブクラスではなく、そのメタクラスのインスタンスになります。3.4の新しいenumモジュールのソース、またはほぼ同等の外部flufl.enumパッケージは、適切なサンプル コードになる可能性があります。次に、両方で遊んで、それらがどのように似ているか、どのように異なるかを確認できます。

于 2013-08-10T00:30:07.513 に答える