0

Python の基本型は、期待どおりに継承をカウントしません。例えば:

class MyUnicode(unicode):
    pass

mu = MyUnicode('xxx')

>>> type(mu)
<class '__main__.MyUnicode'> # ok

>>> type(mu + 'x')
<type 'unicode'> # why not <class '__main__.MyUnicode'> ?

>>> type(mu.strip())
<type 'unicode'> # why not <class '__main__.MyUnicode'> ?

文字列は不変であるため、これら 2 つのメソッドは新しいオブジェクトを返す必要があります。unicodeしかし、開発者がサブクラスを使用する代わりに、これらのメソッド内に戻り値の型をハードコーディングしたのはなぜでしょうか? 私が気付いていない潜在的な欠点を防ぐことができますか?

4

4 に答える 4

2

個人的にはunicode、サブクラスの実装の詳細に依存せずに、希望どおりに動作するように実装する方法を確認したいと思います。

試してみると(明らかな理由から擬似コードです):

def __add__(self, other):
    return type(self)(concatenate(self.string, other.string))

クラスに単一引数のコンストラクターを強制しています。なぜ?さらに、mu + u'x'u'x' + muは異なるタイプであり、効率が悪いことも保証されています。

未知の可能性が低いサブクラスが特定の方法で動作するようにするためだけに、追加の作業を行う理由はありません。unicodeサブクラス化するように設計されているとは思いません。サブクラス化し、基本クラスとは異なる動作が必要な場合は、関連するメソッドを自分でオーバーライドしてください。

于 2013-07-03T14:08:13.113 に答える
1

.stripand をオーバーライドしませんでし__add__た。デフォルトでは、クラスのインスタンスではなく、Unicode オブジェクトを返します。ストリップ機能のソースは次のとおりです (2.7.3)。なぜ開発者は、最初にサブクラスをチェックしてそれを返すのではなく、Unicode メソッドから Py_UNICODE オブジェクトを返すことにしたのでしょうか? ここでの考え方は、自分でそれを行うのに十分なだけ与えられているということだと思います.

于 2013-07-03T14:07:50.323 に答える
0

OPからの複数のコメントの後、回答を最初から書き直すことにしました。

考えられる回避策:

class MyUnicodeMetaClass(type):

    autocast_methods = ('__add__', '__radd__', 'format')

    def __init__(cls, name, bases, attrs):   
        super(MyUnicodeMetaClass, cls).__init__(name, bases, attrs)
        for method_name in MyUnicodeMetaClass.autocast_methods:
            try:
                setattr(cls, method_name, cls.autocast_creator(method_name))
            except AttributeError:
                if method_name.startswith('__r'):
                    setattr(cls, method_name, cls.autocast_reverse(method_name))
                else:
                    raise

    def autocast_creator(cls, method_name):
        method = unicode().__getattribute__(method_name)
        def autocast_method(self, *args, **kwargs):
            method = unicode(self).__getattribute__(method_name)
            return cls(method(*args, **kwargs))
        return autocast_method

    def autocast_reverse(cls, method_name):
        method_name = method_name.replace('__r', '__', 1)
        def autocast_method(self, *args, **kwargs):
            method = unicode(args[0]).__getattribute__(method_name)
            return cls(method(self, *args[1:], **kwargs))
        return autocast_method


class MyUnicode(unicode):
    __metaclass__ = MyUnicodeMetaClass

a = MyUnicode(u'aaa {0}')
print a, type(a)
# aaa {0} <class '__main__.MyUnicode'>
b = a + u'bbb'
print b, type(b)
# aaa {0}bbb <class '__main__.MyUnicode'>
c = u'ddd' + a
print c, type(c)
# cccaaa {0} <class '__main__.MyUnicode'>
d = a.format(115)
print d, type(d)
# aaa 115 <class '__main__.MyUnicode'>

拡張が必要になる場合がありますが、ベース スケルトンは準備ができています。
ここで何が起きてるの?
1.クラスMetaclassの作成を変更するために使用されます。 2. Simpleは、 の代わりに返されるメソッドをクラスに入力するために使用されます。 3.リバース メソッドを提供するために、もう少し洗練された方法が使用されます(最初のオペランドがで、2 番目のオペランドが である場合に必要になるなど) MyUnicode
autocast_creatorMyUnicodeMyUnicodeunicode
autocast_reverse__radd__unicodeMyUnicode
autocast_methods

背景情報:

継承:
オブジェクト指向プログラミングは、実際の言葉を可能な限り反映することを目的としています。
ここでは継承も例外ではありません。

