👍3👎1
«Код вызовет ошибку:
TypeError: __init__() takes 2 positional arguments but 3 were given
Ошибка типа: __init__() принимает 2 позиционных аргумента, но было предоставлено 3.
Проблема связана с тем, что вызов super в Chef не обращается к своему суперклассу Employee, но взамен делает обращение к родственному классу того же уровня и следующему в MRO, т.е. Server. Поскольку конструктор родственного класса имеет список аргументов, отличающийся от списка аргументов в конструкторе настоящего суперкласса, который ожидает только self и name, код перестает работать».
👍2👎1😱1
👍2👎1
«Но обратите внимание, что происходит, когда мы изменяем атрибут класса динамически за пределами оператора class: в итоге также изменяется атрибут в каждом объекте, унаследованном от класса. Кроме того, новые экземпляры, созданные из класса в течение текущего сеанса или запуска программы, также получают динамически установленное значение
независимо от того, что указано в исходном коде класса.
...
Это полезная возможность или опасная ловушка? Судить вам. ... вы можете сделать работу за счет изменения атрибутов класса даже без создания одиночного экземпляра — методика, с помощью которой удастся эмулировать использование “записей” или “структур” в других языках. ...
class X: pass # Создать несколько пространств имен атрибутов
class Y: pass
X.а = 1 # Использовать атрибуты классов как переменные
X.b = 2 # Какие-либо экземпляры отсутствуют
X.с = 3
Y.a = X.а + X.b + X.с
for X.i in range (Y.a): print(X.i)
Код выведет:
1
2
3
4
5
Здесь классы X и Y работают подобно модулям «без файлов» — пространствам имен для хранения переменных, которые не должны конфликтовать. Это вполне законный трюк в программировании на Python, но он менее уместен, когда применяется к классам, реализованным другими; вы не всегда можете быть уверены в том, что изменяемые атрибуты отдельного класса не являются критически важными для его внутреннего поведения».
👍4👎2🔥1🤯1
👍2👎1
«При продумывании смысла имен в коде, основанном на классах, полезно помнить о том, что классы в точности как функции вводят локальные области видимости, а методы являются просто более вложенными функциями. В следующем примере функция generate возвращает экземпляр вложенного класса Spam. Внутри своего кода имя класса Spam присваивается в локальной области видимости функции generate и потому оно доступно любой вложенной функции, включая код в method; это соответствует букве Е в правиле LEGB:
def generate():
class Spam: # Spam - имя в локальной области видимости generate
count = 1
def method(self):
print(Spam.count) # Согласно правилу LEGB (E) доступно в области видимости generate
return Spam()
generate().method()
... локальные области видимости всех операторов def объемлющих функций автоматически видны вложенным def».
👍3👎1
👍2👎1
«Оператор with используется для обертывания выполнения блока методами, определенными менеджером контекста (см. раздел Менеджеры контекста оператора With)».
Оператор with / as запускает логику управления контекстом объекта, чтобы гарантировать
выполнение действий при завершении безотносительно к любым исключениям
в его вложенном блоке:
with open('lumberjack.txt', 'w') as file:
file.write('Лиственница!')
# Попытка обратиться к несуществующему атрибуту
file.nonexistent_method() # AttributeError
Файл все равно будет создан, хоть программа и выпадет в исключение:
AttributeError: '_io.TextIOWrapper' object has no attribute 'nonexistent_method'
👍2👎1
сохранять внутреннее состояние между вызовами next(), даже когда происходят исключения.
«При вызове функции-генератора возвращается итератор, известный как генератор. Этот генератор затем управляет выполнением функции-генератора (counter). Выполнение начинается при вызове одного из методов генератора. В этот момент выполнение переходит к первому выражению yield, где снова приостанавливается, возвращая значение yield_list вызывающей стороне генератора или None, если yield_list опущен. Под приостановкой мы подразумеваем сохранение всего локального состояния, включая текущие привязки локальных переменных, указатель инструкций, внутренний стек вычислений и состояние обработки любых исключений».
👍3
«Целевое назначение конструкции else не всегда сразу очевидно для новичков в Python. Однако без нее отсутствует прямой способ сообщить (без установки и проверки булевских флагов), продолжил поток управления выполнение после оператора try из-за того, что никаких исключений не возникало или же исключение произошло и обработано. В любом случае мы оказываемся после оператора try:
try:
...выполнить код...
except IndexError:
...обработать исключение...
# Мы сюда попали из-за того, что try потерпел неудачу или же прошел?
Во многом подобно тому, как конструкции else в циклах придают причине выхода
большую очевидность, конструкция else предоставляет в операторе try синтаксис,
который делает то, что произошло, ясным и недвусмысленным:
try:
...выполнить код...
except IndexError:
...обработать исключение...
else:
...исключения не возникали...»
👍2🔥1