Подтвердить что ты не робот

Как подклассы str в Python

Я пытаюсь подклассифицировать объект str и добавлять к нему несколько методов. Моя главная цель - научиться делать это. Где я застрял, я должен подклассифицировать строку в метаклассе и создать мой класс с этой мета или подклассом str напрямую?

А также, я думаю, мне нужно каким-то образом реализовать __new__(), потому что мои пользовательские методы изменят мой строковый объект и вернут новый mystr obj.

Мои методы класса должны быть полностью привязаны с помощью методов str и всегда должны возвращать новый экземпляр класса, когда пользовательские методы его модифицировали. Я хочу иметь возможность сделать что-то вроде этого:

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

Я хочу иметь все возможности, которые есть у str. Например, a = mystr("something"), тогда я хочу использовать его как,  a.capitalize(). mycustommethod(). ниже()

Насколько я понимаю, мне нужно реализовать __new__(). Я так думаю, потому что методы строк, вероятно, попытаются создать новые экземпляры str. Итак, если я перезаписал __new__(), они якобы вернут мой собственный класс str. Тем не менее, я не знаю, как передать аргументы моему методу __init__() класса в этом случае. И я предполагаю, что мне нужно будет использовать type(), чтобы создать новый экземпляр в методе __new__() правильно?

4b9b3361

Ответ 1

Перезапись __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.

Ответ 2

Здесь вы можете быстро взломать все, что хотите: вы перехватываете каждый вызов функции и, если видите, что он возвращает строку, вы конвертируете ее обратно в свой собственный тип класса.

Хотя это работает в этом простом примере, у него есть некоторые ограничения. Среди прочего, операторы, такие как оператор индекса, по-видимому, не обрабатываются.

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()

Ответ 3

Я пытаюсь подклассифицировать объект 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!!!')

Ответ 4

Я немного ужасен от сложности других ответов, а также стандартной библиотеки python. Вы можете использовать collection.UserString для подкласса строки и не использовать методы проксирования str.

Просто подклассируйте его и добавьте свои методы. self.data содержит фактическую строку, представленную вашим объектом, поэтому вы можете даже реализовать str- "мутирующие" методы, переназначив self.data внутренне.

Пример.

Ответ 5

Вы можете попробовать что-то вроде:

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

но вы не будете уверены, что стандартные методы также вернут экземпляр "mystr"