現実の世界では、ゾウの群れは常に動物の群れです。ここで間違いありません。
しかし、動物のグループはゾウのグループかもしれませんが、さまざまな動物のグループである可能性もあり、このグループにゾウがいないことさえあります.
これは、象のどのグループも特別な種類の動物のグループであるためです。のサブクラスとして
定義することで、コンピュータプログラムに反映させることができます。 どのように拡張できますか? たとえば、 new fieldと new methodを定義します。 このような単純な操作を考えてみましょう。 期待される結果のクラスは何ですか?ElephantGroupAnimalGroup
ElephantGroupAnimalGroupivory_weighttoot()
ElephantGroup() + AnimalGroup()
AnimalGroup-ここには魔法がなく、ネズミ、イルカなどは象になりません。ネズミとイルカは象牙を提供せず、歯を出すこともできません。

MyUnicodeとに戻りましょうunicode
マトリックスやターミネーターで提示された意味では、機械は知的ではありません。
Python インタープリターは、 の目的を理解していませんMyUnicode。名前が付けられ、電子メール アドレスを保持することを目的としたor (ここではあまり重要ではありません) を
拡張するクラスを考えてみましょう。当然のことです:) そして、コード スニペットができました。unicodestrEmailAddress

a = EmailAddress(u'example@example.com')
b = u'/!\n'
c = a + b
d = b + a

まだ?のインスタンスになることを期待cしています。(または?)はいと答え た場合は、次のことを教えてください 。そうでない場合は例外が発生します... 2. インタープリターは、実行せずに任意のインスタンスでインスタンスを 安全に初期化できることをどのように認識できますか? また、これは Python であり、実行時に動的に変更することもできることを覚えておいてください。覚えておいてください - インスタンスをその先祖のいずれかにいつでもキャストできます。逆の操作は暗黙的に行うことはできません。インタプリタが暗黙的にインスタンスをdEmailAddressMyUnicode

EmailAddress.__init__(...)
MyUnicode unicode__init____init__
objectobjectsサブクラス。
このstrip()メソッドは同じ規則に従います。unicode文字はオリジナルから削除され、新しいunicodeインスタンスへの参照が返されます (まったく同じものunicodeが存在しない場合 - この場合、既存のインスタンスへの参照が返されます)。

インスタンスクラスの参照:

あなたのコメントの1つでcls、実行チェーンを開始したインスタンスのクラスを参照していると述べました...は、インスタンスを持たないことを示すために、およびメソッド
clsで使用される命名規則です-クラスしかありません。 実際、これらのケースのいずれにおいてもインスタンスにアクセスすることはできませんが、ほとんどの場合、新しいインスタンスを返す必要があります。 metaclassesclassmethods__new__()
__new__()

identifier.__class__属性を考えていたのでしょう。実行チェーンとも関係ありません。によって参照されるインスタンスの実際のクラスを指しますidentifier。サブクラスを作成するためにおよび
のメソッドを使用することを期待するのはなぜですか? サブクラスに何かを暗黙的にキャストすることは、予期される動作ではありません。あなたのコードのオペランドの 1 つはですが、もう 1 つはそうです。unicodestr
MyUnicodeunicodestrip()unicode

Unicode 実装の詳細:

Pythonunicodestring型は不変でユニークです(説明は後ほど)。不変とは、それらに対する変更操作がそれぞれまたはの他のインスタンスを返すことを意味します。他のインスタンスは新しいインスタンスを意味しますが、私が言ったように、これらのタイプは一意です。 その意味?コードを参照してください:unicodestring

a = u'aaa'
b = u'aaa'

ここで何が起こったのですか?初期化するために作成され
た の新しいインスタンスがありました。 初期化するオブジェクトが見つかったため、新しいインスタンスは作成されませんでした。 代わりに、オブジェクト保持への参照カウンターがインクリメントされました。 unicodea
unicodeb
unicodeu'aaa'

それがわかったら、次のコードを検討してください。

a = u'aaa'
b = MyUnicode(u'aa')
c = b + u'a'

変数には正確に何が格納されcますか? オブジェクトへの参照unicode- によって参照される同じオブジェクトa
変更してcも影響がないのはなぜaですか? unicode不変であり、基になるオブジェクトは変更されないためです。
次の行が の場合、c = c + u'b'new c/other インスタンスへの参照が取得され、参照されるオブジェクトaの参照カウンタが減少します。

結論:

Pythonunicodestrクラスは一貫性があり、予測的です。
最適化、特別な目的、または実装の詳細のために、派生するのが難しい型がいくつかあります。サブクラス化することは意図されていませんが、たとえば私のスニペットのように実現でき
unicodeます。 strmetaclass

いつものように、建設的な批判やコメントをお待ちしています。

幸運を!

于 2013-07-03T14:27:10.930 に答える