web-dev-qa-db-ja.com

Pythonでstrをサブクラス化する方法

Strオブジェクトをサブクラス化して、それにいくつかのメソッドを追加しようとしています。私の主な目的はそれを行う方法を学ぶことです。私が立ち往生しているのは、メタクラスの文字列をサブクラス化し、そのメタでクラスを作成するのか、それとも直接サブクラスstrを作成するのか?

また、カスタムメソッドが文字列オブジェクトを変更し、新しいmystr objを返すため、何らかの方法で__new__()を実装する必要があると思います。

私のクラスのメソッドは、strメソッドと完全にチェーン可能である必要があり、カスタムメソッドが変更した場合は常に新しいクラスインスタンスを返す必要があります。私はこのようなことをしたいです:

_a = mystr("something")
b = a.lower().mycustommethod().myothercustommethod().capitalize()
issubclass(b,mystr) # True
_

strが持つすべての能力を持ちたいです。たとえば、a = mystr("something")の場合、a.capitalize()。mycustommethod()。lower()のように使用します。

__new__()を実装する必要があることは私の理解です。文字列メソッドはおそらく新しいstrインスタンスを作成しようとするためだと思います。したがって、__new__()を上書きすると、カスタムstrクラスが返されると思われます。ただし、その場合、カスタムクラスの__init__()メソッドに引数を渡す方法がわかりません。そして、type()メソッドで新しいインスタンスを作成するには、__new__()を使用する必要があると思いますか?

27
yasar

構築時に文字列を変更する場合は、__new__()の上書きが機能します。

_class caps(str):
   def __new__(cls, content):
      return str.__new__(cls, content.upper())
_

ただし、新しいメソッドを追加するだけの場合は、コンストラクターに触れる必要はありません。

_class text(str):
   def duplicate(self):
      return text(self + self)
_

たとえばupper()のような継承されたメソッドは、strではなく通常のtextを返すことに注意してください。

33
sth

strオブジェクトをサブクラス化して、それにいくつかのメソッドを追加しようとしています。私の主な目的は、その方法を学ぶことです。

メソッドを親クラスにハードコーディングしないでください(一番上の答えのように)。代わりに、このようにsuperを使用して、Python 2:

_class Caps(str):
    def __new__(cls, content):
        return super(Caps, cls).__new__(cls, content.upper())
_

Python 3では、このようにsuperを呼び出す方がパフォーマンスが高くなりますが、Python 2:との下位互換性はありません。

_class Caps(str):
    def __new__(cls, content):
        return super().__new__(cls, content.upper())
_

使用法:

_>>> Caps('foo')
'FOO'
>>> isinstance(Caps('foo'), Caps)
True
>>> isinstance(Caps('foo'), str)
True
_

完全な答え

これまでのところ、あなたがここで要求したことを行う答えはありません。

私のクラスのメソッドは、strメソッドと完全にチェーン可能である必要があり、カスタムメソッドが変更した場合は常に新しいクラスインスタンスを返す必要があります。私はこのようなことをしたいです:

_a = mystr("something")
b = a.lower().mycustommethod().myothercustommethod().capitalize()
issubclass(b,mystr) # True
_

(私はあなたがisinstance()ではなくissubclass()を意味すると信じています。)

文字列メソッドをインターセプトする方法が必要です。 ___getattribute___はこれを行います。

_class Caps(str):
    def __new__(cls, content):
        return super(Caps, cls).__new__(cls, content.upper())
    def __repr__(self):
        """A repr is useful for debugging"""
        return f'{type(self).__name__}({super().__repr__()})'
    def __getattribute__(self, name):
        if name in dir(str): # only handle str methods here
            def method(self, *args, **kwargs):
                value = getattr(super(), name)(*args, **kwargs)
                # not every string method returns a str:
                if isinstance(value, str):
                    return type(self)(value)  
                Elif isinstance(value, list):
                    return [type(self)(i) for i in value]
                Elif isinstance(value, Tuple):
                    return Tuple(type(self)(i) for i in value)
                else: # dict, bool, or int
                    return value
            return method.__get__(self) # bound method 
        else: # delegate to parent
            return super().__getattribute__(name)
    def mycustommethod(self): # shout
        return type(self)(self + '!')
    def myothercustommethod(self): # shout harder
        return type(self)(self + '!!')
_

そして今:

_>>> a = Caps("something")
>>> a.lower()
Caps('SOMETHING')
>>> a.casefold()
Caps('SOMETHING')
>>> a.swapcase()
Caps('SOMETHING')
>>> a.index('T')
4
>>> a.strip().split('E')
[Caps('SOM'), Caps('THING')]
_

そして、要求されたケースは機能します:

_>>> a.lower().mycustommethod().myothercustommethod().capitalize()
Caps('SOMETHING!!!')
_
13
Aaron Hall

私は他の答えの複雑さにちょっと怖いので、python標準ライブラリです。 collections.UserString を使用して文字列をサブクラス化でき、混乱しないでください。 strのメソッドのプロキシ。

サブクラス化して、メソッドを追加するだけです。 self.dataには、オブジェクトによって表されている実際の文字列が含まれているため、self.dataを内部的に再割り当てすることで、str- "mutating"メソッドを実装することもできます。

10
Andrew Morozko

これがあなたがやりたいことをするための簡単なハックです:あなたは基本的にすべての関数呼び出しをインターセプトし、それが文字列を返しているのを見たら、それをあなた自身のクラスタイプに戻します。

これはこの単純な例では機能しますが、いくつかの制限があります。とりわけ、添え字演算子などの演算子は明らかに処理されません。

class FunWrapper(object):
    def __init__(self, attr):
        self.attr = attr

    def __call__(self, *params, **args):
        ret = self.attr(*params, **args)
        if type(ret) is str:
            return Foo(ret)
        return ret

class Foo(object):
    def __init__(self, string):
        self.string = string

    def __getattr__(self, attr):
        return FunWrapper(getattr(self.string, attr))

    def newMethod(self):
        return "*%s*" % self.string.upper()


f = Foo('hello')
print f.upper().newMethod().lower()
8
UncleZeiv

次のようなことを試すことができます:

class mystr(str):
    def new_method(self):
        pass

ただし、標準メソッドが「mystr」インスタンスも返すかどうかはわかりません。

0
Dona