Найти в Дзене
ZDG

Про ошибки Python-новичков и неверные их объяснения

Наткнулся в ленте на такой материал:

4 ошибки в коде на Python, которые выдают в вас новичка

Нет, это не дружеская реклама другого канала. Просто, так как я сам только недавно занялся Питоном, то считаю себя новичком, и пошёл посмотреть, какие ошибки могут меня выдать.

И знаете, увидел странную вещь по одному вопросу. Я не могу оттуда всё копировать, поэтому предлагаю просто ознакомиться с материалом по ссылке, а здесь напишу свои комментарии к нему.

1. Значения по умолчанию

Речь идёт о том, что в классе, например, можно объявить свойство и дать ему значение по умолчанию. Например, так:

Скриншот
Скриншот

В данном случае в классе House создаётся свойство cats, которому присваивается по умолчанию пустой список [].

Далее мы создаём два объекта класса House: my_house и office. Затем в объекте my_house обращаемся к свойству cats (то есть к тому самому пустому списку) и добавляем в него элемент 'Tom'.

После чего выясняется, что элемент 'Tom' теперь есть и в свойстве cats объекта office.

В чём дело? Как пишет Маша, у всех экземпляров класса House будет ссылка на один и тот же список cats. Да, это так.

Чтобы решить эту проблему, предлагается инициализировать свойство cats значением None:

Скриншот
Скриншот

И тогда всё работает.

Но это объяснение в корне неверно (или не так объяснено). После такого примера у новичка в голове отложится, что нужно писать не cats = [], а cats = None. И всё. А ведь кроме этого, в классе появился конструктор __init__(), где мы видим присваивание self.cats = []. Но про это не сказано ни слова.

Точнее говоря, данная проблема вообще никак не связана со значениями по умолчанию.

Что происходит на самом деле:

Свойство cats в классе Houseстатическое. Это значит, что все экземпляры класса (то есть объекты, созданные конструктором этого класса) видят одно и то же свойство. Так как оно инициализировано как список, то все объекты видят один указатель на список, и при добавлении в список элементов все объекты получают к ним доступ через указатель.

Но решает эту проблему вовсе не присваивание cats = None, а именно инструкция self.cats = [], которая находится в __init__(). В этом месте вместо статического, общего для всего класса свойства cats у объекта (то есть у непосредственно сейчас конструируемого экземпляра) создаётся СОБСТВЕННОЕ свойство self.cats, которое, имея то же самое имя, просто перекрывает статическое свойство. И этому свойству присваивается НОВЫЙ, то есть свой собственный, список. Именно поэтому оно оказывается изолированным от других объектов.

Это значит, что в объявлении статического свойства класса вы можете написать cats = [], или cats = None, или вообще не писать никакого cats. Это не изменит ничего, если в __init__() создаётся собственное свойство self.cats с собственным списком.

В моём примере я присвоил статическому свойству cats непустой список: cats = ['a']:

-3

Как можно видеть, объекты my_house и office получили собственные списки, но я всё ещё могу обратиться к статическому свойству cats через имя класса, т.е. House.cats.

Никакого отношения к значениям по умолчанию это не имеет. На что надо было обратить внимание? На то, что инструкция my_house.cats.append('Tom') обращается не к собственному свойству объекта, а к общему статическому свойству класса. Как только начали создавать собственное свойство в конструкторе, доступ к статическому свойству прекратился. Вот и всё.

По остальным пунктам у меня нет принципиальных возражений и даже рекомендую с ними ознакомиться.

Читайте также:

Наука
7 млн интересуются