Чи можу я додати власні методи / атрибути до вбудованих типів Python?


78

Наприклад, скажімо, що я хочу додати helloWorld()метод до типу дикту Python. Чи можу я це зробити?

JavaScript має об'єкт-прототип, який поводиться так. Можливо, це поганий дизайн, і мені слід підкласувати об’єкт dict, але тоді він працює лише для підкласів, і я хочу, щоб він працював на будь-яких майбутніх словниках.

Ось як це могло б знизитися в JavaScript:

String.prototype.hello = function() {
    alert("Hello, " + this + "!");
}
"Jed".hello() //alerts "Hello, Jed!"

Ось корисне посилання з іншими прикладами - http://www.javascriptkit.com/javatutors/proto3.shtml


1
Ви можете зробити це в рубіні, просто відкривши клас знову, тобто: class String; def new_method () ...; end; Я майже впевнений, що у python є щось подібне.
Абдулла Джибалі

@Abdullah Я вважаю, що ви маєте на увазі виправлення мавп, на яке здебільшого здивовано.
Рейф Кетлер

Так точно. Це зроблено повсюдно в Rails.
Абдулла Джибалі

Це специфічно, numpy.ndarrayале якщо хтось прийшов сюди, щоб спробувати це, і не вдалося додати метод до екземпляра numpy.ndarray, подивіться на numpy.ndarray.viewметод.
mondaugen

Відповіді:


80

Ви не можете безпосередньо додати метод до вихідного типу. Тим не менш, ви можете підкласувати тип, а потім замінити його у вбудованому / глобальному просторі імен, що досягає більшої частини бажаного ефекту. На жаль, об’єкти, створені буквальним синтаксисом, і надалі матимуть ванільний тип і не матимуть нових методів / атрибутів.

Ось як це виглядає

# Built-in namespace
import __builtin__

# Extended subclass
class mystr(str):
    def first_last(self):
        if self:
            return self[0] + self[-1]
        else:
            return ''

# Substitute the original str with the subclass on the built-in namespace    
__builtin__.str = mystr

print str(1234).first_last()
print str(0).first_last()
print str('').first_last()
print '0'.first_last()

output = """
14
00

Traceback (most recent call last):
  File "strp.py", line 16, in <module>
    print '0'.first_last()
AttributeError: 'str' object has no attribute 'first_last'
"""

Я думаю, мені просто доведеться скористатися найкращим методом підкласу. Можливо, я полюблю це.
jedmao

27
Примітка: __builtin__Модуль було перейменовано на builtinsPython3.
changokun

Ну, я зіткнувся з проблемою в python 2. Після цього я більше не можу перевірити тип> type ('') == str <just gives> false <. Коли я набираю (""), я отримую <type 'str'>, але якось порівняння більше не працює. Перед запуском "рядка вбудованого .str = mystr" перевірка типу працює.
Наду

@Nadu, як сказав ОП, "об'єкти, створені буквальним синтаксисом, і надалі матимуть ванільний тип"
Алекс Холл,

@nadu. Зазвичай ви не повинні використовувати == при перевірці типів. Натомість використовуйте isinstance ("", str), який поверне True, якщо перше значення є екземпляром другого або будь-яким підкласом другого.
Chris Cogdon,

6

Щойно спробував заборонений фрукт!

ось код, дуже простий!

from forbiddenfruit import curse


def list_size(self):
    return len(self)

def string_hello(self):
    print("Hello, {}".format(self))

if __name__ == "__main__":
    curse(list, "size", list_size)
    a = [1, 2, 3]
    print(a.size())
    curse(str, "hello", string_hello)
    "Jesse".hello()

6
Окрім того, що заборонений фрукт, насамперед, є поганою ідеєю, він погано впроваджується і дуже легко призводить до помилок та пошкодження пам’яті.
user2357112 підтримує Моніку

мені це подобається. я буду використовувати.
Віталій Фадєєв

@ user2357112supportsMonica Чи є у вас посилання, де я можу детальніше прочитати про заборонені фрукти?
DarthVlader

2
class MyString:
    def __init__(self, string):
        self.string = string
    def bigger_string(self):
        print(' '.join(self.string))

mystring = MyString("this is the string")
mystring.bigger_string()

