diff --git a/lesson15.md b/lesson15.md index 3f799efd6..449fec9ea 100644 --- a/lesson15.md +++ b/lesson15.md @@ -4,63 +4,64 @@ ## Что такое ООП, и что же такое класс и объект. -Объектно-ориентированное программирование (в дальнейшем ООП) — парадигма программирования, в которой основными +Объектно-ориентированное программирование (в дальнейшем - ООП) — парадигма программирования, в которой основными концепциями являются понятия объектов и классов. Взаимодействие между операциями при помощи объектов. -Есть говорить человеческим языком, то в обычной жизни, мы не оперируем понятиями строка или кортеж, мы оперируем +Есть говорить человеческим языком, то в обычной жизни мы не оперируем понятиями строка или кортеж, мы оперируем объектами. -Мы говорим: "Подай чашку", "Пришли фотку", "Прикольный стол" итд. Так вот чашка, фотка и стол в этих примерах будут -являться объектами. Объекты могут обладать некоторыми атрибутами (цвет, название, размер итд.) и методами (они же -действия, например, на кнопку можно нажать, автомобиль может ехать, карандаш может писать итд.) +Мы говорим: "Подай чашку", "Пришли фотку", "Прикольный стол" и т. д. Так вот чашка, фотка и стол в этих примерах будут +являться объектами. Объекты могут обладать некоторыми атрибутами (цвет, название, размер и т. д.) и методами (они же +действия, например, на кнопку можно нажать, автомобиль может ехать, карандаш может писать и т. д.) В центре ООП находится понятие `объекта`. Объекты создаются на основе `классов`. -Класс это шаблон для создания объекта, допустим у нас есть класс автомобиль, автомобиль это класс, а конкретный лексус -2015 года выпуска, это уже объект класса автомобиль. +Класс - это шаблон для создания объекта. Допустим, у нас есть класс автомобиль, автомобиль - это класс, а конкретный +Lexus 2015 года выпуска - это уже объект класса автомобиль. -Объект — это сущность, экземпляр класса, содержащий свои аттрибуты и свои методы, созданный при помощи шаблона (т.е. -Класса). +Объект — это сущность, экземпляр класса, содержащий свои атрибуты и свои методы, созданный при помощи шаблона (т. е. +класса). -Аттрибут класса это данные принадлежащие классу, например цвет автомобиля (каждый автомобиль какого-то цвета, но пока у -нас нет "экземпляра" мы не можем сказать какого цвета просто понятие автомобиль) +Атрибут класса - это данные, принадлежащие классу, например, цвет автомобиля (каждый автомобиль какого-то цвета, но +пока у нас нет "экземпляра," мы не можем сказать, какого цвета просто понятие автомобиль). -Метод класса, это функция описанная внутри объекта и описывающая определенное действие, например кофемашина делает кофе. +Метод класса - это функция, описанная внутри объекта, которая описывает определенное действие, например, кофемашина +делает кофе. ### Ключевое слово `self` Что такое `self`? `self` - это специальный аргумент в методах класса, который является ссылкой на экземпляр. -В большинстве случаев (когда нет обсудим отдельно на следующих занятиях), первым аргументом любого метода будет `self`. +В большинстве случаев (когда нет, обсудим отдельно на следующих занятиях), первым аргументом любого метода будет `self`. Он обязательный. Чисто технически можно написать любое слово первым аргументом, и это тоже будет работать. Но не надо так делать. Не сбивайте ни себя, ни других разработчиков. Первый аргумент большинства методов это `self`. `self` - это конкретный объект внутри метода класса. Если у нас есть класс студент, то через `self` мы можем получить доступ ко всем атрибутам и методам конкретного -студента, например списку оценок, или методу "прогулять занятие". +студента, например, списку оценок или методу "прогулять занятие". -Доступ к атрибутам и методам получается через точку. +Доступ к атрибутам и методам предоставляется через точку. Еще про `self`: -- у котов внутри есть мурчалка -- она реализована для всех котов в классе Кот -- в объекте кот надо как то вызвать метод мурчало у класса Кот +- у котов внутри есть мурчалка; +- она реализована для всех котов в классе Кот; +- в объекте кот надо как-то вызвать метод мурчало у класса Кот; - как ты это сделаешь? - Кот.мурчало() -- ежели ты вызовешь Кот.мурчало(), муркнут сразу все коты на свете -- а ежели ты вызовешь self.мурчало(), муркнет только тот кот, на которого указывает self +- ежели ты вызовешь Кот.мурчало(), муркнут сразу все коты на свете; +- а ежели ты вызовешь self.мурчало(), муркнет только тот кот, на которого указывает self. -Наконец к коду: +Наконец, к коду: ```python # Используем ключевое слово `class` class Car: - # Опишем класс Машина, у которого будет два атрибута цвет и максимальная скорость. - # Я указал для них типы данных, но для питона это не обязательно, скорее удобный инструмент. + # Опишем класс Машина, у которого будет два атрибута: цвет и максимальная скорость. + # Я указал для них типы данных, но для Python это не обязательно, скорее, удобный инструмент. color: str = 'red' top_speed: int = 250 @@ -70,7 +71,7 @@ class Car: def find_color_and_top_speed(self): return f'this car top speed is {self.top_speed} and color is {self.color}' - # Вернуть булеан который отвечает на вопрос, может ли машина ехать с указанной скоростью + # Вернуть булево значение, которое отвечает на вопрос, может ли машина ехать с указанной скоростью def is_car_can_go_with_needed_speed(self, speed: int): return speed < self.top_speed @@ -78,39 +79,39 @@ class Car: def set_top_speed(self, speed: int): self.top_speed = speed - # Назначить кол-во колес. Обратите внимания, + # Назначить количество колес. Обратите внимания, # такого атрибута изначально вообще не было # (лучше так не делать, но технически нет никаких ограничений) def set_count_of_wheels(self, wheels: int): self.wheels = wheels -# Как создать объект? для этого нужно просто "вызвать" класс +# Как создать объект? Для этого нужно просто "вызвать" класс -lamborgini = Car() -print(lamborgini.find_color_and_top_speed()) # this car top speed is 250 and color is red -print(lamborgini.is_car_can_go_with_needed_speed(200)) # True +lamborghini = Car() +print(lamborghini.find_color_and_top_speed()) # This car's top speed is 250 and color is red +print(lamborghini.is_car_can_go_with_needed_speed(200)) # True # Доступ к атрибутам можно получить напрямую -print(lamborgini.top_speed) # 250 -lamborgini.set_top_speed(150) # Назначаем новую максимальную скорость для этого объекта -print(lamborgini.is_car_can_go_with_needed_speed(200)) # False +print(lamborghini.top_speed) # 250 +lamborghini.set_top_speed(150) # Назначаем новую максимальную скорость для этого объекта +print(lamborghini.is_car_can_go_with_needed_speed(200)) # False cherry = Car() -print(cherry.find_color_and_top_speed()) # this car top speed is 250 and color is red +print(cherry.find_color_and_top_speed()) # This car's top speed is 250 and color is red cherry.top_speed = 140 cherry.color = 'yellow' print(cherry.wheels) # ВЫЗОВЕТ ОШИБКУ, у нас нет такого атрибута cherry.set_count_of_wheels(4) print(cherry.wheels) # Все ок, напечатает 4 -cherry.size = 'small' # Можно добавлять атрибуты к любому объекту если это необходимо, у ламборджини такого атрибута не будет +cherry.size = 'small' # Можно добавлять атрибуты к любому объекту если это необходимо, у ламборгини такого атрибута не будет ``` **В python вообще все является объектом** -И строка, и число, и список, итд. +И строка, и число, и список и т. д. ## Парадигмы ООП -![](https://waksoft.susu.ru/wp-content/uploads/2020/08/oop.jpg) +[/не отображается/]: # (![](https://waksoft.susu.ru/wp-content/uploads/2020/08/oop.jpg)) ООП держится на трёх основных и одной второстепенной парадигме. @@ -122,17 +123,17 @@ cherry.size = 'small' # Можно добавлять атрибуты к лю полностью заимствующейся функциональностью. Класс, от которого производится наследование, называется базовым или родительским. Новый класс — потомком, наследником или производным классом. -Например, у нас есть базовый класс автомобиль и три наследника, легковой, самосвал и фура, все три класса могут иметь -общие атрибуты, например двигатель или материал лобового стекла, или методы, например газ и тормоз, но при этом иметь -свои особенные атрибуты или методы, например, только у фуры будет больше чем 4 колеса, или у самосвала, будет метод -поднять кузов, или у фуры отцепить груз. +Например, у нас есть базовый класс автомобиль и три наследника, легковой, самосвал и фура. Все три класса могут иметь +общие атрибуты, например, двигатель или материал лобового стекла, или методы, например, газ и тормоз, но при этом иметь +свои особенные атрибуты или методы, например, только у фуры будет больше 4 колес, у самосвала будет метод +поднять кузов, или у фуры - отцепить груз. -При описании ООП, мне очень нравятся бытовые примеры на автомобилях. Представте что при разработке новой модели BMW, -конструкторы решили бы начисто забыть, о том, что у них уже были предыдущие модели. Тогда им пришлось бы абсолютно +При описании ООП мне очень нравятся бытовые примеры на автомобилях. Представьте, что при разработке новой модели BMW, +конструкторы решили бы начисто забыть о том, что у них уже были предыдущие модели. Тогда им пришлось бы абсолютно каждую новую модель разрабатывать с нуля. Возможно ли это? Конечно. Есть ли в этом необходимость? Очень сомнительно. -Если можно взять прошлую модель немного изменить дизайн, поменять систему тормозов и вставить новые фары, и вуаля, новая -модель готова, при минимальных затратах и максимальном результате. С наследованием точно так же, можно описывать все -классы с нуля, но часто это очень не удобно и трудозатратно. +Если можно взять прошлую модель, немного изменить дизайн, поменять систему тормозов и вставить новые фары, и вуаля, +новая модель готова при минимальных затратах и максимальном результате. С наследованием точно также, можно описывать все +классы с нуля, но часто это очень неудобно и затратно. #### Пример кода @@ -150,13 +151,13 @@ class Car: self.current_speed = 0 -# тут класс унаследовал атрибуты wheels и current_speed, а так же все методы +# тут класс унаследовал атрибуты wheels и current_speed, а также все методы class Track(Car): doors = 2 max_speed = 120 -# а тут класс унаследовал все атрибуты кроме max_speed, а так же все методы +# а тут класс унаследовал все атрибуты кроме max_speed, а также все методы class SportCar(Car): max_speed = 350 @@ -172,36 +173,36 @@ print(sport.curent_speed) # 175 ![](https://studme.org/imag/inform/tuz_oop/image002.jpg) -### Метод `super` +### Метод `super()` -Практически всегда когда нам нужно в дочернем классе выполнить такое же действие как и в родительском нам необходимо -основываться на данных из родительского (в конце блока вы узнаете, что это даже целое правило) +Практически всегда, когда нам нужно в дочернем классе выполнить такое же действие как и в родительском, нам необходимо +основываться на данных из родительского (в конце блока вы узнаете, что это даже целое правило). -Но как вызвать код из другого класса? Нам поможет метод `super` +Но как вызвать код из другого класса? Нам поможет метод `super()`. -Допустим у нас есть класс, который занимается тем, что просто возвращает нам цену продукта. -И еще два класса которые считают скидку для цены в 20%. И второй еще 20% уже от уменьшенной цены. +Допустим, у нас есть класс, который занимается тем, что просто возвращает нам цену продукта. +И еще два класса, которые вычисляют скидку 20%. И второй класс отнимает еще 20% уже от уменьшенной цены. ```python class PriceCounter: price = 100 def calculate_price(self): - print('In original price') + print('In PriceCounter calculate price') return self.price class DiscountCounter(PriceCounter): def calculate_price(self): - print('In discount calculate') + print('In DiscountCounter calculate price: ') return super().calculate_price() * 0.8 class SuperDiscountCounter(DiscountCounter): def calculate_price(self): - print('In super discount calculate') + print('In SuperDiscountCounter calculate price: ') return super().calculate_price() * 0.8 @@ -211,20 +212,20 @@ super_discount_counter = SuperDiscountCounter() print(price_counter.calculate_price()) """ -In original price +In PriceDiscounter calculate price: 100 """ print(discount_counter.calculate_price()) """ -In discount calculate -In original price +In DiscountCounter calculate price: +In PriceDiscounter calculate price: 80.0 """ print(super_discount_counter.calculate_price()) """ -In super discount calculate -In discount calculate -In original price +In SuperDiscountCounter calculate price: +In DiscountCounter calculate price: +In PriceDiscounter calculate price: 64.0 """ ``` @@ -233,10 +234,10 @@ In original price #### Устаревшие синтаксисы -Для питона существуют несколько различных версий, включая 2.х и 3.х +Для Python существуют несколько различных версий, включая 2.х и 3.х. -Версии 2.х считаются устаревшими, но все таки иногда можно встретить код на втором питоне или "отнаследовавшийся от -него" +Версии 2.х считаются устаревшими, но все-таки иногда можно встретить код на Python 2.x или "отнаследовавшийся от +него". ```python class A(): # Вариант из python2, работать будет, но и удивлять коллег тоже @@ -245,9 +246,9 @@ class A(): # Вариант из python2, работать будет, но и class B(object): """ - Вариант который тоже будет работать - и на самом деле показывает нам суть любого класса и объекта в питоне, - вообще все унаследовано от объекта + Вариант, который тоже будет работать + и на самом деле показывает нам суть любого класса и объекта в Python, + вообще все унаследовано от объекта. """ @@ -255,16 +256,16 @@ class C: # Традиционный способ для объявления к pass ``` -Заодно познакомились с ключевым словом `pass`. Которое нужно в качестве `заглушки`, так как класс или функция не могут -быть пустыми, но могут быть с такой заглушкой. Вместо заглушки лучше все таки ставить комментарий, если есть +Заодно познакомились с ключевым словом `pass`, которое нужно в качестве `заглушки`, так как класс или функция не могут +быть пустыми, но могут быть с такой заглушкой. Вместо заглушки лучше все-таки ставить комментарий, если есть необходимость создать пустой класс (а она вполне бывает). -#### Методы `type`, `isinstance` и `issubclass` +#### Методы `type()`, `isinstance()` и `issubclass()` -У любого объекта всегда есть тип данных. И по факту этим типом данных всегда является класс (Да, `str`, `int`, `list` -итд. и даже функции это тоже классы) +У любого объекта всегда есть тип данных. И по факту этим типом данных всегда является класс (да, `str`, `int`, `list` +и т. д. и даже функции - это тоже классы) -Что бы узнать тип данных любого объекта необходимо вызвать метод `type` +Чтобы узнать тип данных любого объекта, необходимо вызвать метод `type()`. ```python class A: @@ -288,10 +289,10 @@ print(type(collection)) # print(type(some_func)) # ``` -Для того что бы сравнить узнать является ли объект подклассом существует специальная функция `isinstance`, принимает на -вход объект и класс, либо кортеж из классов, а возвращает булеан. +Для того чтобы сравнить и узнать, является ли объект подклассом, существует специальная функция `isinstance()`. Она +принимает на вход объект и класс, либо кортеж из классов, а возвращает булево значение. -И есть такая же функция, которая принимает не объект, а сам класс. `issubclass` +И есть такая же функция, которая принимает не объект, а сам класс: `issubclass()`. ```python class A: @@ -329,14 +330,14 @@ print(issubclass(type(a), (B, C))) # False Абстракция в объектно-ориентированном программировании — это использование только тех характеристик объекта, которые с достаточной точностью представляют его в данной системе. -Часто говорят что абстракция это не обязательная парадигма ООП. +Часто говорят, что абстракция - это не обязательная парадигма ООП. Вернемся к примерам с автомобилями. Когда мы управляем автомобилем, мы часто используем руль и поворотники, но часто ли -мы меняем настройку зеркал или подогрева сидений? Не особо. Так вот абстракция о том, что бы выделять главное, и не +мы меняем настройку зеркал или подогрева сидений? Не особо. Так вот абстракция о том, чтобы выделять главное и не тратить лишние ресурсы на второстепенное. Если говорить простыми словами, то это возможность описать реализацию метода только в том классе, где это необходимо. А -в родительском только описать название и возможно какие-то комментарии к будущей реализации. +в родительском только описать название и, возможно, какие-то комментарии к будущей реализации. ```python class Animal: @@ -358,22 +359,22 @@ class Lion(Animal): ``` -Если в этом примере не описать метод `sound`. То технически все будет работать, но любая IDE будет подсвечивать, что вы -не описали абстрактный метод. +Если в этом примере не описать метод `sound()`, то технически все будет работать, но любая IDE будет подсвечивать, что +вы не описали абстрактный метод. ### Полиморфизм ![](https://cdn.javarush.com/images/article/5d67355f-5702-4ba8-b1c2-5eb3f27fa5b5/800.jpeg) Полиморфизм — это свойство системы использовать объекты с одинаковым интерфейсом без информации о типе и внутренней -структуре объекта, т.е. способность одной функции (метода), -действовать по-разному в зависимости от обстоятельств (которые мы сами указываем). +структуре объекта, т. е. способность одной функции (метода), действовать по-разному в зависимости от обстоятельств +(которые мы сами указываем). -По сути это возможность использовать одни и те же методы или интерфейсы к различным структурам, например обычный +По сути, это возможность использовать одни и те же методы или интерфейсы к различным структурам, например, обычный знак `+`, ведь мы можем сложить числа, а можем и строки, и получим разный результат, но применим один и тот же метод. В примере с автомобилем, если мы умеем водить мерседес, то скорее всего у нас не вызовет проблема управлять тойотой или -фордом. Ведь для управления мы будем использовать точно те же самые интерфейсы (руль, педали, поворотники, итд.) +фордом. Ведь для управления мы будем использовать точно те же самые интерфейсы (руль, педали, поворотники и т. д.) ```python class English: @@ -393,44 +394,43 @@ def intro(language): flora = English() -aalase = French() +michelle = French() intro(flora) -intro(aalase) +intro(michelle) ``` Прошлый пример из абстракции тоже подходит, ведь разные звери издают разные звуки. А для нас это не имеет значения, мы -все-го лишь вызовем метод `sound`. +всего лишь вызовем метод `sound()`. ### Инкапсуляция ![](https://cdn.javarush.com/images/article/a3a32228-a349-4de8-b402-16c19fc4db88/1024.jpeg) -`Инкапсуляция` - Принцип "убирания с глаз" функционала или данных с предоставлением только входных и выходных +`Инкапсуляция` - принцип "убирания с глаз" функционала или данных с предоставлением только входных и выходных параметров. Например, если в автомобиле повернуть руль налево, то колёса тоже повернутся налево, но как именно это -происходит от нас скрыто, мы не -знаем какие именно рычаги и шестерёнки в этот момент двигаются внутри автомобиля. +происходит от нас скрыто, мы не знаем, какие именно рычаги и шестерёнки в этот момент двигаются внутри автомобиля. -Так же, например, с кофемашиной, мы знаем, что если нажать кнопку с капучино, то мы получим чашку кофе. Какие в этот +Так же, например, с кофе машиной, мы знаем, что если нажать кнопку с капучино, то мы получим чашку кофе. Какие в этот момент процессы происходят внутри, нас практически не интересует. Что-то нагревается, где-то по трубкам бежит жидкость. -Но нас интересует только результат. Могу ли я разобрать кофемашину, и посмотреть из чего она состоит? Могу, но у меня +Но нас интересует только результат. Могу ли я разобрать кофемашину и посмотреть, из чего она состоит? Могу, но у меня нет в этом необходимости. -Так же и в программировании, мы можем "скрыть" внутренние процессы +Так же и в программировании, мы можем "скрыть" внутренние процессы. ![](https://senior.ua/storage/article/651ea79a-83e1-4f4a-9ca4-8968b7ae86f9.jpeg) -В пайтоне инкапсуляция очень условная (всегда можно получить доступ куда угодно, было бы желание). Как сказал создатель -языка, Гвидо Ван Россум, ну мы все же взрослые люди, зачем мы будем кого-то ограничивать. +В Python инкапсуляция очень условная (всегда можно получить доступ куда угодно, было бы желание). Как сказал создатель +языка, Гвидо Ван Россум: "Мы все же взрослые люди, зачем мы будем кого-то ограничивать?" В первую очередь код пишется для людей, поэтому и разделение существует на уровне понимания людей. Существует три вида состояния атрибутов и свойств, и для их разделения используется специальный синтаксис. -1. Атрибуты и методы чьи названия начинаются с букв, называются `public`. И они доступны везде. В объекте, в классе, в +1. Атрибуты и методы, чьи названия начинаются с букв, называются `public`. И они доступны везде. В объекте, в классе, в наследовании. -2. Атрибуты и методы чьи названия начинаются с символа `_`. Они называются `protected`, и подразумевается, что мы будем +2. Атрибуты и методы, чьи названия начинаются с символа `_`. Они называются `protected`, и подразумевается, что мы будем их использовать исключительно в классе и в наследовании, но не будем использовать в объектах. 3. Атрибуты и методы которые начинаются с `__`. Они называются `private` и подразумевается, что мы будем их использовать @@ -445,22 +445,22 @@ class Car: __max_carrying = 1000 def find_color_and_top_speed(self): - return 'this car top speed is {} and color is {}'.format(self._top_speed, self.color) + return f'This car top speed is {self._top_speed} and color is {self.color}.' - def is_can_go_with_needed_speed(self, speed): + def can_go_with_needed_speed(self, speed): return speed < self._top_speed - def is_can_get_weight(self, weight): + def can_get_weight(self, weight): return self.__max_carrying > weight def change_max_carrying(self, new_carrying): self.__max_carrying = new_carrying def __private_method(self): - print('this is private method') + print('This is private method') def _this_is_protected_method(self): - print('this is protected method') + print('This is protected method') def run_hidden_and_protected_methods(self): self.__private_method() @@ -469,9 +469,9 @@ class Car: car = Car() car.color # всё нормально -car._top_speed # сработает, но мы же сами описали это свойство так что бы сообщить, что не надо так его использовать +car._top_speed # сработает, но мы же сами описали это свойство так, чтобы сообщить, что не надо так его использовать car.__max_carrying # не сработает (будет ошибка, что этот атрибут не найден) -car._Car__max_carrying # Сработает и это как раз описание того, что добраться можно куда угодно. Но сам синтаксис нам говорит, что мы что-то не то делаем +car._Car__max_carrying # Сработает, и это как раз описание того, что добраться можно куда угодно. Но сам синтаксис нам говорит, что мы что-то не то делаем car.__max_carrying = 800 # не сработает car.change_max_carrying(800) # сработает сar.__hidden_method() # не сработает (будет ошибка, что этого метода не существует) @@ -486,44 +486,46 @@ car.run_hidden_and_protected_methods() # сработает и вызовет 1. Описываем телефон: Класс телефон. У него должны быть: - - Поле, для описания номера - - Метод, что-бы задать номер телефона - - Защищенное поле для счетчика входящих звонков - - Метод, который вернет нам количество принятых звонков - - Метод принять звонок, который добавляет к счетчику единицу + - Поле для описания номера. + - Метод, чтобы задать номер телефона. + - Защищенное поле для счетчика входящих звонков. + - Метод, который вернет нам количество принятых звонков. + - Метод принять звонок, который добавляет к счетчику единицу. Создайте три разных объекта телефона. Поменяйте всем изначальный номер. - Примите по несколько звонков на каждом (разное кол-во) + Примите по несколько звонков на каждом (разное количество) - Напишите функцию, которая принимает список из объектов телефонов, а возвращает общее кол-во принятых звонков со всех - телефонов. + Напишите функцию, которая принимает список из объектов телефонов, а возвращает общее количество принятых звонков + со всех телефонов. 2. Опишите класс для шахматной фигуры. Фигура должна содержать такие атрибуты: - - Цвет (белый или черный) + - Цвет (белый или черный). - Место на доске (тут есть варианты, или два отдельных поля, для описания координат или одно, но, например, кортеж - из двух чисел) + из двух чисел). И такие методы как: - - Изменить цвет (ничего не принимает, только меняет цвет на противоположный) + - Изменить цвет (ничего не принимает, только меняет цвет на противоположный). - Изменить место на доске (принимает или две переменные, или один кортеж из двух элементов), не забудьте проверить, - что мы не пытаемся поставить фигуру за пределы доски (оба значения от 0 до 7) - - Абстрактный метод проверки потенциального хода (детали ниже) - На данном этапе фигуры могут стоять на одной и тоже же клетке, пока нам это не важно - - Опишите классы, для пешки, коня, офицера, ладьи, ферзя и короля. - Все что в них нужно добавить это один метод для проверки, - возможно ли за один ход поменять место фигуры на доске (все ходят по-разному, у пешек будет еще и разница от цвета). Метод принимает опять же или две цифры, или один кортеж. И - опять же проверяем не выходит ли значение за пределы доски (Так как нам необходим этом функционал дважды, я бы делал - его как отдельный защищенный метод в родительском классе) - - И функцию, которая принимает список фигур и потенциальную новую клетку, а возвращает список из фигур. Но только тех + что мы не пытаемся поставить фигуру за пределы доски (оба значения от 0 до 7). + - Абстрактный метод проверки потенциального хода (детали ниже). + На данном этапе фигуры могут стоять на одной и той же клетке, пока нам это не важно. + + Опишите классы для пешки, коня, офицера, ладьи, ферзя и короля. + Все, что в них нужно добавить - это один метод для проверки, + возможно, ли за один ход поменять место фигуры на доске (все ходят по-разному, у пешек будет еще и разница от цвета). + Метод принимает опять же или две цифры, или один кортеж. И опять же проверяем, не выходит ли значение за пределы + доски (Так как нам необходим этом функционал дважды, я бы делал его как отдельный защищенный метод в родительском + классе) + + И функцию, которая принимает список фигур и потенциальную новую клетку, а возвращает список из фигур. Но только тех, которые могут за один ход добраться до этой клетки. -3. Везде описать типизации (в функциях, атрибутах и методах) -4. ЗАДАЧА СО ЗВЁЗДОЧКОЙ. Сохранять информацию о принятых звонках в базу данных при помощи кода. (Пока только сохранять, читать будем позже) +3. Везде описать типизации (в функциях, атрибутах и методах). +4. ЗАДАЧА СО ЗВЁЗДОЧКОЙ. Сохранять информацию о принятых звонках в базу данных при помощи кода. + (Пока только сохранять, читать будем позже) - Для этого понадобится установить модуль `psycopg2` через `pip install` и разобраться как им пользоваться. \ No newline at end of file + Для этого понадобится установить модуль `psycopg2` через `pip install` и разобраться, как им пользоваться. diff --git a/lesson16.md b/lesson16.md index 616ba104c..3b7dbb187 100644 --- a/lesson16.md +++ b/lesson16.md @@ -6,22 +6,21 @@ ![](https://python-course.eu/images/oop/clock_calendar_500w.webp) -Множественное наследование это возможность у класса потомка наследовать функционал не от одного, а от нескольких -родителей. -Благодаря этому мы можем создавать сложные структуры, сохраняя простой и легко-поддерживаемый код. +Множественное наследование - это возможность у класса потомка наследовать функционал не от одного, а от нескольких +родителей. Благодаря этому мы можем создавать сложные структуры, сохраняя простой и легко-поддерживаемый код. -Во многих языках программирования нет множественного наследования, так что давайте разбираться как это вообще работает. +Во многих языках программирования нет множественного наследования, так что давайте разбираться, как это вообще работает. Например, у нас есть класс автомобиля: ```python class Auto: def ride(self): - print("Riding on a ground") + print("Riding on ground") ``` -Так же у нас есть класс для лодки: +Также у нас есть класс для лодки: ```python class Boat: @@ -31,7 +30,7 @@ class Boat: ``` Теперь, если нам нужно запрограммировать автомобиль-амфибию, который будет плавать в воде и ездить по земле, мы вместо -написания нового класса, можем просто унаследовать от уже существующих, просто написав их через запятую: +написания нового класса можем просто унаследовать от уже существующих, просто написав их через запятую: ```python class Auto: @@ -55,9 +54,9 @@ a.swim() ![](https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSYboaqixXBn-OWESELK2fLejC9fvKLL4QvBA&usqp=CAU) -Теперь наш класс имеет атрибуты и методы обоих родителей (их может быть сколько угодно) +Теперь наш класс имеет атрибуты и методы обоих родителей (их может быть сколько угодно). -Обратите внимание, что объект класса Amphibian, будет одновременно объектом класса Auto и Boat, то есть: +Обратите внимание, что объект класса Amphibian будет одновременно объектом класса Auto и Boat, то есть: ```python a = Amphibian() @@ -69,9 +68,9 @@ isinstance(a, Amphibian) # True ``` -### Миксины +### Миксины (Mixins) -Миксин, он же примесь, это тип классов которые нужны, что бы добавлять к обычным классам какие-то методы или атрибуты, +Миксин, он же примесь, — это тип классов, которые нужны, чтобы добавлять к обычным классам какие-то методы или атрибуты, но эти классы не используются для создания объектов, только как примесь. (Нас ничего не останавливает создать объект этого класса, но задача в другом) @@ -85,7 +84,7 @@ class Car: print("Riding a car") def play_music(self, song): - print("Now playing: {} ".format(song)) + print(f"Now playing: {song}.") c = Car() @@ -95,13 +94,13 @@ c.play_music("Queen - Bohemian Rhapsody") # Now playing: Queen - Bohemian Rhapsody ``` -Но что если, у нас есть еще и телефон, радио или любой другой девайс, с которого мы будем слушать музыку. -В таком случае, лучше вынести функционал проигрывания музыки в отдельный класс-миксин: +Но что, если у нас есть еще и телефон, радио или любой другой девайс, с которого мы будем слушать музыку. +В таком случае лучше вынести функционал проигрывания музыки в отдельный класс-миксин: ```python class MusicPlayerMixin: def play_music(self, song): - print("Now playing: {}".format(song)) + print(f"Now playing: {song}.") ``` Мы можем "домешивать" этот класс в любой, где нужна функция проигрывания музыки: @@ -154,37 +153,34 @@ d = D() d.hi() ``` -Эта ситуация, так называемое ромбовидное наследование (diamond problem) решается в Python путем установления порядка +Эта ситуация, так называемое ромбовидное наследование (diamond problem), решается в Python путем установления порядка разрешения методов. В Python3 для определения порядка используется алгоритм поиска в ширину, то есть сначала интерпретатор будет искать -метод hi в классе B, если его там нету - в классе С, потом A. +метод `hi()` в классе B, если его там нет - в классе С, потом A. В Python второй версии используется алгоритм поиска в глубину, то есть в данном случае - сначала B, потом - А, потом С. -В Pytho3 можно посмотреть в каком порядке будут проинспектированы родительские классы при помощи метода класса mro(): +В Pytho3 можно посмотреть в каком порядке будут проинспектированы родительские классы при помощи метода класса `mro()`: ### MRO - Method resolution order -Что бы посмотреть в каком порядке питон будет искать атрибуты или методы у родителей, у любого класса можно вызывать -метод `mro`: +Чтобы посмотреть, в каком порядке Python будет искать атрибуты или методы у родителей, у любого класса можно вызывать +метод `mro()`: ```python ->> > D.mro() -[ < - - -class '__main__.D'>, < class '__main__.B' >, < class '__main__.C' >, < class '__main__.A' >, < class 'object' >] +>> D.mro() +[ , < class '__main__.B' >, < class '__main__.C' >, < class '__main__.A' >, < class 'object'>] ``` -Обратите внимание в конце всегда будет `object` если вы используете любой питон версии 3. +Обратите внимание, в конце всегда будет `object`, если вы используете любой Python версии 3. Потому что вообще все отнаследовано от него, как я и говорил на прошлом занятии. -Если по какой-то причине вас не устраивает существующий порядок есть возможность вызвать метод ровно из того класса +Если по какой-то причине вас не устраивает существующий порядок, есть возможность вызвать метод ровно из того класса, откуда вам надо, но это считается плохой практикой и лучше так не делать, а полностью поменять структуру. -Если вам необходимо использовать метод конкретного родителя, например hi() класса С, нужно напрямую вызвать его по имени -класса, передав self в качестве аргумента: +Если вам необходимо использовать метод конкретного родителя, например, `hi()` класса С, нужно напрямую вызвать его по +имени класса, передав `self` в качестве аргумента: ```python class D(B, C): @@ -198,23 +194,22 @@ d.call_hi() [Большая статья про МРО и вообще множественное наследование тут](https://habr.com/ru/post/62203/?_ga=2.205768979.1207595081.1598867257-330984554.1578271027) -## Magic methods (Они же ногда называются дандер методы) +## Magic methods (Они же иногда называются dunder-методы) -Что такое магические методы? Они всё в объектно-ориентированном Питоне. -Это специальные методы, с помощью которых вы -можете добавить в ваши классы «магию». -Они всегда обрамлены двумя нижними подчеркиваниями, два вначале, два в конце (например, __init__ или __lt__) +Что такое магические методы? Они всё в объектно-ориентированном Python. +Это специальные методы, с помощью которых вы можете добавить в ваши классы «магию». +Они всегда обрамлены двумя нижними подчеркиваниями, два вначале, два в конце (например, `__init__` или `__lt__`). -Это методы, которые отвечают за любые действия под "капотом". Те которые выполняются неявно, допустим, -вам нужно сложить два объекта через `+`, нам нужен магический метод `__add__` или мы хотим поменять поведение при удалении `__del__`, поведение при переборе объекта -в цикле `__iter__` или `__next__` итд. +Это методы, которые отвечают за любые действия под "капотом". Те, которые выполняются неявно. Допустим, +вам нужно сложить два объекта через оператор `+`, нам нужен магический метод `__add__`, или мы хотим поменять поведение +при удалении `__del__`, поведение при переборе объекта в цикле `__iter__` или `__next__` и т. д. -Почти любое действие которое вам кажется выполняется само собой, скорее всего описано в меджик методе. +Почти любое действие, которое вам кажется выполняется само собой, скорее всего описано в magic методе. -### init, new, del +### init(), new(), del() При создании (инициализации) объекта вызывается метод `__init__` и помогает нам задать стартовые параметры для нашего -класса +класса. ```python class A: @@ -229,42 +224,46 @@ a.some_arg_one a.some_arg_two # 22 ``` -Такой способ куда предпочтительнее, чем задавать какое-то бесполезное значение вначале и сразу же его поменять (номер телефона или место на доске из прошлой домашки) +Такой способ предпочтительнее, чем вначале задавать какое-то бесполезное значение и сразу же его поменять (номер +телефона или место на доске из прошлой домашки). -Когда я пишу x = SomeClass(), `__init__` не самое первое, что вызывается. На самом деле, экземпляр объекта создаёт метод `__new__`, а затем аргументы передаются в инициализатор. На другом конце жизненного цикла объекта находится метод `__del__`. +Когда я пишу `x = SomeClass()`, `__init__` не самое первое, что вызывается. На самом деле, экземпляр объекта создаёт +метод `__new__`, а затем аргументы передаются в инициализатор. На другом конце жизненного цикла объекта находится +метод `__del__`. Давайте подробнее рассмотрим эти три магических метода: `__new__(cls, [...)` Это первый метод, который будет вызван при инициализации объекта. Он принимает в качестве параметров класс и потом любые -другие аргументы, которые будут переданы в `__init__`. `__new__` используется весьма редко, но иногда бывает полезен, в -частности, когда класс наследуется от неизменяемого (immutable) типа, такого как кортеж (tuple) или строка. Я не намерен -очень детально останавливаться на `__new__`, так как он не то чтобы очень часто нужен, но этот метод очень хорошо и +другие аргументы, которые будут переданы в `__init__`. Метод `__new__` используется весьма редко, но иногда бывает +полезен, в частности, когда класс наследуется от неизменяемого (immutable) типа, такого как кортеж (tuple) или строка. +Я не намерен очень детально останавливаться на `__new__`, так как он не очень часто нужен, и этот метод очень хорошо и детально описан в документации. `__init__(self, [...)` Инициализатор класса. Ему передаётся всё, с чем был вызван первоначальный конструктор (так, например, если мы вызываем -x = SomeClass(10, 'foo'), `__init__` получит 10 и 'foo' в качестве аргументов. `__init__` почти повсеместно используется при -определении классов. +`x = SomeClass(10, 'foo')`, `__init__` получит 10 и 'foo' в качестве аргументов. `__init__` почти повсеместно +используется при определении классов. `__del__(self)` -Если `__new__` и `__init__` образуют конструктор объекта, `__del__` это его деструктор. Он не определяет поведение для -выражения `del x` (поэтому этот код не эквивалентен `x.__del__()`). Скорее, он определяет поведение объекта в то время, -когда объект попадает в сборщик мусора. Это может быть довольно удобно для объектов, которые могут требовать +Если `__new__` и `__init__` образуют конструктор объекта, то `__del__` - это его деструктор. Он не определяет поведение +для выражения `del x` (поэтому этот код не эквивалентен `x.__del__()`). Скорее, он определяет поведение объекта в то +время, когда объект попадает в сборщика мусора. Это может быть довольно удобно для объектов, которые могут требовать дополнительных чисток во время удаления, таких как сокеты или файловые объекты. Однако, нужно быть осторожным, так как нет гарантии, что `__del__` будет вызван, если объект продолжает жить, когда интерпретатор завершает работу. Поэтому `__del__` не может служить заменой для хороших программистских практик (всегда завершать соединение, если -закончил с ним работать и тому подобное). Фактически, из-за отсутствия гарантии вызова, `__del__` не должен использоваться -почти никогда; используйте его с осторожностью +закончил с ним работать и тому подобное). Фактически, из-за отсутствия гарантии вызова `__del__` не должен +использоваться почти никогда; используйте его с осторожностью. #### Магические методы сравнения -В Питоне огромное количество магических методов, созданных для определения интуитивного сравнения между объектами -используя операторы, а не плохо выглядящие методы (что лучше? `a==b` или `a.is_equal(b)` . Кроме того, они предоставляют способ переопределить поведение Питона -по-умолчанию для сравнения объектов (по ссылке). Вот список этих методов и что они делают: +В Python огромное количество магических методов, созданных для определения интуитивного сравнения между объектами при +помощи операторов, а не плохо выглядящих методов (Что лучше? `a==b` или `a.is_equal(b)` . Кроме того, они предоставляют +способ переопределить поведение Python по умолчанию для сравнения объектов (по ссылке). Вот список этих методов и что +они делают: `__eq__(self, other)` @@ -296,14 +295,14 @@ x = SomeClass(10, 'foo'), `__init__` получит 10 и 'foo' в качест ```python class Word(str): - '''Класс для слов, определяющий сравнение по длине слов.''' + """Класс для слов, определяющий сравнение по длине слов.""" def __new__(cls, word): # Мы должны использовать __new__, так как тип str неизменяемый # и мы должны инициализировать его раньше (при создании) if ' ' in word: print("Value contains spaces. Truncating to first space.") - word = word[:word.index(' ')] # Теперь Word это все символы до первого пробела + word = word[:word.index(' ')] # Теперь Word - это все символы до первого пробела return str.__new__(cls, word) def __gt__(self, other): @@ -356,7 +355,7 @@ class Word(str): `__divmod__(self, other)` -Определяет поведение для встроенной функции divmod(). +Определяет поведение для встроенной функции `divmod()`. `__pow__` @@ -384,8 +383,8 @@ class Word(str): ### Магические методы преобразования типов -Кроме того, в Питоне множество магических методов, предназначенных для определения поведения для встроенных функций -преобразования типов, таких как float(). Вот они все: +Кроме того, в Python множество магических методов, предназначенных для определения поведения для встроенных функций +преобразования типов, таких как `float()`. Вот они все: `__int__(self)` @@ -414,7 +413,7 @@ class Word(str): `__index__(self)` Преобразование типа к int, когда объект используется в срезах (выражения вида [start:stop:step]). Если вы определяете -свой числовой тип, который может использоваться как индекс списка, вы должны определить __index__. +свой числовой тип, который может использоваться как индекс списка, вы должны определить `__index__`. `__trunc__(self)` @@ -422,13 +421,13 @@ class Word(str): `__coerce__(self, other)` -Метод для реализации арифметики с операндами разных типов. __coerce__ должен вернуть None если преобразование типов -невозможно. Если преобразование возможно, он должен вернуть пару (кортеж из 2-х элементов) из self и other, +Метод для реализации арифметики с операндами разных типов. `__coerce__` должен вернуть `None`, если преобразование типов +невозможно. Если преобразование возможно, он должен вернуть пару (кортеж из 2-х элементов) из `self` и `other`, преобразованные к одному типу. ### Представление своих классов -Часто бывает полезно представление класса в виде строки. В Питоне существует несколько методов, которые вы можете +Часто бывает полезно представление класса в виде строки. В Python существует несколько методов, которые вы можете определить для настройки поведения встроенных функций при представлении вашего класса. `__str__(self)` @@ -437,28 +436,28 @@ class Word(str): `__repr__(self)` -Определяет поведение функции repr(), вызыванной для экземпляра вашего класса. Главное отличие от str() в целевой -аудитории. repr() больше предназначен для машинно-ориентированного вывода (более того, это часто должен быть валидный -код на Питоне), а str() предназначен для чтения людьми. +Определяет поведение функции repr(), вызванной для экземпляра вашего класса. Главное отличие от `str()` в целевой +аудитории. `repr()` больше предназначен для машинно-ориентированного вывода (более того, это часто должен быть валидный +код на Python), а `str()` предназначен для чтения людьми. `__unicode__(self)` -Определяет поведение функции unicode(), вызыванной для экземпляра вашего класса. unicode() похож на str(), но возвращает -строку в юникоде. Будте осторожны: если клиент вызывает str() на экземпляре вашего класса, а вы определили только _ -_unicode__(), то это не будет работать. Постарайтесь всегда определять __str__() для случая, когда кто-то не имеет такой -роскоши как юникод. +Определяет поведение функции unicode(), вызванной для экземпляра вашего класса. `unicode()` похож на `str()`, но возвращает +строку в Unicode. Будьте осторожны: если клиент вызывает str() на экземпляре вашего класса, а вы определили только +`__unicode__()`, то это не будет работать. Постарайтесь всегда определять `__str__()` для случая, когда кто-то не имеет +такой роскоши как Unicode. `__format__(self, formatstr)` Определяет поведение, когда экземпляр вашего класса используется в форматировании строк нового стиля. Например, "Hello, -{0:abc}!".format(a) приведёт к вызову a.__format__("abc"). Это может быть полезно для определения ваших собственных +{0:abc}!".format(a) приведёт к вызову `a.__format__("abc")`. Это может быть полезно для определения ваших собственных числовых или строковых типов, которым вы можете захотеть предоставить какие-нибудь специальные опции форматирования. `__hash__(self)` Определяет поведение функции hash(), вызванной для экземпляра вашего класса. Метод должен возвращать целочисленное значение, которое будет использоваться для быстрого сравнения ключей в словарях. Заметьте, что в таком случае обычно -нужно определять и __eq__ тоже. Руководствуйтесь следующим правилом: a == b подразумевает hash(a) == hash(b). +нужно определять и `__eq__` тоже. Руководствуйтесь следующим правилом: `a == b` подразумевает `hash(a) == hash(b)`. `__bool__(self)` (`__nonzero__(self)` в python < 3.0) @@ -468,15 +467,14 @@ _unicode__(), то это не будет работать. Постарайте `__dir__(self)` Определяет поведение функции dir(), вызванной на экземпляре вашего класса. Этот метод должен возвращать пользователю -список атрибутов. Обычно, определение __dir__ не требуется, но может быть жизненно важно для интерактивного -использования вашего класса, если вы переопределили __getattr__ или __getattribute__ (с которыми вы встретитесь в +список атрибутов. Обычно определение __dir__ не требуется, но может быть жизненно важно для интерактивного +использования вашего класса, если вы переопределили `__getattr__` или `__getattribute__` (с которыми вы встретитесь в следующей части), или каким-либо другим образом динамически создаёте атрибуты. `__sizeof__(self)` -Определяет поведение функции sys.getsizeof(), вызыванной на экземпляре вашего класса. Метод должен вернуть размер вашего -объекта в байтах. Он главным образом полезен для классов, определённых в расширениях на C, но всё-равно полезно о нём -знать. +Определяет поведение функции sys.getsizeof(), вызванной на экземпляре вашего класса. Метод должен вернуть размер вашего +объекта в байтах. Он полезен для классов, определённых в расширениях на C, но всё-равно полезно о нём знать. ### Протоколы @@ -488,7 +486,7 @@ _unicode__(), то это не будет работать. Постарайте #### Магия контейнеров -Без дальнейшего промедления, вот магические методы, используемые контейнерами: +Без дальнейшего промедления вот магические методы, используемые контейнерами: `__len__(self)` @@ -496,14 +494,14 @@ _unicode__(), то это не будет работать. Постарайте `__getitem__(self, key)` -Определяет поведение при доступе к элементу, используя синтаксис `self[key]`. Тоже относится и к протоколу изменяемых и -к протоколу неизменяемых контейнеров. Должен выбрасывать соответствующие исключения: TypeError если неправильный тип -ключа и KeyError если ключу не соответствует никакого значения. +Определяет поведение при доступе к элементу, используя синтаксис `self[key]`. То же относится и к протоколу изменяемых и +к протоколу неизменяемых контейнеров. Должен выбрасывать соответствующие исключения: TypeError, если неправильный тип +ключа, и KeyError, если ключу не соответствует никакого значения. `__setitem__(self, key, value)` Определяет поведение при присваивании значения элементу, используя синтаксис `self[nkey] = value`. Часть протокола -изменяемого контейнера. Опять же, вы должны выбрасывать KeyError и TypeError в соответсвующих случаях. +изменяемого контейнера. Опять же, вы должны выбрасывать KeyError и TypeError в соответствующих случаях. `__delitem__(self, key)` @@ -512,57 +510,57 @@ _unicode__(), то это не будет работать. Постарайте `__iter__(self)` -Должен вернуть итератор для контейнера. Итераторы возвращаются в множестве ситуаций, главным образом для встроенной +Должен вернуть итератор для контейнера. Итераторы возвращаются в множестве ситуаций, главным образом, для встроенной функции `iter()` и в случае перебора элементов контейнера выражением `for x in container:`. Итераторы сами по себе объекты и они тоже должны определять метод `__iter__`, который возвращает `self`. `__reversed__(self)` -Вызывается чтобы определить поведения для встроенной функции `reversed()`. Должен вернуть обратную версию -последовательности. Реализуйте метод только если класс упорядоченный, как список или кортеж. +Вызывается, чтобы определить поведения для встроенной функции `reversed()`. Должен вернуть обратную версию +последовательности. Реализуйте метод, только если класс упорядоченный, как список или кортеж. `__contains__(self, item)` `__contains__` предназначен для проверки принадлежности элемента с помощью `in` и `not in`. Вы спросите, почему же это -не часть протокола последовательности? Потому что когда `__contains__` не определён, Питон просто перебирает всю -последовательность элемент за элементом и возвращает True если находит нужный. +не часть протокола последовательности? Потому что когда `__contains__` не определён, Python просто перебирает всю +последовательность элемент за элементом и возвращает True, если находит нужный. `__next__(self)` -Возвращает следующий объкт в последовательности или StopIteration если больше обхектов нет. +Возвращает следующий объект в последовательности или StopIteration, если больше объектов нет. ### Вызываемые объекты -Как вы наверное уже знаете, в Питоне функции являются объектами первого класса. Это означает, что они могут быть +Как вы, наверное, уже знаете, в Python функции являются объектами первого класса. Это означает, что они могут быть переданы в функции или методы так же, как любые другие объекты. Это невероятно мощная особенность. -Специальный магический метод позволяет экземплярам вашего класса вести себя так, как будто они функции, тоесть вы +Специальный магический метод позволяет экземплярам вашего класса вести себя так, как будто они функции, то есть вы сможете «вызывать» их, передавать их в функции, которые принимают функции в качестве аргументов и так далее. Это другая -удобная особенность, которая делает программирование на Питоне таким приятным. +удобная особенность, которая делает программирование на Python таким приятным. `__call__(self, [args...])` -Позволяет любому экземпляру вашего класса быть вызванным как-будто он функция. Главным образом это означает, что `x()` -означает то же, что и `x.__call__()`. Заметьте, `__call__` принимает произвольное число аргументов; то есть, вы можете -определить `__call__` так же как любую другую функцию, принимающую столько аргументов, сколько вам нужно. +Позволяет любому экземпляру вашего класса быть вызванным как будто он функция. Главным образом, это означает, что `x()` +означает то же, что и `x.__call__()`. Заметьте, `__call__()` принимает произвольное число аргументов; то есть вы можете +определить `__call__()` так же как любую другую функцию, принимающую столько аргументов, сколько вам нужно. -`__call__`, в частности, может быть полезен в классах, чьи экземпляры часто изменяют своё состояние. «Вызвать» экземпляр -может быть интуитивно понятным и элегантным способом изменить состояние объекта. Примером может быть класс, +`__call__()`, в частности, может быть полезен в классах, чьи экземпляры часто изменяют своё состояние. «Вызвать» +экземпляр может быть интуитивно понятным и элегантным способом изменить состояние объекта. Примером может быть класс, представляющий положение некоторого объекта на плоскости: ```python class Entity: - '''Класс, описывающий объект на плоскости. "Вызываемый", чтобы обновить позицию объекта.''' + """Класс, описывающий объект на плоскости. "Вызываемый", чтобы обновить позицию объекта.""" def __init__(self, size, x, y): self.x, self.y = x, y self.size = size def __call__(self, x, y): - '''Изменить положение объекта.''' + """Изменить положение объекта.""" self.x, self.y = x, y ``` -На самом деле меджик методов гораздо больше, и вы можете изучить их из документации если вам необходимо, но практически -любое действие можно переопределить при помощи мейджик методов. +На самом деле, magic методов гораздо больше, и вы можете изучить их из документации, если вам необходимо, но практически +любое действие можно переопределить при помощи magic методов. -[хорошая статья по теме](https://habr.com/ru/post/186608/) +[Хорошая статья по теме](https://habr.com/ru/post/186608/) diff --git a/lesson17.md b/lesson17.md index 4c8a0bece..436d92aee 100644 --- a/lesson17.md +++ b/lesson17.md @@ -3,43 +3,62 @@ ![](https://ymatuhin.ru/assets/img/styleguide/styleguide.jpg) -## PEP8 +## [PEP 8](https://peps.python.org/pep-0008/) PEP - Python Enhanced Proposal, рекомендация по код стайлингу (следовать обязательно:) -Гвидо ван Россум - создатель языка python +Гвидо ван Россум - создатель языка Python. -Ключевая идея Гвидо такова: код читается намного больше раз, чем пишется. Собственно, рекоммендации о стиле написания кода направлены на то, чтобы улучшить читабельность кода и сделать его согласованным между большим числом проектов. В идеале, весь код будет написан в едином стиле, и любой сможет легко его прочесть. Как говорится в PEP 20 «Читабельность имеет значение». +Ключевая идея Гвидо такова: код читается намного больше раз, чем пишется. Собственно, рекомендации о стиле написания +кода направлены на то, чтобы улучшить читабельность кода и сделать его согласованным между большим числом проектов. +В идеале, весь код будет написан в едином стиле, и любой сможет легко его прочесть. Как говорится в PEP 20 +«Читабельность имеет значение». -Это руководство о согласованности и единстве. Согласованность с этим руководством очень важна. Согласованность внутри одного проекта еще важнее. А согласованность внутри модуля или функции — самое важное. Но важно помнить, что иногда это руководство неприменимо, и понимать, когда можно отойти от рекоммендаций. Когда вы сомневаетесь, просто посмотрите на другие примеры и решите, какой выглядит лучше. +Это руководство о согласованности и единстве. Согласованность с этим руководством очень важна. Согласованность внутри +одного проекта еще важнее. А согласованность внутри модуля или функции — самое важное. Но важно помнить, что иногда это +руководство неприменимо, и понимать, когда можно отойти от рекомендаций. Когда вы сомневаетесь, просто посмотрите на +другие примеры и решите, какой выглядит лучше. ### Две причины, чтобы нарушить правила: 1. Когда применение правила сделает код менее читабельным даже для того, кто привык читать код, который следует правилам. -2. Чтобы писать в едином стиле с кодом, который уже есть в проекте и который нарушает правила (может быть, в силу исторических причин) — впрочем, это возможность подчистить чужой код. +2. Чтобы писать в едином стиле с кодом, который уже есть в проекте и нарушает правила (может быть, в силу + исторических причин) — впрочем, это возможность подчистить чужой код. ### Внешний вид кода #### Отступы -Используйте 4 пробела на один уровень отступа. В старом коде, который вы не хотите трогать, можно продолжить пользоваться 8 пробелами для отступа. +Используйте 4 пробела на один уровень отступа. В старом коде, который вы не хотите трогать, можно продолжить +пользоваться 8 пробелами для отступа. #### Табуляция или пробелы? Никогда не смешивайте символы табуляции и пробелы. -Самый распространенный способ отступов — пробелы. На втором месте — отступы только с использованием табуляции. Код, в котором используются и те, и другие типы отступов, должен быть исправлен так, чтобы отступы в нем были расставлены только с помощью пробелов. Когда вы вызываете интерпретатор в командной строке с параметром -t, он выдает предупреждения (warnings) при использовании смешанного стиля в отступах, а запустив интерпретатор с параметром -tt, вы получите в этих местах ошибки (errors). Используйте этии опции! +Самый распространенный способ отступов — пробелы. На втором месте — отступы только с использованием табуляции. Код, в +котором используются и те, и другие типы отступов, должен быть исправлен так, чтобы отступы в нем были расставлены +только с помощью пробелов. Когда вы вызываете интерпретатор в командной строке с параметром `-t`, он выдает +предупреждения (warnings) при использовании смешанного стиля в отступах, а запустив интерпретатор с параметром `-tt`, +вы получите в этих местах ошибки (errors). Используйте эти опции! -В новых проектах для отступов мы настоятельно рекомендуем использовать пробелы. К тому же, многие редакторы позволяют легко делать. +В новых проектах для отступов мы настоятельно рекомендуем использовать пробелы. К тому же, многие редакторы позволяют +легко это делать. #### Максимальная длина строки Ограничьте максимальную длину строки 79 символами. -Пока еще существует немало устройств, где длина строки равна 80 символам; к тому же, ограничив ширину окна 80 символами, мы сможем расположить несколько окон рядом друг с другом. Автоматический перенос строк на таких устройствах нарушит форматирование, и код будет труднее понять. Так что, пожалуйста, ограничьте длину строки 79 символами, и 72 символами в случае длинных блоков текста (строки документации или комментарии). +Пока еще существует немало устройств, где длина строки равна 80 символам; к тому же, ограничив ширину окна 80 символами, +мы сможем расположить несколько окон рядом друг с другом. Автоматический перенос строк на таких устройствах нарушит +форматирование, и код будет труднее понять. Так что, пожалуйста, ограничьте длину строки 79 символами, и 72 символами +в случае длинных блоков текста (строки документации или комментарии). -Предпочтительный способ переноса длинных строк — использование подразумевающегося продолжения строки между обычными, квадратными и фигурными скобками. В случае необходимости можно добавить еще одну пару скобок вокруг выражения, но часто лучше выглядит обратный слэш. Постарайтесь сделать правильные отступы для перенесённой строки. Предпочтительнее вставить перенос строки после бинарного оператора, но не перед ним. +Предпочтительный способ переноса длинных строк — использование подразумевающегося продолжения строки между обычными, +квадратными и фигурными скобками. В случае необходимости можно добавить еще одну пару скобок вокруг выражения, но часто +лучше выглядит обратный слэш. Постарайтесь сделать правильные отступы для перенесённой строки. Предпочтительнее вставить +перенос строки после бинарного оператора, но не перед ним. Вот несколько примеров: @@ -50,11 +69,10 @@ class Rectangle(Blob): if width == 0 and height == 0 and \ color == 'red' and emphasis == 'strong' or \ highlight > 100: - raise ValueError("sorry, you lose") + raise ValueError("Sorry, you lose") if width == 0 and height == 0 and (color == 'red' or emphasis is None): - raise ValueError("I don't think so -- values are %s, %s" % - (width, height)) + raise ValueError(f"I don't think so -- values are {width}, {height}") Blob.__init__(self, width, height, color, emphasis, highlight) @@ -62,23 +80,34 @@ class Rectangle(Blob): ### Пустые строки -Отделяйте функции (верхнего уровня, не функции внутри функций) и определения классов двумя пустыми строчками. +Отделяйте функции (верхнего уровня, не функции внутри функций) и определения классов двумя пустыми строками. Определения методов внутри класса отделяйте одной пустой строкой. -Дополнительные отступы строками могут быть изредка использованы для выделения группы логически связанных функций. Пустые строки могут быть пропущены, между несколькими выражениями, записанными в одну строку, например, «заглушки» функций. +Дополнительные отступы строками могут быть изредка использованы для выделения группы логически связанных функций. Пустые +строки могут быть пропущены между несколькими выражениями, записанными в одну строку, например, «заглушки» функций. Используйте (без энтузиазма) пустые строки в коде функций, чтобы отделить друг от друга логические части. -Python расценивает символ control+L как незначащий (whitespace), и вы можете использовать его, потому что многие редакторы обрабатывают его как разрыв страницы — таким образом логические части в файле будут на разных страницах. +Python расценивает символ `Ctrl+L` как незначащий (whitespace), и вы можете использовать его, потому что многие +редакторы обрабатывают его как разрыв страницы — таким образом, логические части в файле будут на разных страницах. -### Кодировки (PEP 263) +### Кодировки ([PEP 263](https://peps.python.org/pep-0263/)) -Код ядра python всегда должен использовать ASCII или Latin-1 кодировку (также известную как ISO-8859-1). Начиная с версии python 3.0, предпочтительной является кодировка UTF-8 (смотрите PEP 3120). +Код ядра Python всегда должен использовать ASCII или Latin-1 кодировку (также известную как ISO-8859-1). Начиная с +версии Python 3.0, предпочтительной является кодировка UTF-8 (см. [PEP 3120](https://peps.python.org/pep-3120/)). -Files using ASCII (or UTF-8, for Python 3.0) should not have a coding cookie. Используйте Latin-1 (или UTF-8), только если это необходимо, чтобы указать в комментарии или строке документации имя автора, содержащее в себе символ из Latin-1. В противном случае предпочтительнее использовать escape-символы \x, \u или \U для не-ASCII символов в строках. +Files using ASCII (or UTF-8, for Python 3.0) should not have a coding cookie. Используйте Latin-1 (или UTF-8), только +если это необходимо, чтобы указать в комментарии или строке документации имя автора, содержащее в себе символ из +Latin-1. В противном случае предпочтительнее использовать escape-символы `\x`, `\u` или `\U` для не-ASCII символов в +строках. -Начиная с версии python 3.0 в стандартной библиотеке действует следующая политика (смотрите PEP 3131): все идентификаторы обязаны содержать только ASCII символы, и означать английские слова везде, где это возможно (во многих случаях используются сокращения или неанглийские технические термины). Кроме того, строки и комментарии тоже должны содержать лишь ASCII символы. Исключения составляют: (а) test case, тестирующий не-ASCII особенности программы, и (б) имена авторов. Авторы, буквы в именах которых не из латинского алфавита, должны транслитерировать свои имена в латиницу. +Начиная с версии Python 3.0 в стандартной библиотеке действует следующая политика +(см. [PEP 3131](https://peps.python.org/pep-3131/)): все идентификаторы обязаны содержать только ASCII символы, и +означать английские слова везде, где это возможно (во многих случаях используются сокращения или неанглийские +технические термины). Кроме того, строки и комментарии тоже должны содержать лишь ASCII символы. Исключения составляют: +(а) test case, тестирующий не-ASCII особенности программы, и (б) имена авторов. Авторы, буквы в именах которых не из +латинского алфавита, должны транслитерировать свои имена в латиницу. Проектам с открытым кодом для широкой аудитории также рекомендуется использовать это соглашение. @@ -98,11 +127,12 @@ import sys import os, sys ``` -В то же время, можно писать вот так: +В то же время можно писать вот так: ```python from subprocess import Popen, PIPE ``` -Импортирование всегда нужно делать сразу после комментариев к модулю и строк документации, перед объявлением глобальных переменных и констант. +Импортирование всегда нужно делать сразу после комментариев к модулю и строк документации, перед объявлением глобальных +переменных и констант. Группируйте импорты в следующем порядке: @@ -112,16 +142,20 @@ from subprocess import Popen, PIPE Вставляйте пустую строку между каждой группой импортов. -Относительные импорты крайне не рекомендуются — всегда указывайте абсолютный путь к модулю для всех импортирований. Даже сейчас, когда PEP 328 реализован в версии python 2.5, использовать явные относительные импорты нежелательно, потому что абсолютные импорты лучше переносимы и читабельны. +Относительные импорты крайне не рекомендуются — всегда указывайте абсолютный путь к модулю для всех импортирований. +Даже сейчас, когда PEP 328 реализован в версии Python 2.5, использовать явные относительные импорты нежелательно, +потому что абсолютные импорты лучше переносимы и читабельны. Когда вы импортируете класс из модуля, вполне можно писать вот так: ```python -from myclass import MyClass from foo.bar.yourclass import YourClass +from myclass import MyClass +from foo.bar.yourclass import YourClass ``` Если такое написание вызывает конфликт имен, тогда пишите: ```python -import myclass import foo.bar.yourclass +import myclass +import foo.bar.yourclass ``` И используйте `«myclass.MyClass»` и `«foo.bar.yourclass.Yourclass»`. @@ -132,288 +166,394 @@ import myclass import foo.bar.yourclass - Сразу после или перед скобками (обычными, фигурными и квадратными) ```python -можно: +# можно: spam(ham[1], {eggs: 2}) -нельзя: +# нельзя: spam( ham[ 1 ], { eggs: 2 } ) ``` - Сразу перед запятой, точкой с запятой, двоеточием: ```python -if x == 4: print x, y; x, y = y, x - -if x == 4 : print x , y ; x , y = y , x +# можно: + if x == 4: + print(x, y) + x, y = y, x +# нельзя: + if x == 4 : + print(x , y) + x , y = y , x ``` - Сразу перед открывающей скобкой, после которой начинается список аргументов при вызове функции: ```python -spam(1) - -spam (1) +# можно: + spam(1) +# нельзя: + spam (1) ``` - Сразу перед открывающей скобкой, после которой следует индекс или срез: ```python -dict['key'] = list[index] - -dict ['key'] = list [index] +# можно: + dict['key'] = list[index] +# нельзя: + dict ['key'] = list [index] ``` -- Использование более одного пробела вокруг оператора присваивания (или любого другого) для того, чтобы выровнять его с другим таким же оператором на соседней строке: +- Использование более одного пробела вокруг оператора присваивания (или любого другого) для того, чтобы выровнять его + с другим таким же оператором на соседней строке: ```python -x = 1 -y = 2 -long_variable = 3 +# можно: + x = 1 + y = 2 + long_variable = 3 -x = 1 -y = 2 -long_variable = 3 +# нельзя: + x = 1 + y = 2 + long_variable = 3 ``` -Прочие рекоммендации: -- Вседа окружайте эти бинарные операторы одним пробелом с каждой стороны: присваивание (`=`, `+=`, `-=` и прочие), сравнения (`==`, `<`, `>`, `!=`, `<>`, `<=`, `>=`, `in`, `not in`, `is`, `is not`), логические операторы (`and`, `or`, `not`). +Прочие рекомендации: +- Всегда окружайте эти бинарные операторы одним пробелом с каждой стороны: присваивание (`=`, `+=`, `-=` и прочие), + сравнения (`==`, `<`, `>`, `!=`, `<>`, `<=`, `>=`, `in`, `not in`, `is`, `is not`), логические операторы + (`and`, `or`, `not`). - Ставьте пробелы вокруг арифметических операций. ```python -i = i + 1 -submitted += 1 -x = x * 2 - 1 -hypot2 = x * x + y * y -c = (a + b) * (a - b) +# можно: + i = i + 1 + submitted += 1 + x = x * 2 - 1 + hypot2 = x * x + y * y + c = (a + b) * (a - b) -i=i+1 -submitted +=1 -x = x*2 - 1 -hypot2 = x*x + y*y -c = (a+b) * (a-b) +# нельзя: + i=i+1 + submitted +=1 + x = x*2 - 1 + hypot2 = x*x + y*y + c = (a+b) * (a-b) ``` -- Не используйте пробелы для отделения знака `=`, когда он употребляется для обозначения аргумента-ключа (keyword argument) или значения параметра по умолчанию. +- Не используйте пробелы для отделения знака `=`, когда он употребляется для обозначения аргумента-ключа + (keyword argument) или значения параметра по умолчанию. ```python -def complex(real, imag=0.0): - return magic(r=real, i=imag) +# можно: + def complex(real, imag=0.0): + return magic(r=real, i=imag) -def complex(real, imag = 0.0): - return magic(r = real, i = imag) +# нельзя: + def complex(real, imag = 0.0): + return magic(r = real, i = imag) ``` - Не используйте составные инструкции (несколько команд в одной строке). ```python -if foo == 'blah': - do_blah_thing() -do_one() -do_two() -do_three() +# можно: + if foo == 'blah': + do_blah_thing() + do_one() + do_two() + do_three() -if foo == 'blah': do_blah_thing() -do_one(); do_two(); do_three() +# нельзя: + if foo == 'blah': do_blah_thing() + do_one(); do_two(); do_three() ``` -- Иногда можно писать тело циклов while, for или ветку if в той же строке, если команда короткая, но если команд несколько, никогда так не пишите. +- Иногда можно писать тело циклов `while`, `for` или ветку `if` в той же строке, если команда короткая, но если команд + несколько, никогда так не пишите. ```python -if foo == 'blah': do_blah_thing() -for x in lst: total += x -while t < 10: t = delay() +# можно: + if foo == 'blah': do_blah_thing() + for x in lst: total += x + while t < 10: t = delay() -if foo == 'blah': do_blah_thing() -else: do_non_blah_thing() -try: something() -finally: cleanup() -do_one(); do_two(); do_three(long, argument, - list, like, this) -if foo == 'blah': one(); two(); three() +# нельзя: + if foo == 'blah': do_blah_thing() + else: do_non_blah_thing() + try: something() + finally: cleanup() + do_one(); do_two(); do_three(long, argument, list, like, this) + if foo == 'blah': one(); two(); three() ``` ### Комментарии -Комментарии, которые противоречат коду, хуже, чем отсутствие комментариев. Всегда исправляйте комментарии, если меняете код! +Комментарии, которые противоречат коду, хуже, чем отсутствие комментариев. Всегда исправляйте комментарии, если +меняете код! -Комментарии должны являться законченными предложениями. Если комментарий — фраза или предложение, первое слово должно быть написано с большой буквы, если только это не имя переменной, которая начинается с маленькой буквы (кстати, никогда не отступайте от этого правила для имен переменных). +Комментарии должны являться законченными предложениями. Если комментарий — фраза или предложение, первое слово должно +быть написано с большой буквы, если только это не имя переменной, которая начинается с маленькой буквы (кстати, +никогда не отступайте от этого правила для имен переменных). -Если комментарий короткий, можно опустить точку в конце предложения. Блок комментариев обычно состоит из одного или более абзацев, составленных из полноценных предложений, поэтому каждое предложение должно оканчиваться точкой. +Если комментарий короткий, можно опустить точку в конце предложения. Блок комментариев обычно состоит из одного или +более абзацев, составленных из полноценных предложений, поэтому каждое предложение должно оканчиваться точкой. Ставьте два пробела после точки в конце предложения. -Если вы пишете по-английски, не забывайте о Странке и Уайте (имеется в виду книга Strunk & White, “Elements of style”, которая является практически эталонным руководством по правильному написанию текстов на английском языке, — прим. перев.) +Если вы пишете по-английски, не забывайте о Странке и Уайте (имеется в виду книга Strunk & White, “Elements of style”, +которая является практически эталонным руководством по правильному написанию текстов на английском языке.) -Программисты, которые не говорят на английском языке, пожалуйста, пишите комментарии на английском, если только вы не уверены на 120 процентов, что ваш код никогда не будут читать люди, не знающие вашего родного языка. +Программисты, которые не говорят на английском языке, пожалуйста, пишите комментарии на английском, если только вы не +уверены на 120 процентов, что ваш код никогда не будут читать люди, не знающие вашего родного языка. #### Блок комментариев -Блок комментариев обычно объясняет код (весь, или только некоторую часть), идущий после блока, и должен иметь тот же отступ, что и сам код. Каждая строчка такого блока должна начинаться с символа # и одного пробела после него (если только сам текст комментария не имеет отступа). +Блок комментариев обычно объясняет код (весь или только некоторую часть), идущий после блока, и должен иметь тот же +отступ, что и сам код. Каждая строчка такого блока должна начинаться с символа `#` и одного пробела после него (если +только сам текст комментария не имеет отступа). -Абзацы внутри блока комментариев лучше отделять строкой, состоящей из одного символа #. +Абзацы внутри блока комментариев лучше отделять строкой, состоящей из одного символа `#`. -Комментарии в строке с кодом +**Комментарии в строке с кодом** Старайтесь реже использовать подобные комментарии. -Такой комментарий находится в той же строке, что и инструкция. «Встрочные» комментарии должны отделяться хотя бы двумя пробелами от инструкции. Они должны начинаться с символа # и одного пробела. +Такой комментарий находится в той же строке, что и инструкция. «Встрочные» комментарии должны отделяться хотя бы двумя +пробелами от инструкции. Они должны начинаться с символа `#` и одного пробела. Комментарии в строке с кодом не нужны и только отвлекают от чтения, если они объясняют очевидное. Не пишите вот так: ```python x = x + 1 # Увеличиваем X на один ``` -Впрочем, иногда такие комметарии полезны: +Впрочем, иногда такие кометарии полезны: ```python x = x + 1 # Место для рамки окна ``` ### Строки документации -Соглашения о написании хорошей документации (docstrings) увековечены (да, забавно, но автор использует именно такое слово, — прим. перев.) в PEP 257. +Соглашения о написании хорошей документации (docstrings) увековечены (да, забавно, но автор использует именно такое +слово, — прим. перев.) в [PEP 257](https://peps.python.org/pep-0257/). -Пишите документацию для всех модулей, функций, классов, методов, которые объявлены как public. Строки документации необязательны для не-public методов, но лучше написать, что делает метод. Комментарий нужно писать после строки с def. +Пишите документацию для всех модулей, функций, классов, методов, которые объявлены как `public`. Строки документации +необязательны для не-public методов, но лучше написать, что делает метод. Комментарий нужно писать после строки с `def`. -PEP 257 объясняет, как правильно и хорошо документировать. Заметьте, очень важно, чтобы закрывающие """ стояли на отдельной строчке. А еще лучше, если перед ними будет ещё и пустая строка, например: +PEP 257 объясняет как правильно и хорошо документировать. Заметьте, очень важно, чтобы закрывающие кавычки `"""` стояли +на отдельной строке. А еще лучше, если перед ними будет ещё и пустая строка, например: ```python """Return a foobang Optional plotz says to frobnicate the bizbaz first. """ ``` -Для однострочной документации можно оставить """ на той же строке. +Для однострочной документации можно оставить `"""` на той же строке. ### Имена -Соглашения об именах переменных в python немного туманны, поэтому их список никогда не будет полным — тем не менее, ниже мы приводим список рекоммендаций, действующих на данный момент. Новые модули и пакеты должны быть написаны согласно этим стандартам, но если в какой-либо уже существующей библиотеке эти правила нарушаются, предпочтительнее писать в едином с ней стиле. +Соглашения об именах переменных в Python немного туманны, поэтому их список никогда не будет полным — тем не менее, +ниже мы приводим список рекомендаций, действующих на данный момент. Новые модули и пакеты должны быть написаны согласно +этим стандартам, но если в какой-либо уже существующей библиотеке эти правила нарушаются, предпочтительнее писать в +одном стиле с ней. -### Описание: Стили имен +### Описание: стили имен -Существует много разных стилей. Поможем вам распознать, какой стиль именования используется, независимо от того, для чего он используется. +Существует много разных стилей. Поможем вам распознать, какой стиль именования используется, независимо от того, для +чего он используется. Обычно различают следующие стили: -b (одиночная маленькая буква) +`b` (одиночная маленькая буква) -B (одиночная заглавная буква) +`B` (одиночная заглавная буква) -lowercase (слово в нижнем регистре) +`lowercase` (слово в нижнем регистре) -lower_case_with_underscores (слова из маленьких букв с подчеркиваниями) +`lower_case_with_underscores` (слова из маленьких букв с подчеркиваниями) -UPPERCASE (заглавные буквы) +`UPPERCASE` (заглавные буквы) -UPPERCASE_WITH_UNDERSCORES (слова из заглавных букв с подчеркиваниями) +`UPPERCASE_WITH_UNDERSCORES` (слова из заглавных букв с подчеркиваниями) -CapitalizedWords (слова с заглавными буквами, или CapWords, или CamelCase 5. Иногда называется StudlyCaps). Замечание: когда вы используете аббревиатуры в таком стиле, пишите все буквы аббревиатуры заглавными — HTTPServerError лучше, чем HttpServerError. +`CapitalizedWords` (слова с заглавными буквами, или CapWords, или CamelCase. Иногда называется StudlyCaps). +Замечание: когда вы используете аббревиатуры в таком стиле, пишите все буквы аббревиатуры заглавными — HTTPServerError +лучше, чем HttpServerError. -mixedCase (отличается от CapitalizedWords тем, что первое слово начинается с маленькой буквы) +`mixedCase` (отличается от `CapitalizedWords` тем, что первое слово начинается с маленькой буквы) -Capitalized_Words_With_Underscores (слова с заглавными буквами и подчеркиваниями — уродливо!) -Ещё существует стиль, в котором имена, принадлежащие одной логической группе, имеют один короткий префикс. Этот стиль редко используется в python, но мы упоминаем его для полноты. Например, функция os.stat() возвращает кортеж, имена в котором традиционно имеют вид st_mode, st_size, st_mtime и так далее. (Так сделано, чтобы подчеркнуть соответствие этих полей структуре системных вызовов POSIX, что помогает знакомым с ней программистам). +`Capitalized_Words_With_Underscores` (слова с заглавными буквами и подчеркиваниями — уродливо!) +Ещё существует стиль, в котором имена, принадлежащие одной логической группе, имеют один короткий префикс. Этот стиль +редко используется в Python, но мы упоминаем его для полноты. Например, функция `os.stat()` возвращает кортеж, имена +в котором традиционно имеют вид `st_mode`, `st_size`, `st_mtime` и так далее. (Так сделано, чтобы подчеркнуть +соответствие этих полей структуре системных вызовов POSIX, что помогает знакомым с ней программистам). -В библиотеке X11 используется префикс Х для всех public-функций. В python этот стиль считается излишним, потому что перед полями и именами методов стоит имя объекта, а перед именами функций стоит имя модуля. +В библиотеке X11 используется префикс Х для всех public-функций. В Python этот стиль считается излишним, потому что +перед полями и именами методов стоит имя объекта, а перед именами функций стоит имя модуля. -В дополнение к этому, используются следующие специальные формы записи имен с добавлением символа подчеркивания в начало или конец имени: +В дополнение к этому используются следующие специальные формы записи имен с добавлением символа подчеркивания в начало +или конец имени: -_single_leading_underscore: слабый индикатор того, что имя используется для «внутренних нужд». Например, from M import * не будет импортировать объекты, чьи имена начинаются с символа подчеркивания. +`_single_leading_underscore`: слабый индикатор того, что имя используется для «внутренних нужд». Например, +`from M import *` не будет импортировать объекты, чьи имена начинаются с символа подчеркивания. -single_trailing_underscore_: используется по соглашению для избежания конфликтов с ключевыми словами языка python, например: +`single_trailing_underscore_`: используется по соглашению для избежания конфликтов с ключевыми словами языка Python, +например: -Tkinter.Toplevel(master, class_='ClassName') +`Tkinter.Toplevel(master, class_='ClassName') ` -__double_leading_underscore: изменяет имя атрибута класса, т.е. в class FooBar поле __boo становится _FooBar__boo. +`__double_leading_underscore`: изменяет имя атрибута класса, т.е. в `class FooBar` поле `__boo` становится +`_FooBar__boo`. -__double_leading_and_trailing_underscore__ (двойное подчеркивание в начале и в конце имени): «волшебные» объекты или атрибуты, которые «живут» в пространствах имен, управляемых пользователем (user-controlled namespaces). Например, __init__, __import__ или __file__. Не изобретайте такие имена, используйте их только так, как написано в документации. +`__double_leading_and_trailing_underscore__` (двойное подчеркивание в начале и в конце имени): «волшебные» объекты или +атрибуты, которые «живут» в пространствах имен, управляемых пользователем (user-controlled namespaces). Например, +`__init__`, `__import__` или `__file__`. Не изобретайте такие имена, используйте их только так, как написано в +документации. ### Стили имен -Имена, которых следует избегать +**Имена, которых следует избегать** -Никогда не используйте символы l (малелькая латинская буква «эль»), O (заглавная латинская буква «о») или I (заглавная латинская буква «ай») как однобуквенные идентификаторы. +Никогда не используйте символы `l` (маленькая латинская буква «эль»), `O` (заглавная латинская буква «о») или `I` +(заглавная латинская буква «ай») как односимвольные идентификаторы. -В некоторых шрифтах эти символы неотличимы от цифры один и нуля (и символа вертикальной палочки, — прим. перев.) Если очень нужно использовать l имена, пишите вместо неё заглавную L. +В некоторых шрифтах эти символы неотличимы от цифры один и нуля (и символа вертикальной палочки, — прим. перев.) +Если очень нужно использовать `l`-имена, пишите вместо неё заглавную `L`. -Имена модулей и пакетов +### Имена модулей и пакетов -Модули должны иметь короткие имена, состоящие из маленьких букв. Можно использовать и символы подчеркивания, если это улучшает читабельность. То же, за исключением символов подчеркивания, относится и к именам пакетов. +Модули должны иметь короткие имена, состоящие из маленьких букв. Можно использовать и символы подчеркивания, если это +улучшает читабельность. То же, за исключением символов подчеркивания, относится и к именам пакетов. -Так как имена модулей отображаются в имена файлов, а некоторые файловые системы являются нечувствительными к регистру символов и обрезают длинные имена, очень важно использовать достаточно короткие имена модулей — это не проблема в Unix, но, возможно, код окажется непереносимым в старые версии Windows или Mac, или DOS. Когда модуль расширения, написанный на С или C++, имеет сопутствующий python-модуль (содержащий интерфейс высокого уровня), С/С++ модуль начинается с символа подчеркивания, например, _socket. +Так как имена модулей отображаются в имена файлов, а некоторые файловые системы являются нечувствительными к регистру +символов и обрезают длинные имена, очень важно использовать достаточно короткие имена модулей — это не проблема в Unix, +но, возможно, код окажется непереносимым в старые версии Windows или Mac, или DOS. Когда модуль расширения, написанный +на С или C++, имеет сопутствующий Python-модуль (содержащий интерфейс высокого уровня), С/С++ модуль начинается с +символа подчеркивания, например, _socket. ### Имена классов -Все имена классов должны следовать соглашению CapWords почти без исключений. Классы внутреннего использования могут начинаться с символа подчеркивания. +Все имена классов должны следовать соглашению CapWords почти без исключений. Классы внутреннего использования могут +начинаться с символа подчеркивания. #### Имена исключений (exceptions) -Так как исключения являются классами, к исключениями применяется стиль именования классов. Однако вы можете добавить Error в конце имени (если конечно исключение действительно является ошибкой). +Так как исключения являются классами, к исключениям применяется стиль именования классов. Однако вы можете добавить +`Error` в конце имени (если конечно исключение действительно является ошибкой). #### Имена глобальных переменных -Будем надеяться, что такие имена используются только внутри одного модуля. Руководствуйтесь теми же соглашениями, что и для имен функций. +Будем надеяться, что такие имена используются только внутри одного модуля. Руководствуйтесь теми же соглашениями, что и +для имен функций. -Добавляйте в модули, которые написаны так, чтобы их использовали с помощью from M import *, механизм __all__ чтобы предотвратить экспортирование глобальных переменных. Или же, используйте старое соглашение, добавляя перед именами таких глобальных переменных один символ подчеркивания (которым вы можете обозначить те глобальные переменные, которые используются только внутри модуля). +Добавляйте в модули, которые написаны так, чтобы их использовали с помощью `from M import *`, механизм `__all__`, чтобы +предотвратить экспортирование глобальных переменных. Или же используйте старое соглашение, добавляя перед именами таких +глобальных переменных один символ подчеркивания (которым вы можете обозначить те глобальные переменные, которые +используются только внутри модуля). ### Имена функций -Имена функций должны состоять из маленьких букв, а слова разделяться символами подчеркивания — это необходимо, чтобы увеличить читабельность. +Имена функций должны состоять из маленьких букв, а слова разделяться символами подчеркивания — это необходимо, чтобы +увеличить читабельность. -Стиль mixedCase допускается в тех местах, где уже преобладает такой стиль, например во threading.py, для сохранения обратной совместимости. +Стиль mixedCase допускается в тех местах, где уже преобладает такой стиль, например, в threading.py, для сохранения +обратной совместимости. ### Аргументы функций и методов -Всегда используйте self в качестве первого аргумента метода экземпляра объекта (instance method). +Всегда используйте `self` в качестве первого аргумента метода экземпляра объекта (instance method). -Всегда используйте cls в качестве первого аргумента метода класса (class method). +Всегда используйте `cls` в качестве первого аргумента метода класса (class method). -Если имя аргумента конфликтует с зарезервированным ключевым словом python, обычно лучше добавить в конец имени символ подчеркивания, чем исказить написание слова или использовать аббревиатуру. Таким образом, print_ лучше, чем prnt. (Возможно, хорошим вариантом будет подобрать синоним). +Если имя аргумента конфликтует с зарезервированным ключевым словом Python, обычно лучше добавить в конец имени символ +подчеркивания, чем исказить написание слова или использовать аббревиатуру. Таким образом, print_ лучше, чем prnt. +(Возможно, хорошим вариантом будет подобрать синоним). #### Имена методов и переменные экземпляров классов -Используйте тот же стиль, что и для имен функций: имена должны состоять из маленьких букв, а слова разделяться символами подчеркивания. +Используйте тот же стиль, что и для имен функций: имена должны состоять из маленьких букв, а слова разделяться символами +подчеркивания. -Чтобы избежать конфликта имен с подклассами, добавьте два символа подчеркивания, чтобы включить механизм изменения имен. Если класс Foo имя атрибут с именем __foo, к нему нельзя обратиться, написав Foo.__a. (Настойчивый пользователь всё равно может получить доступ, написав Foo._Foo__a). Вообще, двойное подчеркивание в именах должно использоваться, чтобы избежать конфликта имен с атрибутами классов, спроектированных так, чтобы от них наследовали подклассы. +Чтобы избежать конфликта имен с подклассами, добавьте два символа подчеркивания, чтобы включить механизм изменения имен. +Если класс `Foo` содержит имя атрибута с именем `__a`, к нему нельзя обратиться, написав `Foo.__a`. (Настойчивый +пользователь всё равно может получить доступ, написав `Foo._Foo__a`). Вообще, двойное подчеркивание в именах должно +использоваться, чтобы избежать конфликта имен с атрибутами классов, спроектированных так, чтобы от них наследовали +подклассы. ### Константы -Константы обычно объявляются на уровне модуля и записываются только заглавными буквами, а слова разделяются символами подчеркивания. Например: MAX_OVERFLOW, TOTAL. +Константы обычно объявляются на уровне модуля и записываются только заглавными буквами, а слова разделяются символами +подчеркивания. Например: `MAX_OVERFLOW`, `TOTAL`. ### Проектирование наследования -Обязательно решите, каким должен быть метод класса или переменная экземпляра класса (в общем, атрибут) — public или не-public. Если вы сомневаетесь, выберите закрытый, не-public атрибут. Потом будет проще сделать их public, чем наоборот. +Обязательно решите, каким должен быть метод класса или переменная экземпляра класса (в общем, атрибут) — public или +не-public. Если вы сомневаетесь, выберите закрытый, не-public атрибут. Потом будет проще сделать их public, чем +наоборот. -Открытые атрибуты — это те, которые будут использовать потребители ваших классов, и вы должны быть уверены в отсутствии обратной несовместимости. Не-public атрибуты, в свою очередь, не предназначены для использования третьими лицами, поэтому вы можете не гарантировать, что не измените или не удалите эти атрибуты. +Открытые атрибуты — это те, которые будут использовать потребители ваших классов, и вы должны быть уверены в отсутствии +обратной несовместимости. Не-public атрибуты, в свою очередь, не предназначены для использования третьими лицами, +поэтому вы можете не гарантировать, что не измените или не удалите эти атрибуты. -Мы не используем термин «закрытый член» (private), потому что на самом деле в python таких членов не бывает. +Мы не используем термин «закрытый член» (private), потому что на самом деле в Python таких членов не бывает. -Другой тип аттрибутов классов принадлежит так называемому API подклассов (в других языках они часто называются protected). Некоторые классы проектируются так, чтобы от них наследовали другие классы, которые расширяют или модифицируют поведение базового класса. Когда вы проектируете такой класс, решите и явно укажите, какие атрибуты являются открытыми (public), какие принадлежат API подклассов (subclass API), а какие используются только базовым классом. +Другой тип атрибутов классов принадлежит к так называемому API подклассов (в других языках они часто называются +protected). Некоторые классы проектируются так, чтобы от них наследовали другие классы, которые расширяют или +модифицируют поведение базового класса. Когда вы проектируете такой класс, решите и явно укажите, какие атрибуты +являются открытыми (public), какие принадлежат API подклассов (subclass API), а какие используются только базовым +классом. -Теперь сформулируем рекомендации: +**Теперь сформулируем рекомендации:** -Открытые атрибуты не должны иметь в начале имени символа подчеркивания +Открытые атрибуты не должны иметь в начале имени символа подчеркивания. -Если имя открытого атрибута конфликтует с ключевым словом языка, добавьте в конец имени один символ подчеркивания. Это более предпочтительно, чем аббревиатура или искажение написания (однако, у этого правила есть исключение — аргумента который означает класс, и особенно первый аргумент метода класса (class method) должен иметь имя cls). +Если имя открытого атрибута конфликтует с ключевым словом языка, добавьте в конец имени один символ подчеркивания. Это +более предпочтительно, чем аббревиатура или искажение написания (однако, у этого правила есть исключение — аргумент, +который означает класс, и особенно первый аргумент метода класса (`class method`) должен иметь имя `cls`). -Назовите простые открытые атрибуты понятными именами и не пишите сложные методы доступа и изменения (accessor/mutator, get/set, — прим. перев.) Помните, что в python очень легко добавить их потом, если потребуется. В этом случае используйте свойства (properties), чтобы скрыть функциональную реализацию за синтаксисом доступа к атрибутам. +Назовите простые открытые атрибуты понятными именами и не пишите сложные методы доступа и изменения (`accessor/mutator`, +`get/set`, — прим. перев.) Помните, что в Python очень легко добавить их потом, если потребуется. В этом случае +используйте свойства (properties), чтобы скрыть функциональную реализацию за синтаксисом доступа к атрибутам. -Свойства (properties) работают только в классах нового стиля (new-style classes) +Свойства (properties) работают только в классах нового стиля (new-style classes). -Постарайтесь избавиться от побочных эффектов, связанным с функциональным поведением; впрочем, такие вещи, как кэширование, вполне допустимы. +Постарайтесь избавиться от побочных эффектов, связанным с функциональным поведением; впрочем, такие вещи как +кэширование, вполне допустимы. -Избегайте использования вычислительно затратных операций, потому что из-за записи с помощью атрибутов создается впечатление, что доступ происходит (относительно) быстро. +Избегайте использования вычислительно затратных операций, потому что из-за записи с помощью атрибутов создается +впечатление, что доступ происходит (относительно) быстро. -Если вы планируете класс таким образом, чтобы от него наследовались другие классы, но не хотите, чтобы подклассы унаследовали некоторые атрибуты, добавьте в имена два символа подчеркивания в начало, и ни одного — в конец. Механизм изменения имен в python (name mangling, — прим. перев.) сработает так, что имя класса добавится к имени такого атрибута, что позволит избежать конфликта имен с атрибутами подклассов. +Если вы планируете класс таким образом, чтобы от него наследовались другие классы, но не хотите, чтобы подклассы +унаследовали некоторые атрибуты, добавьте в имена два символа подчеркивания в начало, и ни одного — в конец. Механизм +изменения имен в Python (`name mangling`, — прим. перев.) сработает так, что имя класса добавится к имени такого +атрибута, что позволит избежать конфликта имен с атрибутами подклассов. Будьте внимательны: если подкласс будет иметь то же имя класса и имя атрибута, то вновь возникнет конфликт имен. -Механизм изменения имен может затруднить отладку или работу с __getattr__(), однако он хорошо документирован и легко реализуется вручную. +Механизм изменения имен может затруднить отладку или работу с `__getattr__()`, однако он хорошо документирован и легко +реализуется вручную. -Не всем нравится этот механизм, поэтому старайтесь достичь компромисса между необходимостью избежать конфликта имен и возможностью доступа к этим атрибутам. +Не всем нравится этот механизм, поэтому старайтесь достичь компромисса между необходимостью избежать конфликта имен и +возможностью доступа к этим атрибутам. ### Общие рекомендации -Код должен быть написан так, чтобы не зависеть от разных реализация языка (PyPy, Jython, IronPython, Pyrex, Psyco и пр.). Например, не полагайтесь на эффективную реализацию в CPython конкатенации строк в выражениях типа a+=b или a=a+b. Такие инструкции выполняются значительно медленнее в Jython. В критичных к времени выполнения частях программы используйте ''.join() — таким образом склеивание строк будет выполнено за линейное время независимо от реализации python. +Код должен быть написан так, чтобы не зависеть от разных реализация языка (PyPy, Jython, IronPython, Pyrex, Psyco и +пр.). Например, не полагайтесь на эффективную реализацию в CPython конкатенации строк в выражениях типа `a+=b` или +`a=a+b`. Такие инструкции выполняются значительно медленнее в Jython. В критичных к времени выполнения частях программы +используйте `''.join()` — таким образом склеивание строк будет выполнено за линейное время независимо от реализации +Python. -Сравнения с None должны обязательно выполняться с использованием операторов is или is not, а не с помощью операторов равенства или неравенства. Кроме того, не пишите if x, если имеете в виду if x is not None — если, к примеру, при тестировании такая переменная или аргумент примет значение иного типа, то при приведении к булевскому типу получится false. +Сравнения с `None` должны обязательно выполняться с использованием операторов `is` или `is not`, а не с помощью +операторов равенства или неравенства. Кроме того, не пишите `if x`, если имеете в виду `if x is not None` — если, к +примеру, при тестировании такая переменная или аргумент примет значение иного типа, то при приведении к булевому типу +получится `false`. -Создавайте исключения на основе классов. Впрочем, начиная с версии python 2.6, мы уже не можем использовать строки в качестве исключений. В модулях или пакетах создавайте свои базовые классы исключений, наследуя их от встроенного класса Exception и обязательно их документируйте: +Создавайте исключения на основе классов. Впрочем, начиная с версии Python 2.6, мы уже не можем использовать строки в +качестве исключений. В модулях или пакетах создавайте свои базовые классы исключений, наследуя их от встроенного класса +Exception и обязательно их документируйте: ```python class MessageError(Exception): """Base class for errors in the email package.""" ``` -Здесь применимы те же правила, что и для именования классов. Если исключение по своему смыслу является ошибкой, вы можете добавить в конце имени Error. +Здесь применимы те же правила, что и для именования классов. Если исключение по своему смыслу является ошибкой, вы +можете добавить в конце имени `Error`. -Когда вы генерируете исключение, пишите raise ValueError('message') вместо старого синтаксиса raise ValueError, message. Такое использование предпочтительнее, потому что из-за скобок не нужно использовать символы для продолжения перенесенных строк, если эти строки длинные или если используется форматирование. Старая форма записи запрещена в python 3000. +Когда вы генерируете исключение, пишите `raise ValueError('message')` вместо старого синтаксиса +`raise ValueError, message`. Такое использование предпочтительнее, потому что из-за скобок не нужно использовать +символы для продолжения перенесенных строк, если эти строки длинные или если используется форматирование. Старая форма +записи запрещена в Python 3.0. -Когда код перехватывает исключения, «ловите» конкретные ошибки вместо простого выражнения except:. К примеру, пишите вот так: +Когда код перехватывает исключения, «ловите» конкретные ошибки вместо простого выражения `except:`. К примеру, пишите +вот так: ```python try: @@ -422,11 +562,14 @@ except ImportError: platform_specific_module = None ``` -Простое написание 'except:' также перехватит и SystemExit, и KeyboardInterrupt, что породит проблемы, например, сложнее будет завершить программу нажатием control+C. Если вы действительно собираетесь перехватить все исключения, пишите 'except Exception:'. +Простое написание `except:` также перехватит и `SystemExit`, и `KeyboardInterrupt`, что породит проблемы, например, +сложнее будет завершить программу нажатием `Ctrl+C`. Если вы действительно собираетесь перехватить все исключения, +пишите `except Exception:`. -Ограничьтесь использованием чистого 'except:' в двух случаях: -1. Если обработчик исключения выводит пользователю всё о случившейся ошибке (например, traceback) -2. Если нужно выполнить некоторый код после перехвата исключения, а потом вновь «бросить» его для обработки где-то в другом месте. Обычно же лучше пользоваться конструкцией `try...finally`. +Ограничьтесь использованием чистого `except:` в двух случаях: +1. Если обработчик исключения выводит пользователю всё о случившейся ошибке (например, `traceback`). +2. Если нужно выполнить некоторый код после перехвата исключения, а потом вновь «бросить» его для обработки где-то в + другом месте. Обычно же лучше пользоваться конструкцией `try...finally`. Постарайтесь заключать в каждую конструкцию `try...except` минимум кода, чтобы легче отлавливать ошибки. ```python try: @@ -444,41 +587,53 @@ except KeyError: return key_not_found(key) ``` -Используйте строковые методы вместо модуля string — они всегда быстрее и имеют тот же API для unicode-строк. Можно отказаться от этого правила, если необходима совместимость с версиями python младше 2.0. +Используйте строковые методы вместо модуля `string` — они всегда быстрее и имеют тот же API для unicode-строк. Можно +отказаться от этого правила, если необходима совместимость с версиями Python младше 2.0. -Пользуйтесь `''.startswith()` и `''.endswith()` вместо обработки частей строк (string slicing) для проверки суффиксов или префиксов. `startswith()` и `endswith()` выглядят чище и порождают меньше ошибок. Например: +Пользуйтесь `''.startswith()` и `''.endswith()` вместо обработки частей строк (string slicing) для проверки суффиксов +или префиксов. `startswith()` и `endswith()` выглядят чище и порождают меньше ошибок. Например: ```python -да: if foo.startswith('bar'): +# да: + if foo.startswith('bar'): -нет: if foo[:3] == 'bar': +# нет: + if foo[:3] == 'bar': ``` -Сравнение типов объектов нужно делать с помощью isinstance(), а не прямым сравнением типов: +Сравнение типов объектов нужно делать с помощью `isinstance()`, а не прямым сравнением типов: ```python -да: if isinstance(obj, int): +# да: + if isinstance(obj, int): -нет: if type(obj) is type(1): +# нет: + if type(obj) is type(1): ``` -Для последовательностей (строк, списков, кортежей) можно использовать тот факт, что пустая последовательность есть false: +Для последовательностей (строк, списков, кортежей) можно использовать тот факт, что пустая последовательность есть +`false`: ```python -да: if not seq: +# да: + if not seq: if seq: -нет: if len(seq): - if not len(seq): +# нет: + if len(seq): + if not len(seq): ``` -Не пользуйтесь строковыми константами, которые имеют важные пробелы в конце — они невидимы, а многие редакторы (а теперь и reindent.py) обрезают их. +Не пользуйтесь строковыми константами, которые имеют важные пробелы в конце — они невидимы, а многие редакторы (а +теперь и reindent.py) обрезают их. -Не сравнивайте логические типы с True и False с помощью ==: +Не сравнивайте логические типы с `True` и `False` с помощью оператора `==`: ```python -да: if greeting: -нет: if greeting == True: -А вот так писать совсем плохо: -if greeting is True: +# да: + if greeting: +# нет: + if greeting == True: +# А вот так писать совсем плохо: + if greeting is True: ``` @@ -488,11 +643,12 @@ if greeting is True: Модуль: любой файл *.py. Имя модуля — имя этого файла. -Встроенный модуль: «модуль», который был написан на Си, скомпилирован и встроен в интерпретатор Python, и потому не имеет файла *.py. +Встроенный модуль: «модуль», который был написан на языке С, скомпилирован и встроен в интерпретатор Python, и потому +не имеет файла *.py. -Пакет: любая папка, которая содержит файл __init__.py. Имя пакета — имя папки. +Пакет: любая папка, которая содержит файл `__init__.py`. Имя пакета — имя папки. -С версии Python 3.3 любая папка (даже без __init__.py) считается пакетом. +С версии Python 3.3 любая папка (даже без `__init__.py`) считается пакетом. Объект: в Python почти всё является объектом — функции, классы, переменные и т. д. ### Пример структуры директорий @@ -518,31 +674,46 @@ test/ # Корневая папка ## Что делает import -При импорте модуля Python выполняет весь код в нём. При импорте пакета Python выполняет код в файле пакета __init__.py, если такой имеется. Все объекты, определённые в модуле или __init__.py, становятся доступны импортирующему. +При импорте модуля Python выполняет весь код в нём. При импорте пакета Python выполняет код в файле пакета +`__init__.py`, если такой имеется. Все объекты, определённые в модуле или `__init__.py`, становятся доступны +импортирующему. -Встроенные функции Python: какие нужно знать и на какие не стоит тратить время +Встроенные функции Python: какие нужно знать и на какие не стоит тратить время. ### Основы import и sys.path -Вот как оператор import производит поиск нужного модуля или пакета согласно документации Python: +Вот как оператор `import` производит поиск нужного модуля или пакета согласно документации Python: -При импорте модуля spam интерпретатор сначала ищёт встроенный модуль с таким именем. Если такого модуля нет, то идёт поиск файла spam.py в списке директорий, определённых в переменной sys.path. sys.path инициализируется из следующих мест: +При импорте модуля **spam** интерпретатор сначала ищет встроенный модуль с таким именем. Если такого модуля нет, то +идёт поиск файла spam.py в списке директорий, определённых в переменной `sys.path`. `sys.path` инициализируется из +следующих мест: - директории, содержащей исходный скрипт (или текущей директории, если файл не указан); - директории по умолчанию, которая зависит от дистрибутива Python; -- PYTHONPATH (список имён директорий; имеет синтаксис, аналогичный переменной окружения PATH). +- `PYTHONPATH` (список имён директорий; имеет синтаксис, аналогичный переменной окружения PATH). -Программы могут изменять переменную sys.path после её инициализации. Директория, содержащая запускаемый скрипт, помещается в начало поиска перед путём к стандартной библиотеке. Это значит, что скрипты в этой директории будут импортированы вместо модулей с такими же именами в стандартной библиотеке. +Программы могут изменять переменную `sys.path` после её инициализации. Директория, содержащая запускаемый скрипт, +помещается в начало поиска перед путём к стандартной библиотеке. Это значит, что скрипты в этой директории будут +импортированы вместо модулей с такими же именами в стандартной библиотеке. -Технически документация не совсем полна. Интерпретатор будет искать не только файл (модуль) `spam.py`, но и папку (пакет) spam. +Технически документация не совсем полна. Интерпретатор будет искать не только файл (модуль) `spam.py`, но и папку +(пакет) `spam`. -Обратите внимание, что Python сначала производит поиск среди встроенных модулей — тех, которые встроены непосредственно в интерпретатор. Список встроенных модулей зависит от дистрибутива Python, а найти этот список можно в `sys.builtin_module_names` (Python 2 и Python 3). Обычно в дистрибутивах есть модули `sys` (всегда включён в дистрибутив), `math`, `itertools`, `time` и прочие. +Обратите внимание, что Python сначала производит поиск среди встроенных модулей — тех, которые встроены непосредственно +в интерпретатор. Список встроенных модулей зависит от дистрибутива Python, а найти этот список можно в +`sys.builtin_module_names` (Python 2 и Python 3). Обычно в дистрибутивах есть модули `sys` (всегда включён в +дистрибутив), `math`, `itertools`, `time` и прочие. -В отличие от встроенных модулей, которые при поиске проверяются первыми, остальные (не встроенные) модули стандартной библиотеки проверяются после директории запущенного скрипта. Это приводит к сбивающему с толку поведению: возможно «заменить» некоторые, но не все модули стандартной библиотеки. Допустим, модуль `math` является встроенным модулем, а `random` — нет. Таким образом, `import math в start.py` импортирует модуль из стандартной библиотеки, а не наш файл `math.py` из той же директории. В то же время, `import random` в `start.py` импортирует наш файл `random.py`. +В отличие от встроенных модулей, которые при поиске проверяются первыми, остальные (не встроенные) модули стандартной +библиотеки проверяются после директории запущенного скрипта. Это приводит к сбивающему с толку поведению: возможно +«заменить» некоторые, но не все модули стандартной библиотеки. Допустим, модуль `math` является встроенным модулем, +а `random` — нет. Таким образом, `import math в start.py` импортирует модуль из стандартной библиотеки, а не наш +файл `math.py` из той же директории. В то же время, `import random` в `start.py` импортирует наш файл `random.py`. Кроме того, импорты в Python регистрозависимы: `import Spam` и `import spam` — разные вещи. -Функцию `pkgutil.iter_modules()` (Python 2 и Python 3) можно использовать, чтобы получить список всех модулей, которые можно импортировать из заданного пути: +Функцию `pkgutil.iter_modules()` (Python 2 и Python 3) можно использовать, чтобы получить список всех модулей, которые +можно импортировать из заданного пути: ```python import pkgutil search_path = ['.'] # Используйте None, чтобы увидеть все модули, импортируемые из sys.path @@ -558,36 +729,49 @@ print(all_modules) import sys print(sys.path) ``` -Документация Python описывает sys.path так: +Документация Python описывает `sys.path` так: -Список строк, указывающих пути для поиска модулей. Инициализируется из переменной окружения `PYTHONPATH` и директории по умолчанию, которая зависит от дистрибутива Python. +Список строк, указывающих пути для поиска модулей. Инициализируется из переменной окружения `PYTHONPATH` и директории +по умолчанию, которая зависит от дистрибутива Python. -При запуске программы после инициализации первым элементом этого списка, `path[0]`, будет директория, содержащая скрипт, который был использован для вызова интерпретатора Python. Если директория скрипта недоступна (например, если интерпретатор был вызван в интерактивном режиме или скрипт считывается из стандартного ввода), то `path[0]` является пустой строкой. Из-за этого Python сначала ищет модули в текущей директории. Обратите внимание, что директория скрипта вставляется перед путями, взятыми из `PYTHONPATH`. +При запуске программы после инициализации первым элементом этого списка, `path[0]`, будет директория, содержащая скрипт, +который был использован для вызова интерпретатора Python. Если директория скрипта недоступна (например, если +интерпретатор был вызван в интерактивном режиме или скрипт считывается из стандартного ввода), то `path[0]` является +пустой строкой. Из-за этого Python сначала ищет модули в текущей директории. Обратите внимание, что директория скрипта +вставляется перед путями, взятыми из `PYTHONPATH`. -Документация к интерфейсу командной строки Python добавляет информацию о запуске скриптов из командной строки. В частности, при запуске python