2

私はPython: Exception in the separate module works wrongを見ていました。これは、多目的の GnuLibError クラスを使用して、さまざまなエラーに対応します。各サブエラーには、独自の ID 番号とエラー形式の文字列があります。

例外クラスの階層として記述したほうがよいと考え、そのように設定しました。

class GNULibError(Exception):
    sub_exceptions = 0  # patched with dict of subclasses once subclasses are created
    err_num = 0
    err_format = None

    def __new__(cls, *args):
        print("new {}".format(cls)) # DEBUG
        if len(args) and args[0] in GNULibError.sub_exceptions:
            print("  factory -> {} {}".format(GNULibError.sub_exceptions[args[0]], args[1:])) # DEBUG
            return super(GNULibError, cls).__new__(GNULibError.sub_exceptions[args[0]], *(args[1:]))
        else:
            print("  plain {} {}".format(cls, args)) # DEBUG
            return super(GNULibError, cls).__new__(cls, *args)

    def __init__(self, *args):
        cls = type(self)
        print("init {} {}".format(cls, args)) # DEBUG
        self.args = args
        if cls.err_format is None:
            self.message = str(args)
        else:
            self.message = "[GNU Error {}] ".format(cls.err_num) + cls.err_format.format(*args)

    def __str__(self):
        return self.message

    def __repr__(self):
        return '{}{}'.format(type(self).__name__, self.args)

class GNULibError_Directory(GNULibError):
    err_num = 1
    err_format = "destination directory does not exist: {}"

class GNULibError_Config(GNULibError):
    err_num = 2
    err_format = "configure file does not exist: {}"

class GNULibError_Module(GNULibError):
    err_num = 3
    err_format = "selected module does not exist: {}"

class GNULibError_Cache(GNULibError):
    err_num = 4
    err_format = "{} is expected to contain gl_M4_BASE({})"

class GNULibError_Sourcebase(GNULibError):
    err_num = 5
    err_format = "missing sourcebase argument: {}"

class GNULibError_Docbase(GNULibError):
    err_num = 6
    err_format = "missing docbase argument: {}"

class GNULibError_Testbase(GNULibError):
    err_num = 7
    err_format = "missing testsbase argument: {}"

class GNULibError_Libname(GNULibError):
    err_num = 8
    err_format = "missing libname argument: {}"

# patch master class with subclass reference
# (TO DO: auto-detect all available subclasses instead of hardcoding them)
GNULibError.sub_exceptions = {
    1: GNULibError_Directory,
    2: GNULibError_Config,
    3: GNULibError_Module,
    4: GNULibError_Cache,
    5: GNULibError_Sourcebase,
    6: GNULibError_Docbase,
    7: GNULibError_Testbase,
    8: GNULibError_Libname
}

これは、ファクトリ クラスとして GNULibError で始まります。認識されたサブクラスに属するエラー番号で呼び出すと、そのサブクラスに属するオブジェクトが返されます。それ以外の場合は、デフォルトのエラー タイプとして自身が返されます。

このコードに基づいて、以下は完全に同等である必要があります (ただし、そうではありません)。

e = GNULibError(3, 'missing.lib')
f = GNULibError_Module('missing.lib')

print e  # -> '[GNU Error 3] selected module does not exist: 3'
print f  # -> '[GNU Error 3] selected module does not exist: missing.lib'

いくつかの戦略的な印刷ステートメントを追加しましたが、エラーは次のようですGNULibError.__new__:

>>> e = GNULibError(3, 'missing.lib')

new <class '__main__.GNULibError'>
  factory -> <class '__main__.GNULibError_Module'> ('missing.lib',)  # good...
init <class '__main__.GNULibError_Module'> (3, 'missing.lib')        # NO!
                                            ^
                                           why?

サブクラスのコンストラクタを as と呼びますsubclass.__new__(*args[1:])- これはサブクラスのタイプ ID である 3 を削除する必要があります__init__が、とにかくまだ 3 を取得しています! に渡される引数リストをトリムするにはどうすればよいsubclass.__init__ですか?

4

3 に答える 3

2

に渡されるものに影響を与えることはできません__init__。現在のように、それ自体のサブクラスを返す「ファクトリ クラス」を使用している限りです。「3」引数がまだ渡されている理由は、まだ GNULibError のインスタンスを から返しているためです__new__。が呼び出されるまでに__new__、 に何を渡すかを決定するには遅すぎます__init__ドキュメントに記載されているように(強調を追加):

() が cls のインスタンスを返す場合__new__、新しいインスタンスの__init__() メソッドは のように呼び出されます__init__(self[, ...])。ここで、self は新しいインスタンスで、残りの引数は に渡されたものと同じ__new__()です。

言い換えると、 を呼び出したときにはGNULibError(3, 'missing.lib')手遅れです --- これらの引数を指定してクラスを呼び出すことにより、それらが に渡される引数であることを確認したことになります__init____new__他の方法で取得する場合とは異なるインスタンスを返すことができますが、通常の初期化の発生を止めることはできません。

@Ned Batchelder が示唆しているように、関数には this __new__/__init__機械がなく、必要なクラスのインスタンスを返すことができるため、「ファクトリ クラス」の代わりにファクトリ関数を使用することをお勧めします。

于 2012-06-24T02:59:14.013 に答える
0

あなたのユースケースでは-私はネッドに同意します-それが必要であるより複雑です。

次のようなことを試すことができます(派生クラスは何もしないようですが、エラーメッセージとは異なるという事実に基づいています)。

class GNULibError(Exception):
    pass # put logic code here

GNULibErrors = {
    1: type('GNULibError_Directory', (GNULibError,), {'message': 'suitable whatever here'})
}

そしてそこから調整します...

于 2012-06-24T03:07:17.400 に答える
0

これは必要以上に複雑です。クラスに別のクラスのオブジェクトを作成させようとしないでください。例外を作成するファクトリ関数を作成し、いじらないでください__new__。あなたが見つけているように、それはあまりにもトリッキーです。

于 2012-06-24T02:34:42.993 に答える