Что значит вернуть self из метода касса
Нужно было написать работающий класс, который бы считал количество добавленных элементов заданного типа данных. Я смог сделать, чтобы программа заработала, но я не понимаю почему нельзя было просто вернуть self.count и вообще интересно, что значит return self.
Код:
class List:
def __init__(self, type):
self.type = type
self.items = []
self.count = 0
def add(self, item):
if type(item) != self.type:
item_type = "str" if self.type == str else "int" if self.type == int else "float"
return "This item is not of type: %s" %(item_type)
self.items += [item]
self.count = len(self.items)
return self
Тесты:
def simple_test():
my_list = List(str)
print(my_list.add('Hello').count, "---", 1, 'How many items in the List?')
print(my_list.add(5), "This item is not of type: str", "---", 'Wrong type added')
print(my_list.add(' ').add('World!').count, "---", 3, 'How many items in the List?')
simple_test()
Ответы (1 шт):
Для лучшего понимания вами приведённого кода думаю лучше привести немного теории перед ответом на ваш вопрос.
Теория
Сам по себе self - это просто переменная. Она может называться как угодно. Интерпретатору главное, чтобы первый аргумент метода класса был ссылкой на вызвавший его экземпляр. То есть первый аргумент таких методов - это ссылка на текущий экземпляр данного класса (что-то напоминающее указатель this, если знакомы с C++).
Написанное можно проиллюстрировать примером. Это...
my_list = List(str) # создан конкретный экземпляр класса
my_list.add('Hello')
...то же самое, что и это...
my_list = List(str)
List.add(my_list, 'Hello') # здесь my_list является указателем self в методе List.add
# то есть метод List.add применяется к экземпляру my_list
...просто в первом случае self передаётся первым аргументом автоматически, так как вызов метод осуществляется через экземпляр, а не через класс (как во втором случае).
Ответ на вопрос
В Python всё является объектом. Это справедливо и для self - он имеет тип своего класса - List в вашем случае. Поэтому метод List.add возвращает тот же объект, что его и вызвал. Тогда получается, что в строке вашего кода
print(my_list.add('Hello').count, "---", 1, 'How many items in the List?')
происходит следующее:
- Экземпляр
my_listвызывает методadd(...), передавая емуself=my_listиitem='Hello', то есть за кулисами интерпретатора происходит ровно это
List.add(self=my_list, item='Hello')
- В методе
addпроисходит добавлениеitemв списокself.items, который в данном случае является как быmy_list.items. Также обновляется значениеself.count. После чего метод возвращает объектself. Но погодите-ка... Ведьselfбыл передан при вызовеadd(...)и равнялсяmy_list... Всё верно! Тот же самыйmy_list(экземпляр классаList) и возвращается, только к нему добавлен новыйitemи обновлён счётчикcount. - Обновлённый и возвращённый
my_listтеперь обращается к своему полюcount, в котором хранится уже обновлённое значение.
Вот и вся "магия"! Это и значит "вернуть self из метода".
Как правильно отмечали люди в комментариях, в данном случае появляется возможность использовать цепочки вызовов. Вы бы могли сделать и так (и всё бы работало ровно так же)
my_list = List()
my_list.add('Hello')
print(my_list.count)
При этом метод List.add мог бы вовсе ничего не возвращать, изменив лишь внутреннее состояние экземпляра my_list.
Если бы в методе List.add возвращался self.count, то тип возвращаемого значения был бы int, а у объекта int нет поля count. Так что попытка сделать так my_list.add(...).count провалилась бы с ошибкой типа "объект типа int не имеет поля count".
P.S.
Помните, что не стоит называть свои переменные зарезервированными словами. Это может привести к непонятным трудноуловимым ошибкам в более серьёзном коде. В вашем случае, например, имя аргумента type в конструкторе перекрывает имя стандартной функции type(). Может стоит переименовать в dtype (datatype)?