вихід

t h i s   i s   t h e   s t r i n g

Клас даних у Python 3.7

from dataclasses import dataclass

@dataclass
class St:

    text : str

    def bigger(self) -> None:
        self.text = list(self.text)
        print(" ".join(self.text))

mys = St("Hello")
mys.bigger()

вихід

H e l l o

2

ПРИМІТКА : цей QA позначений як дублікат цього , але IMO вимагає чогось іншого. Я не можу відповісти там, тому відповідаю тут.


Зокрема, я хотів успадкувати strта додати власні атрибути. Існуючі відповіді (особливо ті, що говорять, що ти не можеш ) не зовсім вирішили це, але це спрацювало для мене:

class TaggedString(str):
    """
    A ``str`` with a ``.tags`` set and ``.kwtags`` dict of tags.
    Usage example::
      ts = TaggedString("hello world!", "greeting", "cliche",
                        what_am_i="h4cker")
      (ts.upper(), ts.tags, ts.kwtags)
    """

    def __new__(cls, *args, **kwargs):
        return super().__new__(cls, args[0])

    def __init__(self, s, *tags, **kwtags):
        super().__init__()
        self.tags = set(tags)
        self.kwtags = kwtags

Сподіваємось, це комусь допомагає! Ура,
Андрес


2

Так, але вам потрібно визначити новий клас того самого типу, і він повинен успадковуватися від цього типу.

Наприклад:

class list(list):
    def __init__(self,*args):
        super().__init__(args)
    def map(self,function):
        return [function(i) for i in self]

a = list(1,2,3,4,5)
Def double(i): return i*2

print(a.map(double))

Дякую. Дивовижний, потужний ... і досить небезпечний! У контексті мультимодулів цей клас потрібно спеціально імпортувати. Так само добре, напевно.
гризун Майк

0

Так, шляхом підкласу цих типів. Див. Об’єднання типів та класів у Python .

Ні, це не означає, що справжні дикти матимуть такий тип, оскільки це може заплутати. Підклас вбудованого типу - найкращий спосіб додати функціональність.


11
Це було б заплутано і дуже корисно.
Євген Сергєєв

1
@Veky Я хочу any_dictionary_instance.len()або .size(), крім доступного any_dictionary_instance.__len__()і len(any_dictionary_instance)що називає це. Розмір є семантично властивістю контейнерів, тому зручніше писати так, як ви говорите, наприклад "ітерація від 0 до розміру об'єкта" ... ой, не можу це виразити, мова змушує мене перефразовувати як "ітерація від 0 до розміру об'єкта ". З іншого боку, я не зважаю на історичні причини.
Євген Сергєєв

1
@nehemiah і ти знаєш, що мав на увазі Євгеній ... як саме? Я читав його слова. Ви читали його коментар вище? Він прямо скаржився на той факт, що він не може використовувати саксонський генітив при доступі до розміру. До речі, так, це його код, поки хтось інший не зможе його підтримувати. І він, очевидно, може змінювати та перекомпілювати середовище виконання, як йому заманеться. :-P
Веки,

1
@nehemiah і в чому проблема цього? Ви погоджуєтесь, що str відрізняється від MyStr, правда?
Веки

4
Сподіваюсь, цього не буде (хоча, тепер, коли Гвідо пішов з посади, все можливо: - /). Вся суть знання типів Python полягає в тому, щоб знати, якими методами вони володіють. Якщо хтось міг додати випадкову функціональність str, тоді ви просто не знаєте, що це за тип 'abc'. Звичайно, це неприпустимо. І так, JS має з цим великі проблеми: засвідчіть фіоско motools Array.flatten.
Веки

-3

Підкласифікація - це спосіб піти в Python. Програмісти Polyglot вчаться використовувати правильний інструмент для правильної ситуації - в межах розумного. Щось таке хитро побудоване, як Rails (DSL із використанням Ruby), болісно важко реалізувати в мові з більш жорстким синтаксисом, як Python. Люди часто порівнюють ці два слова, кажучи, наскільки вони схожі. Порівняння є дещо несправедливим. Python світить по-своєму. totochto.


Я не бачу сенсу в підкласуванні рядкового класу. OP чітко розуміє, чого він хоче.
nehem
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.