The words you are searching are inside this book. To get more targeted content, please make full-text search by clicking here.
Discover the best professional documents and content resources in AnyFlip Document Base.
Search
Published by Академик Кодер, 2022-02-10 08:20:25

Python 3. Самое необходимое Прохоренок Н. А., Дронов В. А.

Python 3. Самое необходимое

250 Гпава 13

Вн и м ан и е!

Конструктор базового класса автоматически не вызывается, если он переопределен в про-
изводном классе.

Чтобы вызвать одноименный метод из базового класса, также можно воспользоваться
функцией super (). Формат функции:

super([<Класс>, <Указатель self>])

С ПОМОЩЬЮ функции super () инструкцию

Classl.__init__ (self) # Вызываем конструктор базового класса

можно записать так:

supe r ! ) .__ init__ О # Вызываем конструктор базового класса

или так:

super(Class2, self).__init__О # Вызываем конструктор базового класса

Обратите внимание на то, что при использовании функции super () не нужно явно переда-

вать указатель self в вызываемый метод. Кроме того, в первом параметре функции super о
указывается производный класс, а не базовый. Поиск идентификатора будет производиться
во всех базовых классах. Результатом поиска станет первый найденный идентификатор

в цепочке наследования.

Пр и м е ч а н и е

Все классы в Python 3 неявно наследуют класс object, даже если он не указан в списке на-
следования.

13.4. Множественное наследование

В определении класса в круглых скобках можно указать сразу несколько базовых классов
через запятую. Рассмотрим множественное наследование на примере (листинг 13.9).

_Л__ис__т_и_н_г_1_3_.9_._М_н_о_ж__ес_т_в_е_н_н_о_е_н_ас__ле_д__о_в_ан__и_е____ ___ _____ _______

class Classl: # Базовый класс для класса Class2

def f u n d (self):

print ("Метод f u n d () класса Classl")

class Class2(Classl): # Класс Class2 наследует класс Classl
def func2(self):
print("Метод func2() класса Class2")

class Class3(Classl): # Класс Class3 наследует класс Classl
def f u n d (self):
print ("Метод fund!) класса Class3”)
def func2(self):
print("Метод func2() класса Class3")
def func3(self):
print("Метод func3() класса Class3")

Объектно-ориентированное программирование 251

def func4{self):
print("Метод func4() класса Class3")

class Class4(Class2, Class3): # Множественное наследование

def func4(self):

p r i n t ( "Метод fu n c4 () класса C la s s 4 ")

c = C lass4() # Создаем экземпляр класса C la s s 4

c . f u n d () # Выведет: Метод f u n c l( ) класса C la s s 3

с . f u n c 2 () # Выведет:Метод f u n c 2 ()класса C la s s 2

c .fu n c 3 () # Выведет: Метод fu n c 3 () класса C la s s 3

c.fu n c4() # Выведет: Метод fu n c 4 () класса C la s s 4

Метод f u n d () определен в двух классах: ciassi и ciass3. Так как вначале просматривают-
ся все базовые классы, непосредственно указанные в определении текущего класса, метод
f u n d () будет найден в классе ciass3 (поскольку он указан в числе базовых классов в опре-
делении Class4), а не в классе Classl.

Метод func2 о также определен в двух классах: Class2 и ciass3. Так как класс ciass2 стоит
первым в списке базовых классов, то метод будет найден именно в нем. Чтобы наследовать
метод из класса ciass3, следует указать это явным образом. Переделаем определение класса
ciass4 из предыдущего примера и наследуем метод func2() из класса ciass3 (лис-
тинг 13.10).

Листинг 13.10, Указание класса при наследовании метода

class Class4(Class2, Class3): # Множественное наследование
# Наследуем func2() из класса Class3, а не из класса Class2
func2 = Class3.func2
def func4(self):
print("Метод func4() класса Class4")

Вернемся к листингу 13.9. Метод func3 о определен только в классе ciass3, поэтому метод
наследуется от этого класса. Метод func4 (), определенный в классе ciass3, переопределя-
ется в производном классе.
Если искомый метод найден в производном классе, то вся иерархия наследования просмат-
риваться не будет.

Для получения перечня базовых классов можно воспользоваться атрибутом _bases .
В качестве значения атрибут возвращает кортеж. В качестве примера выведем базовые
классы для всех классов из предыдущего примера:

» > print (Classl.__bases__)
» > print (Class2.__bases__)
» > print (Class3.__bases__)
» > print (Class4. bases__)

Выведет:

(<class 'object'>,)
(<class ' main__.Classl'>,)
(<class ' main__.Classl'>,)

252 Гпава 13

Рассмотрим порядок поиска идентификаторов при сложной иерархии множественного на-
следования (листинг 13.11).

Листинг 13.11. Поиск идентификаторов при множественном наследовании

class Classl: х = 10
class Class2(Classl): pass
class Class3(Class2): pass
class Class4(Class3): pass
class Class5(Class2): pass
class Class6(Class5): pass
class Class7(Class4, Class6): pass
c = Class7 ()
print(c.x)

Последовательность поиска атрибута x будет такой:

Class7 -> Class4 -> Class3 - > Class6 -> Class5 -> Class2 -> Classl

Получить всю цепочку наследования позволяет атрибут_mro__:

» > print (Class7. mro__)

Результат выполнения:

(cclass ' main__ .Class7’>, <class 1 main__ .Class4'>,
<class ' main__ .Class3'>, <class 1 main__ .Class6'>,
<class ' main__ .Class5'>, <class ' main__ .Class2'>,
<class 1 main__.Classl'>, <class 'object'>)

13.4.1. Примеси и их использование

Множественное наследование, поддерживаемое Python, позволяет реализовать интересный
способ расширения функциональности классов с помощью так называемых примесей
(mixins). Примесь — это класс, включающий какие-либо атрибуты и методы, которые необ-
ходимо добавить к другим классам. Объявляются они точно так же, как и обычные классы.

В качестве примера объявим класс-примесь Mixin, после чего объявим еще два класса,
добавим к их функциональности ту, что определена в примеси Mixin, и проверим ее в дей-
ствии (листинг 13.12).

Листинг 13.12. Расширение функциональности классов посредством примеси

class Mixin: # Определяем сам класс-примесь
attr = 0 # Определяем атрибут примеси
def mixin_method(self): # Определяем метод примеси
print("Метод примеси")

class Classl (Mixin):
def methodl(self):
print("Метод класса Classl")

Объектно-ориентированное программирование 253

class Class2 (Classl, Mixin):
def method2(self):
print("Метод класса Class2")

cl = Classl () # Classl поддерживает метод примеси
cl .methodl()
cl .mixin_method()

c2 = Class2 () # Class2 также поддерживает метод примеси
c2 .methodl()
c2.method2()
c2,mixin_method()

Вот результат выполнения кода, приведенного в листинге 13.12:

Метод класса Classl
Метод примеси
Метод класса Classl
Метод класса Class2
Метод примеси

Примеси активно применяются в различных дополнительных библиотеках, в частности
в популярном веб-фреймворке Django.

13.5. Специальные методы

Классы поддерживают следующие специальные методы:

♦ __call__о — позволяет обработать вызов экземпляра класса как вызов функции. Фор-
мат метода:

__call__(self[, <Параметр1>[, <ПараметрЫ>]])

Пример:

class MyClass: ■ # Создание экземпляракласса
def init (self, m) : # Создание экземпляракласса
self.msg = m # Выведет: Значение1
def call (self) : # Выведет: Значение2
print(self.msg)

cl = MyClass("Значение1")
с2 = MyClass("Значение2")
с1()
с2 ()

♦ __getattr__(self, <Атрибут>) — вызывается при обращении к несуществующему атри-
буту класса:

class MyClass:
def __init__(self) :
self.i = 20
def __getattr__(self, attr):
print("Вызван метод __getattr__()")
return 0

254 Гпава 13

с = MyClass()

# Атрибут i существует

p r in t(с .i) # Выведет: 20. Метод__g e ta ttr __() не вызывается

# Атрибут s не существует

p r in t(c .s) # Выведет: Вызван метод __g e ta ttr __() 0

♦ __g e ta ttr ib u te__( s e lf , <Атрибут>) — вызывается при обращении к любому атрибуту
класса. Необходимо учитывать, что использование точечной нотации (для обращения
к атрибуту класса) внутри этого метода приведет к зацикливанию. Чтобы избежать за-
цикливания, следует вызвать м е т о д __g e ta ttr ib u te __() объекта object и внутри этого
метода вернуть значение атрибута или возбудить исключение AttributeError:

c la ss MyClass:

def __i n i t __(se lf) :

s e l f . i = 20

def __g e ta ttr ib u te__( s e lf , a t t r ) :

p r in t ("Вызван метод __g e ta ttr ib u te__()")

return o b je c t.__g e ta ttr ib u te__( s e lf , a ttr) # Только так !!!

с = MyClass()

p r in t(c .i) # Выведет: Вызван метод __g e ta ttr ib u te__О 20

♦ __s e ta ttr __( s e lf , <Атрибут>, <3начение>) — вызывается при попытке присваивания
значения атрибуту экземпляра класса. Если внутри метода необходимо присвоить зна-
чение атрибуту, следует использовать словарь__d ie t__, поскольку при применении то-
чечной нотации м етод__s e ta ttr __о будет вызван повторно, что приведет к зациклива-
нию:

c la ss MyClass:

def __s e ta ttr __( s e lf , a ttr , v a lu e ):

p r in t ("Вызван метод __s e ta ttr __()")

s e l f . __d ie t__[attr] = value # Только так !!!

с = MyClass()

c . i = 10 # Выведет: Вызван метод __s e ta ttr __()

p r in t(с .i) # Выведет: 10

♦ __d e la ttr __( s e lf , <Атрибут>) — вызывается при удалении атрибута с помощью инст-
рукции d el Окземпляр класса>.<Атрибут>;

♦ __1еп__(se lf) — вызывается при использовании функции 1еп(), а также для проверки
объекта на логическое значение при отсутствии м етода__bool__(). Метод должен воз-
вращать положительное целое число:

c la ss MyClass: # Выведет: 50
def __len__(se lf) :
return 50

c = MyClass()
p r in t(le n (с ))

♦ __bool__(s e lf) — вызывается при использовании функции bool ();

♦ __in t__(se lf) — вызывается при преобразовании объекта в целое число с помощью
функции in t ();

♦ __f lo a t __ ( s e lf ) — вызывается при преобразовании объекта в вещественное число
с помощью функции flo a t ();

Объектно-ориентированное программирование 255

♦ __complex__(se lf) — вызывается при преобразовании объекта в комплексное число
с помощью функции complex ();

♦ __round ( s e lf , n) — вызывается при использовании функции round ();

♦ __index_(se lf) — вызывается при использовании функций bin (), hex () и oct ();

♦ __repr__(se lf) и __ s tr __(se lf) — служат для преобразования объекта в строку. Метод
__repr__() вызывается при выводе в интерактивной оболочке, а также при использова-
нии функции repr о . М е т о д __s tr __о вызывается при выводе с помощью функции
p rin t (), а также при использовании функции s tr (). Если м етод __s tr __() отсутствует,
будет вызван м е т о д __repr__(). В качестве значения м етоды __ repr__() и __ str __()
должны возвращать строку:

c la s s MyClass:

def __i n i t __( s e lf , m):

self.m sg = m

def __repr__ ( s e l f ) :

return "Вызван м етод__repr__() {O)".form at(self.m sg)

def __str __ (s e lf) :

return "Вызван метод __s tr __() (0 )" .form at(self.m sg)

c = MyClass("Значение")

p r in t(r e p r (с )) # Выведет: Вызван метод __repr__ () Значение

p r in t ( s t r (с )) # Выведет: Вызван метод __s tr __ () Значение

p r in t (с) # Выведет: Вызван метод __s tr __ О Значение

♦ __hash__(se lf) — этот метод следует переопределить, если экземпляр класса планиру-
ется использовать в качестве ключа словаря или внутри множества:

c la s s MyClass:
def __i n i t __ ( s e lf , у) :
self.x = у
def __hash__ (se lf) :
return hash(self.x)

c = MyClass(10)
d= U
d[c] = "Значение"
p r in t(d [ с ] ) # Выведет: Значение

Классы поддерживают еще несколько специальных методов, которые применяются лишь
в особых случаях и будут рассмотрены в главе 15.

13.6. Перегрузка операторов

Перегрузка операторов позволяет экземплярам классов участвовать в обычных операциях.
Чтобы перегрузить оператор, необходимо в классе определить метод со специальным
названием.
Перегрузка математических операторов производится с помощью следующих методов:
♦ х + у — сл ож ен и ем .__add__(у);

♦ у + х — сложение (экземпляр класса справа): х .__radd__(у);
♦ х += у — сложение и присваивание: х .__iadd__(у);

♦ х - у — вычитание: х . sub (у);

256 Гпава 13

♦ у - х — вычитание (экземпляр класса справа): х .__rsub__(у ) ;
♦ х -= у — вычитание и присваивание: х .__isu b__(у);
♦ х * у — ум н о ж ен и ем .__mul__(у);
♦ у * х — умножение (экземпляр класса справа): х .__rmul__(у);
♦ х *= у — умножение и присваивание: х .__imul__(у);
♦ х / у — д е л е н и е м .__tru e d iv __(у);
♦ у / х — деление (экземпляр класса справа): х .__rtru e d iv __(у);
♦ х /= у — деление и п ри сваи ван и ем .__itr u e d iv __(у);
♦ х / / у — деление с округлением в н и з м .__flo o rd iv __(у);
♦ у / / х — деление с округлением вниз (экземпляр класса сп р а в а ):х .__rfio o rd iv __(у);
♦ х / / = у — деление с округлением вниз и при сваи ван и ем .__if lo o r d iv __(у);
♦ х % у — остаток от деления: х .__mod__(у);
♦ у % х — остаток от деления (экземпляр класса справа): х .__rm od_ (у);
♦ х %= у — остаток от деления и присваивание: х .__imod__(у);
♦ х ** у — возведение в с т е п е н ь м .__pow__(у);
♦ у ** х — возведение в степень (экземпляр класса справа): х .__rpow__(у);
♦ х **= у — возведение в степень и присваивание: х .__ipow__(у);
♦ -х — унарный минус: х .__neg__();
♦ +х — унарный плюс: х .__pos__();
♦ abs (х) — абсолютное значение: х .__abs__().
Пример перегрузки математических операторов приведен в листинге 13.13.

Листинг 13.13. Пример перегрузки математических операторов

c la s s MyClass:

d e f __ i n i t ___( s e l f , у ) :

s e lf.x = у

d e f __ add__ ( s e l f , y) : # Перегрузка оператора +

p r i n t ( "Экземпляр слева")

return s e lf.x + у

d e f __ ra d d ___( s e l f , y ) : # Перегрузка оператора +

p r i n t ( "Экземпляр справа")

return s e lf.x + у

d e f __ ia d d ___( s e l f , y) : # Перегрузка оператора

p r i n t ( "Сложение с присваиванием")

s e lf .x += у

return s e lf

c = M yC lass(50)

p rin t(c + 1 0 ) # Выведет: Экземпляр слева 60

p r i n t (20 + с) # Выведет: Экземпляр справа 70

с += 30 # Выведет: Сложение с присваиванием

p r in t (с .х ) # Выведет: 80

Объектно-ориентированное программирование 257

Перегрузка двоичных операторов производится с помощью следующих методов:
♦ ~х — двоичная инверсия: х .__in v e rt__();
♦ х & у — двоичное И: х .__and__(у);
♦ у s х — двоичное И (экземпляр класса справа): х .__rand__(у);
♦ х s= у — двоичное И и присваивание: х .__iand__(у);
♦ х | у — двоичное ИЛИ: х .__or__(у);
♦ у I х — двоичное ИЛИ (экземпляр класса справа): х .__гог__(у);
♦ х | = у — двоичное ИЛИ и присваивание: х .__io r __(у);
♦ х л у — двоичное исключающее ИЛИ: х .__хог__(у);
♦ у л х — двоичное исключающее ИЛИ (экземпляр класса справа): х .__гхог__(у);
♦ х л= у — двоичное исключающее ИЛИ и присваивание: х .__ix o r__(у);
♦ х « у — сдвиг влево: х .__ l s h i f t __(у);
♦ у « х — сдвиг влево (экземпляр класса справа): х .__ r i s h i f t __(у);
♦ х « = у — сдвиг влево и присваивание: х .__i l s h i f t __(у);
♦ х » у — сдвиг вправо: х'.__ r s h i f t __(у);
♦ у » х — сдвиг вправо (экземпляр класса справа): х ._r r s h i f t ___ (у ) ;
♦ х » = у — сдвиг вправо и присваивание: х . i r s h i f t __(у).
Перегрузка операторов сравнения производится с помощью следующих методов:
♦ х = у — равно: х .__eq__(у);
♦ х != у — не равно: х .__пе__(у);
♦ х < у — меньше: х .__i t __(у);
♦ х > у — больше: х .__g t__(у);
♦ х <= у — меньше или равно: х ._1е__ (у);
♦ х >= у — больше или равно: х .__де__ (у);
♦ у in х — проверка на вхождение: х .__c o n ta in s__(у).
Пример перегрузки операторов сравнения приведен в листинге 13.14.

Листинг 13.14. Пример перегрузки операторов сравнения

c la s s MyClass:

def __i n i t __( s e l f ) :

s e l f . x = 50

s e l f . a r r = [1, 2, 3, 4, 5]

def __eq__( s e l f , у ) : # Перегрузка оператора =

return self.x = у

def __c o n ta in s__( s e l f , у ) : # Перегрузка оператора in

return у in se lf.a rr

c = MyClass ()

p r i n t ("Равно" i f c == 50 e ls e "He равно") # Выведет: Равно

p r i n t ("Равно" i f с == 51 e ls e "He равно") # Выведет: He равно

p r i n t ("Есть" i f 5 i n c e ls e "Нет") # Выведет: Есть

258 Гпава 13

13.7. Статические методы и методы класса

Внутри класса можно создать метод, который будет доступен без создания экземпляра
класса (статический метод). Для этого перед определением метода внутри класса следует
указать декоратор @stationethod. Вызов статического метода без создания экземпляра клас-
са осуществляется следующим образом:

<Название класса>.<Название метода>(<Параметры>)

Кроме того, можно вызвать статический метод через экземпляр класса:

Окземпляр класса>.<Название метода> (<Параметры>)

Пример использования статических методов приведен в листинге 13.15.

Листинг 13.15. Статические методы

class MyClass:

@staticmethod

def funcl(x, у ) : # Статический метод

return х + у

def func2(self, x, у) : # Обычный метод в классе

return х + у

def func3(self, х, у):

return MyClass.f u n d (х, у) # Вызов из метода класса

print (MyClass. f u n d (10, 20)) # Вызываем статический метод
с = MyClass()
print(c.func2(15, 6)) # Вызываем метод класса
print (с. f u n d (50, 12)) # Вызываем статический метод
# через экземпляр класса
print (с.func3(23, 5)) # Вызываем статический метод
# внутри класса

Обратите внимание на то, что в определении статического метода нет параметра self. Это
означает, что внутри статического метода нет доступа к атрибутам и методам экземпляра
класса.

Методы класса создаются с помощью декоратора Sclassmethod. В качестве первого пара-
метра в метод класса передается ссылка на класс. Вызов метода класса осуществляется
следующим образом:

сНазвание класса>.сНазвание метода>(<Параметры>)

Кроме того, можно вызвать метод класса через экземпляр класса:

Окземпляр класса>.<Название метода> (<Параметры>)

Пример использования методов класса приведен в листинге 13.16.

Листинг 13.15. Методы класса

class MyClass:
@classmethod

Объектно-ориентированное программирование 259

def func(els, x ) : # Метод класса

print(els, x)

MyClass.func(lO) # Вызываем метод через название класса

с = MyClass()

c.func(50) # Вызываем метод класса через экземпляр

13.8. Абстрактные методы

Абстрактные методы содержат только определение метода без реализации. Предполагает-
ся, что производный класс должен переопределить метод и реализовать его функциональ-
ность. Чтобы такое предположение сделать более очевидным, часто внутри абстрактного
метода возбуждают исключение (листинг 13.17).

I—Л■и—ст—и—нвгс1я3_._17щ. Абс..тариа_к_т_н_ы_е_м| ет|_о_д_ы_. . s I ; 8...... 1____ ___ ■ i i______ I ■— I—_ _ _ _ —9

class Classl:

def func(self, x ) : # Абстрактный метод

# Возбуждаем исключение с помощью raise

raise NotlmplementedError("Необходимо переопределить метод")

class Class2(Classl): # Наследуем абстрактный метод
def func(self, х ) : # Переопределяем метод
p r i n t (х)

class Class3(Classl): # Класс не переопределяет метод
pass

с2 = Class2()

с 2 . f u n c (50) # Выведет: 50

сЗ = Class3()

try: # Перехватываем исключения

сЗ.f u n c (50) # Ошибка. Метод funcО не переопределен

except NotlmplementedError as msg:

print(msg) # Выведет: Необходимо переопределить метод

В состав стандартной библиотеки входит модуль abc. В этом модуле определен декоратор
@abst ractmethod, который позволяет указать, что метод, перед которым расположен декора-
тор, является абстрактным. При попытке создать экземпляр производного класса, в котором
не переопределен абстрактный метод, возбуждается исключение TypeError. Рассмотрим

использование декоратора @abstractmethod на примере (листинг 13.18).

Листинг 13.18. Использование декоратора Sabstractmethod

from abc import ABCMeta, abstractmethod

class Classl(metaclass=ABCMeta):

@abstractmethod

def func(self, x ) : # Абстрактный метод

pass

260________________________________________________________________________Гпава 13

class Class2(Classl): # Наследуем абстрактный метод
def func(self, х ) : # Переопределяем метод
print (х)
# Класс не переопределяет метод
class Class3(Classl):
pass

с2 = Class2 () # Выведет: 50
c2.func(50) # Ошибка. Метод func() не переопределен
try:
# Can't instantiate abstract class Class3
сЗ = Class3() # with abstract methods func
сЗ.f u n c (50)
except TypeError as msg:
print(msg)

Имеется возможность создания абстрактных статических методов и абстрактных методов

класса, для чего необходимые декораторы указываются одновременно, друг за другом. Для
примера объявим класс с абстрактным статическим методом и абстрактным методом класса

(листинг 13.19).

Листинг 13.19. Абстрактный статический метод и абстрактный метод класса

from abc import ABCMeta, abstractmethod

class MyClass(metaclass=ABCMeta):

Qstaticmethod

@abstractmethod

def static_func(self, x ) : # Абстрактный статический метод

pass

Sclassmethod # Абстрактный метод класса
Sabstractmethod
def class_func(self, x):

pass

13.9. Ограничение доступа
к идентификаторам внутри класса

Все идентификаторы внутри класса в языке Python являются открытыми, т. е. доступны для
непосредственного изменения. Для имитации частных идентификаторов можно воспользо-
ваться методами_getattr__(),__ getattribute_() и __setattr_(), которые перехваты-
вают обращения к атрибутам класса. Кроме того, можно воспользоваться идентификатора-
ми, названия которых начинаются с двух символов подчеркивания. Такие идентификаторы
называются псевдочастными. Псевдочастные идентификаторы доступны лишь внутри
класса, но никак не вне его. Тем не менее, изменить идентификатор через экземпляр класса
все равно можно, зная, каким образом искажается название идентификатора. Например,
идентификатор _privateVar внутри класса ciassl будет доступен по имени ciassl__
privatevar. Как можно видеть, здесь перед идентификатором добавляется название класса
с предваряющим символом подчеркивания. Приведем пример использования псевдочаст-
ных идентификаторов (листинг 13.20).

Объектно-ориентированное программирование_________________________________________ 261

Листинг 13.20. Псецдочастные идентификаторы

class MyClass: # Изменение значения
def __init__(self, x ) :
self.__privateVar = x # Получение значения
def set_var(self, x):
self.__privateVar = x # Создаем экземпляр класса
def get_var(self): # Выведет: 10
return self.__privateVar # Изменяем значение
# Выведет: 20
c = MyClass(10) # Перехватываем ошибки
print(c.get_var()) # Ошибка!!!
c.set_var(20)
print(c.get_var()) # Выведет: 'MyClass' object has
try: # no attribute '__privateVar'
# Значение псевдочастных атрибутов
p r i n t (с.__ privateVar) # все равно можно изменить
except AttributeError as msg: # Выведет: 50

print(msg)

c._MyClass__privateVar = 5 0

print(c.get_var())

Можно также ограничить перечень атрибутов, разрешенных для экземпляров класса. Для

этого разрешенные атрибуты указываются внутри класса в гтрибуте__slots__.В качестве
значения атрибуту можно присвоить строку или список строк с названиями идентифика-
торов. Если производится попытка обращения к атрибуту, не указанному в __slots__, воз-

буждается исключение AttributeError (листинг 13.21).

Глистинг 13.21. Использование атрибута__ s lo ts __

c la s s MyClass: # Выведет: 1 2
__ s l o t s = [ " x " , " y " ] # Изменяем значения атрибутов
d e f __ i n i t __ ( s e l f , a , b ) : # Выведет: 10 20
s e lf.x , s e lf.у = a, b # Перехватываем исключения
# А трибут z не у к а з а н в __ s l o t s __ ,
c = M yC lass(1, 2)
p rin t(c .x , c.y) # поэтому возбуждается исключение
c .x , c .y = 10, 20
p rin t(c .x , c.y) # 'M yC lass' o b je c t has no a t t r ib u t e 'z '
try :

c .z = 5 0

e x c e p t A t t r ib u t e E r r o r as msg:
p rin t(m sg )

13.10. Свойства класса

Внутри класса можно создать идентификатор, через который в дальнейшем будут произво-
диться операции получения и изменения значения какого-либо атрибута, а также его удале-
ния. Создается такой идентификатор с помощью функции p ro p e rty ( ). Формат функции:

262__________________________________________________________________________________Гпава 13

<Свойство> = property (<Чтение>[, <3апись>[, <Удаление:> [,
<Строка документирования^]])

В первых трех параметрах указываются ссылки на соответствующие методы класса. При
попытке получить значение будет вызван метод, указанный в первом параметре. При опе-
рации присваивания значения будет вызван метод, указанный во втором параметре, — этот
метод должен принимать один параметр. В случае удаления атрибута вызывается метод,
указанный в третьем параметре. Если в качестве какого-либо параметра задано значение
None, то это означает, что соответствующий метод не поддерживается. Рассмотрим свойства
класса на примере (листинг 13.22).

Листинг 13.22. Свойства класса

class MyClass:

def __init__(self, value):

self.__var = value

def get_var(self): # Чтение

return self.__var

def set_var(self, value): # Запись

self.__var = value

def del_var(self): # Удаление

del self.__var

v = property(get_var, set_var, del_var, "Строка документирования")

с = MyClass(5)

c.v = 35 # Вызывается метод set_var()

print)c.v) # Вызывается метод get_var()

del c.v # Вызывается метод del_var()

Python поддерживает альтернативный метод определения свойств— с помощью методов

getter(), setter() и deleter!), которые используются в декораторах. Соответствующий
пример приведен в листинге 13.23.

Листинг 13.23. Методы g e tte r (), s e tte r () и d eletarO

class MyClass: # Чтение
def __init__(self, value) :
self.__var = value # Запись
Sproperty
def v(self): # Удаление
return self.__var
@v.setter # Запись
def v(self, value): # Чтение
self.__var = value # Удаление
@v.deleter
def v(self):
del self.__var

c = MyClass(5)
c.v = 35
print(c.v)
del c.v

Объектно-ориентированное программирование 263

Имеется возможность определить абстрактное свойство — в этом случае все реализующие
его методы должны быть переопределены в подклассе. Выполняется это с помощью знако-
мого нам декоратора @ abstractmethod из модуля abc. Пример определения абстрактного
свойства показан в листинге 13.24.

Листинг 13.24. Определение абстрактного свойства

from abc in p o r t ABCMeta, a b s tra c tm e th o d

c la ss M yC lassl(m etaclass=A B C M eta):

d e f __ i n i t __ ( s e l f , v a lu e ) :

s e l f . __ v a r = v a lu e

© property

©abst ractm ethod

def v (s e lf): # Чтение

r e t u r n s e l f . __ v a r

@ v.setter

@abst ractm etho d

def v (s e lf, v a lu e ): # Запись

s e l f . __ v a r = v a lu e

@ v.d eleter

©abstractm ethod

def v (s e lf): # Удаление

d e l s e l f . __ v a r

13.11. Декораторы классов

В языке Python, помимо декораторов функций, поддерживаются декорат оры классов, кото-
рые позволяют изменить поведение самих классов. В качестве параметра декоратор прини-
мает ссылку на объект класса, поведение которого необходимо изменить, и должен возвра-
щать ссылку на тот же класс или какой-либо другой. Пример декорирования класса показан
в листинге 13.25.

Листинг 13.25. Декоратор класса

def deco(С ): # Принимает объект класса
p r i n t ("Внутри декоратора") # Производит ка ки е -то действия
return С # Возвращает объект класса

©deco
c la s s M yClass:

d e f __ i n i t __ ( s e l f , v a lu e ) :
s e lf.v = value

c = M yC lass(5)
p rin t(c .v )

ГЛАВА 14

Обработка исключений

Исключения — это извещения интерпретатора, возбуждаемые в случае возникновения
ошибки в программном коде или при наступлении какого-либо события. Если в коде не
предусмотрена обработка исключения, выполнение программы прерывается, и выводится
сообщение об ошибке.

В программе могут встретиться три типа ошибок:

♦ синт аксические — это ошибки в имени оператора или функции, отсутствие закрываю-
щей или открывающей кавычек и т. д. — т. е. ошибки в синтаксисе языка. Как правило,
интерпретатор предупредит о наличии такой ошибки, а программа не будет выполняться
совсем. Пример синтаксической ошибки:

» > p r i n t ( "Нет завершающей кавы чки!)

S y n ta x E r r o r : EOL w h ile s c a n n in g s t r i n g l i t e r a l

♦ логические — это ошибки в логике программы, которые можно выявить только по ре-
зультатам ее работы. Как правило, интерпретатор не предупреждает о наличии такой
ошибки, и программа будет успешно выполняться, но результат ее выполнения окажется
не тем, на который мы рассчитывали. Выявить и исправить логические ошибки весьма
трудно;

♦ ошибки времени выполнения — это ошибки, которые возникают во время работы про-
граммы. Причиной являются события, не предусмотренные программистом. Класси-
ческим примером служит деление на ноль:

» > d e f te s t (х, у) : re tu rn х / у

» > te s t (4, 2) # Нормально

2.0

» > te s t (4, 0) # Ошибка

T raceback (most re c e n t c a l l l a s t ) :

F il e " < p y s h e ll# 4 > " , l i n e 1 , in <module>

te s t (4, 0) # Ошибка

F ile "<p ysh ell#2>", lin e 1, in te s t

d e f te s t (x, y) : re tu rn x / у

Z ero D iv is io n E rro r: d iv is io n by zero

Необходимо заметить, что в Python исключения возбуждаются не только при возникнове-
нии ошибки, но и как уведомление о наступлении каких-либо событий. Например, метод

Обработка исключений 265

» > "Строка", index ("текст")
Traceback (most recent call last):

File "<pyshell#5>", line 1, in <module>
"Строка".index("текст")

ValueError: substring not found

14.1. Инструкция try...except...else...finally

Для обработки исключений предназначена инструкция try. Формат инструкции:

try:
<Блок, в котором перехватываются исключения»

[except [<Исключение1> [ as <Объект исключения:»] ]:
<Блок, выполняемый при возникновении исключения»

[...

except [сИсключениеЫ»[ as <Объект исключения»]]:
<Елок, выполняемый при возникновении исключения»]]

[else:
<Блок, выполняемый, если исключение не возникло»]

[finally:
<Блок, выполняемый в любом случае»]

Инструкции, в которых перехватываются исключения, должны быть расположены внутри
блока try. В блоке except в параметре Исключение 1> указывается класс обрабатываемого
исключения. Например, обработать исключение, возникающее при делении на ноль, можно
так, как показано в листинге 14.1.

Листинг 14.1. Обработка деления на ноль

try: # Перехватываем исключения

х= 1/0 # Ошибка: деление на О

except ZeroDivisionError: # Указываем класс исключения

print("Обработали деление на О")

х=О

p r i n t (х) # Выведет: О

Если в блоке try возникло исключение, управление передается блоку except. В случае если
исключение не соответствует указанному классу, управление передается следующему бло-

ку except. Если ни один блок except не соответствует исключению, то исключение «всплы-
вает» к обработчику более высокого уровня. Если исключение в программе вообще нигде
не обрабатывается, оно передается обработчику по умолчанию, который останавливает вы-
полнение программы и выводит стандартную информацию об ошибке. Таким образом,

в обработчике может присутствовать несколько блоков except с разными классами исклю-
чений. Кроме того, один обработчик можно вложить в другой (листинг 14.2).

Листинг 14.2. Вложенные обработчики # Обрабатываем исключения
# Вложенный обработчик
try: # Ошибка: деление на О
try:
х= 1/0

266 Гпава 14

except NameError:

p r i n t ( "Неопределенный идентификатор")

except Ind exE rro r:

p r i n t ( "Несуществующий и н д е кс ")

p r i n t ("Выражение после вложенного обработчика")

except Z ero D ivisio n E rro r:

p r i n t ( "О бработка деления на О")

х=О

p r i n t (х) # Выведет: О

В этом примере во вложенном обработчике не указано исключение Z e ro D iv is io n E rro r,
поэтому исключение «всплывает» к обработчику более высокого уровня.

После обработки исключения управление передается инструкции, расположенной сразу
после обработчика. В нашем примере управление будет передано инструкции, выводя-
щей значение переменной х ,— p r in t (х ). Обратите внимание на то, что инструкция
p r i n t ( "Выражение после вложенного о бработчика") выполнена не будет.

В инструкции except можно указать сразу несколько исключений, записав их через запятую
внутри круглых скобок (листинг 14.3).

---- --------------------------------------------------------- ---------------------

Листинг 14.3. Обработка нескольких исключений

try :
х=1/ О

except (NameError, In d e x E rro r, Z e ro D iv is io n E rro r):
# Обработка сразу нескольких исключений
х=О

p r i n t (х) # Выведет: О

Получить информацию об обрабатываемом исключении можно через второй параметр
в инструкции e x c e p t (листинг 14.4).

Листинг 14.4. Получение информации об исключении
Ш ДМ 1ИИИ а /............. .......................- — —— ........... ................. ^ ................ : <ИЯиШмшВИИИ

try : Ind exE rro r, # Ошибка деления на О
х=1/ 0 Z eroD ivision E rro r) as e rr:

except (NameError,

p r i n t ( e r r . __ c la s s __ .__ name__ ) # Название кл асса исключения

p rin t(e rr) # Т екст сообщения об ошибке

Результат выполнения:

Z ero D ivisio n E rro r
d iv is io n by zero

Для получения информации об исключении можно воспользоваться функцией e x c _ in fo ()
из модуля sys, которая возвращает кортеж из трех элементов: типа исключения, значения
и объекта с трассировочной информацией. Преобразовать эти значения в удобочитаемый
вид позволяет модуль traceback. Пример использования функции e x c _ in fo () и модуля

Обработка исключений 267

Листинг 14.5. Пример использования функции exc info () ... — .— .— .— !...........................................— —--------
... .......................

import sys, traceback
try:

x= 1/О
except ZeroDivisionError:

Type, Value, Trace = sys.exc_info()
print("Type: ", Type)
print("Value:", Value)
print("Trace:", Trace)
print("\n", ”print_exception()",center(40, "-"))
traceback.print_exception(Type, Value, Trace, limit=5,

file=sys.stdout)
print("\n", "print_tb()".center(40, "-"))
traceback.print_tb(Trace, limit=l, file=sys.stdout)
print("\n", "format_exception()” .center(40, "-"))
print(traceback.format_exception(Type, Value, Trace, limit=5))
print("\n", "format_exception_only()".center(40, "-"))
print(traceback.fonnat_exception_only(Type, Value))

Результат выполнения примера:

Type: <class 'ZeroDivisionError'>
Value: division by zero
Trace: <traceback object at 0x00000179D4142508>

----------- print_exception()------------
Traceback (most recent call last):

File "D: /Data/floKyMeHTbi/Pa6oTa/KHMrn/Python 3. Самое необходимое
11/Примеры/14/14.5.py", line 3, in <module>

x= 1/0
ZeroDivisionError: division by zero

-------------- print_tb()----------------
File "D:/Data/flOKyMeHTH/Pa6oTa/KHnra/Python 3. Самое необходимое

11/Примеры/14/14.5.py", line 3, in <module>
x= 1/0

---------- format_exception()------------

['Traceback (most recent call last):\n', ' File

"D:/Оа1а/Документы/Работа/Книги/Ру1Ьоп 3. Самое необходимое

11/Примеры/14/14.5.py", line 3, in <module>\n x = 1 / 0\n', 'ZeroDivisionError:

division by zero\n']

-------- f o rmat_exception_only()---------
['ZeroDivisionError: division by zero\n']

Если в инструкции except не указан класс исключения, то такой блок будет перехватывать

все исключения. На практике следует избегать пустых инструкций except, т. к. можно пере-

хватить исключение, которое является лишь сигналом системе, а не ошибкой. Пример пус-

той инструкции приведен в листинге 14.6.

268 Глава 14

Листинг 14.6. Пример перехвата всех исключений

------------- а.--------- :--------------—-------- :---------------------------а------------ *--------------а------ а------- ------------------_________------- ---------- -----------;-----_ ---------------------------------- ---------------------- --------------------- --------_ _ --------ill__Ь. -а-. .. ... , .____ ____

try: , # Ошибка деления на О
х=1/0 # Обработка всех исключений

except:

х=О # Выведет: О
p r i n t (х)

Если в обработчике присутствует блок else, то инструкции внутри этого блока будут вы-
полнены только при отсутствии ошибок. При необходимости выполнить какие-либо завер-
шающие действия вне зависимости от того, возникло исключение или нет, следует восполь-

зоваться блоком finally. Для примера выведем последовательность выполнения блоков
(листинг 14.7).

Листинг 14.7. Блоки e lse и f in a lly

try: # Нет ошибки
х = 10 / 2 # Ошибка деления на 0
#х = 10 / 0

except ZeroDivisionError:
print("Деление на 0")

else:
print("Блок else”)

finally:
print("Елок finally")

Результат выполнения при отсутствии исключения:

Блок else
Блок finally

Последовательность выполнения блоков при наличии исключения будет другой:

Деление на 0
Блок finally

Необходимо заметить, что при наличии исключения и отсутствии блока except инструкции
внутри блока finally будут выполнены, но исключение не будет обработано. Оно продол-
жит «всплывание» к обработчику более высокого уровня. Если пользовательский обработ-
чик отсутствует, управление передается обработчику по умолчанию, который прерывает
выполнение программы и выводит сообщение об ошибке:

» > try:
х = 10 / 0

finally: print("Блок finally")

Блок finally
Traceback (most recent call last):

File "<pyshell#17>", line 2, in <module>
x = 10 / 0

ZeroDivisionError: division by zero

Обработка исключений 269

В качестве примера переделаем нашу программу суммирования произвольного количества
целых чисел, введенных пользователем (см. листинг 4.12), таким образом, чтобы при вводе
строки вместо числа программа не завершалась с фатальной ошибкой (листинг 14.8).

Листинг 14.8. Суммирование не определенного заранее количества чисел

# coding: u tf -8

p r i n t ( "Введите слово 's to p ' для получения результата")

summa = О

w h ile True:

х = in p u t ("Введите число: ")

i f х == "stop ":

b re a k # Выход и з цикла

try :

х = in t ( x ) # Преобразуем строку в число

except V alu eE rror:

p r i n t ( "Необходимо ввести целое ч и с л о !")

e ls e :

summa += х

p r i n t ("Сумма чисел р а в н а :" , summa)

i n p u t ()

Процесс ввода значений и получения результата выглядит так (значения, введенные поль-
зователем, выделены полужирным шрифтом):

Введите слово 's to p ' для получения результата

Введите число: 10

Введите число: str
Необходимо ввести целое число!
Введите число: -5
Введите число:
Необходимо ввести целое число!
Введите число: stop
Сумма чисел р а в н а : 5

14.2. Инструкция w ith...as

Язык Python поддерживает прот окол м енедж еров конт екст а. Этот протокол гарантирует
выполнение завершающих действий (например, закрытие файла) вне зависимости от того,
произошло исключение внутри блока кода или нет.

Для работы с протоколом менеджеров контекста предназначена инструкция w ith ...a s .
Инструкция имеет следующий формат:

w ith <Выражение1>[ as <П ерем енная>][, . . . , <ВыражениеЫ>[ as <Переменная>]] :
<Блок, в котором перехватываем исключения:»

Вначале вычисляется <выражение1>, которое должно возвращать объект, поддерживающий

протокол. Этот объект должен поддерживать два м ето д а:__e n te r __о и __ e x i t _ o . Метод

e n te r о вызывается после создания объекта. Значение, возвращаемое этим методом,

270 Гпава 14

присваивается переменной, указанной после ключевого слова as. Если переменная не ука-
зана, возвращаемое значение игнорируется. Формат метода_enter__():

__enter__(self)

Далее выполняются инструкции внутри тела инструкции with. Если при выполнении воз-
никло исключение, то управление передается методу__exit__(). Метод имеет следующий
формат:

__exit__(self, <Тип исключения;-, <3начение>, <Объект traceback>)

Значения, доступные через последние три параметра, полностью эквивалентны значениям,
возвращаемым функцией exc_info() из модуля sys. Если исключение обработано, метод
должен вернуть значение True, в противном случае — False. Если метод возвращает False,
исключение передается вышестоящему обработчику.

Если при выполнении инструкций, расположенных внутри тела инструкции with, исклю-
чение не возникло, управление все равно передается методу_exit_о. В этом случае
последние три параметра будут содержать значение None.

Рассмотрим последовательность выполнения протокола менеджеров контекста на примере
(листинг 14.9).

Листинг 14.9. Протокол менеджеров контекста

class MyClass:

def __enter__(self):

print("Вызван метод enter ()")

return self

def __exit - (self, Type, Value, Trace):

print("Вызван метод __exit__()")

if Type is None: # Если исключение не возникло

print("Исключение не возникло")

else: # Если возникло исключение

print("Value =", Value)

return False # False - исключение не обработано

# True — исключение обработано

print("Последовательность при отсутствии исключения:")

with MyClass():

print("Блок внутри with")

print("ХпПоследовательность при наличии исключения:")

with MyClass() as obj:

print("Блок внутри with")

raise TypeError("Исключение TypeError")

Результат выполнения:

Последовательность при отсутствии исключения:
Вызван метод enter О
Елок внутри with
Вызван метод __exit__()
Исключение не возникло
Последовательность при наличии исключения:
Вызван метод enter ()

Обработка исключений 271

Блок внутри with
Вызван метод __exit__()
Value = Исключение TypeError
Traceback (most recent call last):

F i l e "D:/Data/floKyMeHTH/Pa6oTa/KHn™/Python 3. Самое необходимое
11/Примеры/14/14.9.py”, l i n e 19, i n <module>

r a i s e T yp eE rror("Исключение TypeError")
TypeError: Исключение TypeError

Некоторые встроенные объекты, например файлы, поддерживают протокол по умолчанию.
Если в инструкции with указана функция open (), то после выполнения инструкций внутри
блока файл автоматически будет закрыт. Вот пример использования инструкции with:

with open("test.txt", "a", encoding="utf-8") as f:
f.write("Строка\п") # Записываем строку в конец файла

Здесь файл test.txt открывается на дозапись данных. После выполнения функции open о
переменной £ будет присвоена ссылка на объект файла. С помощью этой переменной мы
можем работать с файлом внутри тела инструкции with. После выхода из блока вне зависи-
мости от наличия исключения файл будет закрыт.

14.3. Классы встроенных исключений

Все встроенные исключения в языке Python представляют собой классы. Иерархия встроен-
ных классов исключений схематично выглядит так:

BaseException
SystemExit
Keyboardlnterrupt
GeneratorExit
Exception
Stoplteration
ArithmeticError
FloatingPointError, OverflowError, ZeroDivisionError
Assert ionError
AttributeError
BufferError
EOFError
ImportError
LookupError
IndexError, KeyError
MemoryError
NameError
UnboundLocalError
OSError
BlockinglOError
ChildProcessError
ConnectionError
BrokenPipeError, ConnectionAbortedError,
ConnectionRefusedError, ConnectionResetError

272 Гпава 14

FileExistsError
Fi leNot FoundError
interruptedError
IsADirectoryError
NotADirectoryError
PermissionError
ProcessLookupError
TimeoutError
RecursionError
ReferenceError
RuntimeError
NotImplementedError
SyntaxError
indentationError

TabError
SystemError
TypeError
ValueError

UnicodeError
UnicodeDecodeError, UnicodeEncodeError
UnicodeTranslateError

Warning
BytesWarning, Deprecationwarning, Futurewarning, Importwarning,
PendingDeprecationWarning, Resourcewarning, Runtimewarning,
Syntaxwarning, UnicodeWarning, UserWarning

Основное преимущество использования классов для обработки исключений заключается
в возможности указания базового класса для перехвата всех исключений соответствующих
производных классов. Например, для перехвата деления на ноль мы использовали класс

zeroDivisionError, но если вместо него указать базовый класс ArithmeticError, будут пере-

хватываться исключения классов FloatingPointError, OvertlowError И ZeroDivisionError:

try:

x = 1/0 # Ошибка' деление на О

except ArithmeticError: # Указываем базовый класс

print("Обработали деление на О")

Рассмотрим основные классы встроенных исключений:

♦ BaseException — является классом самого верхнего уровня и базовым для всех прочих
классов исключений;

♦ Exception — базовый класс для большинства встроенных в Python исключений. Именно
его, а не BaseException необходимо наследовать при создании пользовательского класса
исключения;

♦ AssertionError — возбуждается инструкцией assert;

♦ AttributeError — попытка обращения к несуществующему атрибуту объекта;

♦ EOFError — возбуждается функцией input () при достижении конца файла;

♦ importError — невозможно импортировать модуль или пакет;

♦ IndentationError — неправильно расставлены отступы в программе;

Обработка исключений 273

♦ indexError — указанный индекс не существует в последовательности;

♦ KeyError — указанный ключ не существует в словаре;
♦ Keyboardlnterrupt — нажата комбинация клавиш <Ctrl>+<C>;

♦ MemoryError — интерпретатору существенно не хватает оперативной памяти;
♦ NameError — попытка обращения к идентификатору до его определения;
♦ NotimpiementedError — должно возбуждаться в абстрактных методах;
♦ osError — базовый класс для всех исключений, возбуждаемых в ответ на возникновение

ошибок в операционной системе (отсутствие запрошенного файла, недостаток места на
диске и пр.);
♦ overfiowErгог — число, получившееся в результате выполнения арифметической опера-
ции, слишком велико, чтобы Python смог его обработать;
♦ RecursionError — превышено максимальное количество проходов рекурсии;
♦ RuntimeError — неклассифицированная ошибка времени выполнения;

♦ stopiteration — возбуждается методом__next__() как сигнал об окончании итераций;

♦ syntaxError — синтаксическая ошибка;
♦ systemError — ошибка в самой программе интерпретатора Python;
♦ TabError— в исходном коде программы встретился символ табуляции, использование

которого для создания отступов недопустимо;

♦ ТуреЕггог — тип объекта не соответствует ожидаемому;

♦ unboundLocalError — внутри функции переменной присваивается значение после обра-
щения к одноименной глобальной переменной;

♦ unicodeDecodeError — ошибка преобразования последовательности байтов в строку;
♦ unicodeEncodeError — ошибка преобразования строки в последовательность байтов;
♦ UnicodeTranslationError — ошибка преобразования строки в другую кодировку;

♦ vaiueError — переданный параметр не соответствует ожидаемому значению;

♦ ZeroDivisionError — попытка деления на ноль.

14.4. Пользовательские исключения

Для возбуждения пользовательских исключений предназначены две инструкции: ra ise и
assert.

Инструкция r a ise возбуждает заданное исключение. Она имеет несколько вариантов фор-
мата:

r a ise Окземпляр класса>
r a ise сНазвание класса>
r a ise Окземпляр или название класса> from <00ъект исключения>
raise

В первом вариант е ф орм ат а инструкции r a ise указывается экземпляр класса возбуждае-
мого исключения. При создании экземпляра можно передать конструктору класса данные,
которые станут доступны через второй параметр в инструкции except. Приведем пример

274 Гпава 14

» > raise ValueError ("Описание исключения")
Traceback (most recent call last):

File "<pyshell#0>", line 1, in <module>
raise ValueError("Описание исключения")

ValueError: Описание исключения

Пример обработки этого исключения показан в листинге 14.10.

Листинг 14.10. Программное возбуждение исключения

try:
raise ValueError("Описание исключения")

except ValueError as msg:
print(msg) # Выведет: Описание исключения

В качестве исключения можно указать экземпляр пользовательского класса (листинг 14.11).

Листинг 14.11. Создание собственного исключения

class MyError(Exception):

def __init__(self, value):

self.msg = value

def __str__(self) :

return self.msg

# Обработка пользовательского исключения

try:

raise MyError("Описание исключения")

except MyError as err:

print(err) # Вызывается метод __str__()

print(err.msg) # Обращение к атрибуту класса

# Повторно возбуждаем исключение

raise MyError("Описание исключения")

Результат выполнения:

Описание исключения
Описание исключения
Traceback (most recent call last):

File "D:/Data/Документы/Работа/Книги/Python 3. Самое необходимое
11/Примеры/14/14.11.py", line 13, in <module>

raise MyError("Описание исключения")
MyError: Описание исключения

Класс Exception поддерживает все необходимые методы для вывода сообщения об ошибке.
Поэтому в большинстве случаев достаточно создать пустой класс, который наследует класс
Exception (листинг 14.12).

| Листинг 14.12. Упрощенный способ создания собственного исключения

class MyError(Exception): pass
try:

raise MyError("Описание исключения")

Обработка исключений 275

except MyError as err:

print(err) # Выведет: Описание исключения

Во втором варианте формата инструкции raise в первом параметре задается объект клас-
са, а не экземпляр:

try:

raise ValueError # Эквивалентно: raise ValueErrorO

except ValueError:

print("Сообщение об ошибке")

В третьем варианте формата инструкции raise в первом параметре задается экземпляр
класса или просто название класса, а во втором параметре указывается объект исключения.
В этом случае объект исключения сохраняется в атрибуте__cause__. При обработке вло-
женных исключений эти данные используются для вывода информации не только о послед-
нем исключении, но и о первоначальном исключении. Пример этого варианта формата
инструкции raise можно увидеть в листинге 14.13.

Листинг 14.13. Применение третьего варианта формата инструкции ra ise

try:
х= 1/О

except Exception as err:
raise ValueError() from err

Результат выполнения:

Traceback (most recent call last):
File "D: /Data/floKyMeHTbi/Pa6oTa/KHMTO/Python 3. Самое необходимое

11/Примеры/14/14.13.ру", line 2, in <module>
x=1/0

ZeroDivisionError: division by zero

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "D:/Data/floKyMeHibi/Pa6oTa/KHnrn/Python 3. Самое необходимое

11/Примеры/14/14.13.ру", line 4, in <module>
raise ValueError!) from err

ValueError

Как видно из результата, мы получили информацию не только по исключению ValueError,
но и по исключению ZeroDivisionError. Следует заметить, что при отсутствии инструкции
from информация сохраняется неявным образом. Если убрать инструкцию from в предыду-
щем примере, мы получим следующий результат:

Traceback (most recent call last):
File "D: /Data/floKyMeHTbi/PaOoTa/KHnrn/Python 3. Самое необходимое

11/Примеры/14/14.13.ру", line 2, in <module>
x=1/0

ZeroDivisionError: division by zero

276 Гпава 14

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "D:/Data/floKyMeHTbi/PaboTa/KHnrn/Python 3. Самое необходимое

11/Примеры/14/14.13.py", line 4, in <module>
raise ValueErrorO

ValueError

Четвертый вариант формата инструкции raise позволяет повторно возбудить последнее
исключение и обычно применяется в коде, следующем за инструкцией except. Пример
этого варианта показан в листинге 14.14.

Листинг 14.14. Применение четвертого варианта формата инструкции ra is e

class MyError(Exception): pass

try:

raise MyError("Сообщение об ошибке")

except MyError as err:

print(err)

raise # Повторно возбуждаем исключение

Результат выполнения:

Сообщение об ошибке
Traceback (most recent call last):

File "D:/Data/floKyMeHTbi/Pa6oTa/KHnrM/Python 3. Самое необходимое
П/Примеры/14/14.14 .py", line 3, in <module>

raise MyError("Сообщение об ошибке")
MyError: Сообщение об ошибке

Инструкция assert возбуждает исключение AssertionError, если логическое выражение
возвращает значение False. Инструкция имеет следующий формат:

assert «^Логическое выражение>[, <Данные>]

Инструкция assert эквивалентна следующему коду:

if __debug__:
if not Логическое выражение:»:
raise AssertionError(<Данные>)

Если при запуске программы используется флаг -о,то переменная__debug__будет иметь

ложное значение. Так можно удалить все инструкции assert из байт-кода.
Пример использования инструкции assert представлен в листинге 14.15.

Листинг 14.15. Использование инструкции assert

try:
х = -3
assert х >= 0, "Сообщение об ошибке"

except AssertionError as err:
print(err) # Выведет: Сообщение об ошибке

ГЛАВА 15

Итераторы, контейнеры
и перечисления

Язык Python поддерживает средства для создания классов особого назначения: итераторов,
контейнеров и перечислений.

♦ Итераторы — это классы, генерирующие последовательности каких-либо значений.
Такие классы мы можем задействовать, например, в циклах for:

class Mylterator: # Определяем класс-итератор

it = Mylterator() # Создаем его экземпляр
for v in it: # и используем в цикле for

♦ Контейнеры — классы, которые могут выступать как последовательности (списки или
кортежи) или отображения (словари). Мы можем обратиться к любому элементу экземп-

ляра такого класса через его индекс или ключ:

class MyList: # Определяем класс-список

class MyDict: # Определяем класс-словарь

1st, dct = MyList О,MyDict () # Используем их
1st[0] = 1
dct["first"] = 578
print(1st[1]), print(dct["second"])

♦ Перечисления — особые классы, представляющие наборы каких-либо именованных
величин. В этом смысле они аналогичны подобным типам данных, доступным в других
языках программирования, например в С:

from enum import Enum # Импортируем базовый класс Enum

class Versions(Enum): # Определяем класс-перечисление

Python2.7 = "2.7"

Python3.6 = "3.6"

# Используем его

if python_version == Versions.Python3.6:

278 Гпава 15

15.1. Итераторы

Для того чтобы превратить класс в итератор, нам следует переопределить в нем два специ-
альных метода:

♦ __ i t e r __ ( s e lf ) — говорит о том, что этот класс является итератором (поддерживает
итерационный протокол, как говорят Python-программисты). Он должен возвращать сам
экземпляр этого класса, а также при необходимости может выполнять всевозможные
предустановки.

Если в классе одновременно определены методы __ i t e r __ о и ___g e tite m __о (о нем
будет рассказано позже), предпочтение отдается первому методу;

♦ __ n e x t__( s e lf ) — вызывается при выполнении каждой итерации и должен возвращать
очередное значение из последовательности. Если последовательность закончилась,
в этом методе следует возбудить исключение s to p it e r a t io n , которое сообщит вызы-
вающему коду об окончании итераций.

Для примера рассмотрим класс, хранящий строку и на каждой итерации возвращающий
очередной ее символ, начиная с конца (листинг 15.1).

Листинг 15.1. Класс-итератор

class Reversestring:
def __init__ (self, s):
self.__s = s
def __iter__(self):
self.__i = 0
return self
def __next_(self):
if self.__i > len(self.__s) - 1:
raise Stopiteration
else:
a = self.__s[-self.__i - 1]
self.__i = self.__ i + 1
return a

Проверим его в действии:

» > s = Reversestring ("Python")
» > for a in s: print (a, end="")
nohtyP

Результат вполне ожидаем — строка, выведенная задом наперед.

Также мы можем переопределить специальный метод__1еп()__, который вернет количест-
во элементов в последовательности, и, разумеется, специальные методы _str()__ и
_герг ()__, возвращающие строковое представление итератора (все эти методы были рас-
смотрены в главе 13).

Перепишем код нашего класса-итератора, добавив в него определение методов__len о __и
_str ()__(листинг 15.2 — часть кода опущена).

Итераторы, контейнеры и перечисления 279

Листинг 15.2. Расширенный класс-итератор
c la ss R eversestring:

def len ( s e lf ) :
r e tu r n l e n ( s e l f . __ s)

def str (self) :
r e tu r n s e l f . __ s [ : : —1]

Теперь мы можем получить длину последовательности, хранящейся в экземпляре класса
R e v e r se str in g , и его строковое представление:

» > s = R ev ersestrin g ("Python")
» > p rin t (len ( s ) )
6
> » print (str (s ))
nohtyP

15.2. Контейнеры

Python позволяет создать как контейнеры-последовательности, аналогичные спискам и кор-
тежам, так и контейнеры-отображения, т. е. словари. Сейчас мы узнаем, как это делается.

15.2.1. Контейнеры-последовательности

Чтобы класс смог реализовать функциональность последовательности, нам следует переоп-
ределить в нем следующие специальные методы:

♦ __getitem__(self, <Индекс>) — вызывается при извлечении элемента последовательно-
сти по его индексу с помощью операции «Экземпляр класса>[«Индекс>]. Метод должен
возвращать значение, расположенное по этому индексу. Если индекс не является целым
числом или срезом, должно возбуждаться исключение TypeError, а если индекса как
такового не существует, следует возбудить исключение indexError;

♦ __setitem__(self, <индекс>, <значение>)— вызывается в случае присваивания нового
значения элементу последовательности с заданным индексом (операция «Экземпляр
класса> [<Индекс>] = «Новое значение>). Метод не должен возвращать результата.
В случае задания индекса недопустимого типа и отсутствия такого индекса в последова-
тельности следует возбуждать те же исключения, что и в случае метода__getitem__();

♦ __deiitem__(self, <кгаоч>) — вызывается в случае удаления элемента последовательно-
сти с заданным индексом с помощью выражения del «Экземпляр кпасса>[«Ключ>]. Ме-
тод не должен возвращать результата. В случае задания индекса недопустимого типа и
отсутствия такого индекса в последовательности следует возбуждать те же исключения,
что и в случае метода_getitem__();

♦ __contains__(self, «Значение;-) — вызывается при проверке существования заданного
значения в последовательности с применением операторов in и not in. Метод должен
возвращать True, если такое значение есть, и False — в противном случае.

В классе-последовательности мы можем дополнительно реализовать функциональность
итератора (см. разд. 15.1), переопределив специальные методы_iter__(), __ next_() и
__len (). Чаще всего так и поступают.

280 Гпава 15

Мы уже давно знаем, что строки в Python являются неизменяемыми. Давайте же напишем
класс Mutabiestring, представляющий строку, которую можно изменять теми же способа-
ми, что и список (листинг 15.3).

Листинг 15.3. Класс M utabiestring

c la s s M utabiestring:
d e f __ i n i t __ ( s e l f , s ) :
s e l f . __ s = l i s t ( s )

# Реализуем функциональность итератора
d e f __ i t e r ___( s e l f ) :

s e l f . __ i = 0
return s e lf
d e f __ n e x t__ ( s e l f ) :
i f s e l f . __i > l e n ( s e l f . __ s) - 1:

raise Stoplteration
e ls e :

a = s e l f . __s [ s e l f . __ i ]
s e l f . __ i = s e l f . __ i + 1
return a
d e f __ le n ___( s e l f ) :
r e tu r n l e n f s e l f . __ s)

d e f __ s t r __ ( s e l f ) :
r e tu r n " " .j o i n ( s e l f . __ s)

# Определяем вспомогательный метод, который будет проверять
# корректность индекса
d e f __ i s c o r r e c t i n d e x ( s e l f , i ) :

i f typ e(i) = in t or typ e(i) = s lic e :
i f t y p e ( i) == i n t and i > s e l f . __le n __ () - 1:
r a ise IndexError

e ls e :
r a is e TypeError

# Реализуем функциональность контейнера-списка
d e f __ q e tite m ( s e l f , i ) :

s e l f . __ is c o r r e c t i n d e x ( i )
r e tu r n s e l f . __ s [ i ]
d e f __ s e t it e m ( s e l f , i , v) :
s e l f . __ i s c o r r e c t i n d e x ( i)
s e l f . __ s [ i ] = v
d e f __ d e lite m ( s e l f , i ) :
s e l f . __ i s c o r r e c t i n d e x (i )
d e l s e l f . __ s [ i ]
d e f __c o n ta in s __ ( s e l f , v ) :
return v in s e lf , s

Итераторы, контейнеры и перечисления 281

Проверим свеженаписанный класс в действии:

> » s = MutableString("Python")
> » print (s [-1])
n
» > s [0] = "J"
> » print (s)
Jython
» > del s[2:4]
> » print (s)
Juon

Теперь проверим, как наш класс обрабатывает нештатные ситуации. Введем вот такой код,
обращающийся к элементу с несуществующим индексом:

» > s [9] = "и"

В ответ интерпретатор Python выдаст вполне ожидаемое сообщение об ошибке:

Traceback (most recent call last):
File "<pyshell#12>", line 1, in <module>
s [9] = "u"
File "D:/ОаЬа/Документы/Работа/Книпи/РуЬЬоп 3. Самое необходимое

11/Примеры/15/15.3.ру", line 36, in __setitem
self.__iscorrectindex(i)

File "D:/Data/floKyMeHTbi/Pa6oTa/KHM:™/Python 3. Самое необходимое
11/Примеры/15/15.3.ру", line 27, in __iscorrectindex

raise IndexError
IndexError

15.2.2. Контейнеры-словари

Класс, реализующий функциональность перечисления, должен переопределять уже знако-
мые нам методы:__getitem__(),__ setitem (),__delitem__() и __ contains__().Разумеет-
ся, при этом следует сделать поправку на то, что вместо индексов здесь будут использо-
ваться ключи произвольного типа (как правило, строкового).

Давайте исключительно для практики напишем класс version, который будет хранить но-
мер версии интерпретатора Python, разбитый на части: старшая цифра, младшая цифра и
подрелиз, при этом доступ к частям номера версии будет осуществляться по строковым
ключам, как в обычном словаре Python (листинг 15.4). Ради простоты чтения кода функ-
циональность итератора реализовывать не станем, а также заблокируем операцию удаления
элемента словаря, возбудив в методе_delitem_() исключение ТуреЕггог.

Листинг 15.4. Класс Version

class Version: sub):
def init (self, major, minor,
self, major = major # Старшая цифра
self._minor = minor # Младшая цифра
self._sub = sub # Подверсия
def str (self):
return str(self. major) + + str(self. minor) + + strfself. sub)

282 Глава 15

# Реализуем функциональность словаря "sub"
def __qetitem (self, k):

if k == "major":
return self, major

elif k = "minor":
return self.__minor

elif k = "sub":
return self.__sub

else:
raise IndexError

def setitem (self, k, v ) :
if k == "major":
self, major = v
elif k = "minor":
self.__minor = v
elif k = "sub":
self.__sub = v
else:
raise IndexError

def delitem (self, k) :
raise TypeError

def __contains__(self, v ) :
return v = "major" or v == "minor" or v =

Чтобы наш новый класс не бездельничал, дадим ему работу, введя такой код:

» > v = Version (3, б, 4)
» > print (vf'major"])
3
» > v["sub"] = 5
» > print (str (v))
3.6.5

Как видим, все работает как надо.

15.3. Перечисления

Перечисление — это определенный самим программистом набор каких-либо именованных
значений. Обычно они применяются для того, чтобы дать понятные имена каким-либо зна-
чениям, используемым в коде программы, — например, кодам ошибок, возвращаемых
функциями Windows API.

Для создания перечислений применяются два класса, определенные в модуле enum:

♦ Enum — базовый класс для создания классов-перечислений, чьи элементы могут хранить
значения произвольного типа.

Для примера определим класс-перечисление versions, имеющий два элемента: V2_7 со
значением "2.7" и V3_6 со значением "3.6" (листинг 15.5). Отметим, что элементы пере-
числений представляют собой атрибуты объекта класса.

Итераторы, контейнеры и перечисления 283

Листинг 18.5. Перечисление с элементами произвольного типа

from enum inport Enum
class Versions(Enum):

V2_7 = "2.7"
V3_6 = "3.6"

♦ intEnum— базовый класс для создания перечислений, способных хранить лишь цело-
численные значения.
Листинг 15.6 представляет код перечисления Colors с тремя элементами, хранящими
целые числа.

Листинг 15.8. Перечисление с целочисленными элементами

from enum inport IntEnum
class Colors(IntEnum):

Red = 1
Green = 2
Blue = 3

Имена элементов перечислений должны быть уникальны (что и неудивительно— ведь
фактически это атрибуты объекта класса). Однако разные элементы все же могут хранить
одинаковые значения (листинг 15.7).

Листинг 15.7. Перечисление с элементами, хранящими одинаковые значения

from enum inport Enum
class Versions(Enum):

V2_7 = "2.7"
V3_6 = "3.6"
MostFresh = "3.6"

Чтобы объявить, что наше перечисление может хранить лишь уникальные значения, мы
можем использовать декоратор unique, также определенный в модуле enum (листинг 15.8).

Листинг 15.8. Использование декоратора unique

from enum inport Enum, unique
0unique
class Versions(Enum):

V2_7 = "2.7"
V3_6 = "3.6"

Если мы попытаемся определить в классе, для которого был указан декоратор unique,
элементы с одинаковыми значениями, то получим сообщение об ошибке.
Определив перечисление, можно использовать его элементы в вычислениях:

» > е = Versions.V3_6
»> е
■«Versions.V3 6: '3.6'>

284 Глава 15

» > е.value
'3.6'
» > е == Versions.V2_7
False

Отметим, что для этого нам не придется создавать экземпляр класса. Это сделает сам
Python, неявно создав экземпляр с тем же именем, что мы дали классу (вся необходимая для
этого функциональность определена в базовых классах перечислений Enum и intEnum).

Все классы перечислений принадлежат типу EnumMeta из модуля enum:

» > type (Colors)
cclass 'enum.EnumMeta'>
» > from enum import EnumMeta
» > type (Colors) == EnumMeta
True

Однако элементы перечислений уже являются экземплярами их классов:

» > type (Colors.Red) = Colors
<enum 'Colors'>
» > type (Colors.Red)
True

Над элементами перечислений можно производить следующие операции:

4 обращаться к ним по их именам, использовав знакомую нам запись с точкой:

> » Vers ions. V3_6
<Versions.V3_6: '3.6'>
» > e = Vers ions. V3_6
>» e
<Versions.V3_6: '3.6'>

4 обращаться к ним в стиле словарей, использовав в качестве ключа имя элемента:

» > Versions ["V3_6" ]
<Versions.V3_6: '3.6’>

4 обращаться к ним по их значениям, указав их в круглых скобках после имени класса
перечисления:

» > Versions ("3.6")
<Versions.V3_6: '3.6'>

4 получать имена соответствующих им атрибутов класса и их значения, воспользовавшись
свойствами name и value соответственно:

» > Vers ions. V2_7 .name, Versions.V2_7 .value
('V2_7', '2.7')

4 использовать в качестве итератора (необходимая для этого функциональность определе-
на в базовых классах):

» > list (Colors)
[cColors.Red: 1>, cColors.Green: 2>, <Colors.Blue: 3>]
» > for c in Colors: print (c.value, end = " ")
12 3

Итераторы, контейнеры и перечисления 285

♦ использовать в выражениях с применением операторов равенства, неравенства, in
и not in:

» > е = Versions.V3_6
> » е == Versions.V3_6
True
» > e != Versions ,V2_7
True
» > e in Versions
True
» > e in Colors
False

Отметим, что элементы разных перечислений всегда не равны друг другу, даже если
хранят одинаковые значения;

♦ использовать элементы перечислений — подклассов intEnum в арифметических выраже-
ниях и в качестве индексов перечислений. В этом случае они будут автоматически пре-
образовываться в целые числа, соответствующие их значениям:

» > Colors.Red + 1 # Значение Colors.Red - 1
2 # Значение Colors.Green - 2

» > Colors.Green != 3
True
» > ["a", "b'\ "c"] [Colors.Red]
'b'

Помимо элементов, классы перечислений могут включать атрибуты экземпляра класса и
методы — как экземпляров, так и объектов класса. При этом методы экземпляра класса все-
гда вызываются у элемента перечисления (и, соответственно, первым параметром ему пере-
дается ссылка на экземпляр класса, представляющий элемент перечисления, у которого был
вызван метод), а методы объекта класса— у самого класса перечисления. Для примера

давайте рассмотрим код класса перечисления VersionExtended (листинг 15.9).

Листинг 15.9. Перечисление, включающее атрибуты и методы

from enum import Enum
class VersionExtended(Enum):

V2_7 = "2.7"
V3_6 = "3.6"

# Методы экземпляра клксса. + self.name + ": " + self.value
# Вызываются у элемента перечисления
def describe(self) :

return'-self.name, self.value
def __str__(self):

return str(__class__.__ name__) +

# Метод объекта класса.
# Вызывается у самого класса перечисления.
0classmethod
def getmostfresh(els):

return cls.V3 6

286 Гпава 15

В методе__s t r _() мы использовали встроенную переменную__ c la s s _, хранящую ссылку
на объект текущего класса. Атрибут_паше_этого объекта содержит имя класса в виде
строки.
Осталось лишь проверить готовый класс в действии, для чего мы введем следующий код:

» > d = V ersio n E x ten d ed .V 2 _ 7 . d e s c r ib e ()
» > print (d[0] + ", " + d [1])
V2_7, 2 .7
» > p rin t (VersionExtended.V2_7)
V ersionExtended.V 2_7: 2.7
» > p rin t (V ersionExtended.getm ostfreshO )
VersionExtended.V3_6: 3 .6

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

» > c la s s ExtendedColors (Colors) :
pass

Traceback (most recent call last):
F ile "<pyshell#44>", li n e 1, in <module>
c la s s E xtendedC olors(C olors):

NameError: name 'C o lo r s ' i s n o t d e fin e d

Пр и м е ч а н и е
В составе стандартной библиотеки Python уже давно присутствует модуль struct, позво-

ляющий создавать нечто похожее на перечисления. Однако он не столь удобен в работе,
как инструменты, предлагаемые модулем enum.

Г Л А В А 16
Работа с файлами и каталогами

Очень часто нужно сохранить какие-либо данные. Если эти данные имеют небольшой
объем, их можно записать в файл.

16.1. Открытие файла

Прежде чем работать с файлом, необходимо создать объект файла с помощью функции
open (). Функция имеет следующий формат:

ореп(<Путь кф ай лу>[, mode=' г ' ] [ , b u f f e r in g = - l] [, encoding=N one][, errors=N one] [ ,
new line=N one][, closefd = T ru e])

В первом параметре указывается путь к файлу. Путь может быть абсолютным или относи-
тельным. При указании абсолютного пути в Windows следует учитывать, что в Python слэш
является специальным символом. По этой причине слэш необходимо удваивать или вместо

обычных строк использовать неформатированные строки:

» > "C :\\tem p\\n ew W file.tx t" # Правильно
'С: WtempWnewWfi l e . t x t ' # Правильно
» > r"C:\te m p \n e w \file .tx t" # Неправильно!!!
'С: WtempWnewWfi l e . t x t '
» > "C :\tem p\new \file.txt"
'C :\tem p \n ew \xO cile.txt1

Обратите внимание на последний пример. В этом пути из-за того, что слэши не удвоены,

возникло присутствие сразу трех специальных символов: \ t , \п и \ f (отображается как
\хОс). После преобразования этих специальных символов путь будет выглядеть следующим
образом:

С:<Табуляция>етр<Перевод строки>еы<Перевод формата^l e . t x t

Если такую строку передать в функцию open (), это приведет к исключению osError:

» > open("C :\tem p\new \file.txt")
Traceback (most r e c e n t c a l l l a s t ) :

F ile " <pyshell#0>", li n e 1, in <module>
o p e n ("C:\te m p \n e w \fi l e . t x t " )

OSError: [Errno 22] In v a lid argument: 'C :\te m p \n e w \x O c ile .tx t'

288 Гпава 16

Вместо абсолютного пути к файлу можно указать относительный путь, который определя-
ется с учетом местоположения текущего рабочего каталога. Относительный путь будет авто-
матически преобразован в абсолютный путь с помощью функции abspath () из модуля os .path.
Возможны следующие варианты:
♦ если открываемый файл находится в текущем рабочем каталоге, можно указать только

имя файла:

» > import os.path # Подключаем модуль
» > # Файл в текущем рабочем каталоге (C:\book\)
» > o s .p a t h .a b s p a t h (г "f i l e .t x t ")
'C: W b o o k W f i l e . t x t '

4 если открываемый файл расположен во вложенном каталоге, перед именем файла через
слэш указываются имена вложенных каталогов:

» > # Открываемый файл в C:\book\folderl\
» > o s .p a t h .a b s p a t h (r " f o l d e r 1 / f i l e .t x t " )
'C :W b o o k W f o l d e r l W f i l e .t x t '
» > # Открываемый файл в C:\book\folderl\folder2\
» > os . p a th.abspath (r"folder l / f o l d e r 2 / f i l e . t x t " )
'C :W b o o k \ \ f o l d e r l \ \ f o l d e r 2 \ \ f i l e .t x t '

4 если каталог с файлом расположен ниже уровнем, перед именем файла указываются две
точки и слэш ( ".. /"):

> » # Открываемый файл в С:\
» > os.path. a b s p a t h (г", ./file.txt")

'С: W f i l e . t x t '

♦ если в начале пути расположен слэш, путь отсчитывается от корня диска. В этом случае
местоположение текущего рабочего каталога не имеет значения:

» > # Открываемый файл в C:\book\folderl\
> » o s .p a t h .a b s p a t h (г" /book/ f older 1/f i l e .t x t ")
'C :W b o o k W f o l d e r l W f i l e .t x t '
» > # Открываемый файл в C:\book\folderl\folder2\
» > o s .p a t h .a b s p a t h (r "/ b o o k / f o l d e r 1/ f o l d e r 2 / f i l e .t x t ")
'C: W b o o k W f o l d e r l W f o l d e r 2 W f i l e . t x t '

Как можно видеть, в абсолютном и относительном путях можно указать как прямые, так и
обратные слэши. Все они будут автоматически преобразованы с учетом значения атрибута
sep из модуля os.path. Значение этого атрибута зависит от используемой операционной
системы. Выведем значение атрибута sep в операционной системе Windows:

» > os.path.sep

'W

» > o s .p a t h .a b s p a t h (r " C :/ b o o k / f o l d e r 1/ fi l e .t x t ")
'C :W b o o k W f o l d e r l W f i l e .t x t '

При использовании относительного пути необходимо учитывать местоположение текущего
рабочего каталога, т. к. рабочий каталог не всегда совпадает с каталогом, в котором нахо-
дится исполняемый файл. Если файл запускается с помощью двойного щелчка на его знач-
ке, то каталоги будут совпадать. Если же файл запускается из командной строки, то теку-
щим рабочим каталогом будет каталог, из которого запускается файл.

Работа с файлами и каталогами 289

Рассмотрим все это на примере, для чего в каталоге C:\book создадим следующую структуру
файлов и каталогов:

С :\book\
t e s t .ру
folderl\
__ init__ .ру
module1.ру

Содержимое файла C:\book\test.py приведено в листинге 16.1.

Листинг 16.1. Содержимое файла C:\bookVtestpy

# coding: utf-8
import os, sys
print("%-25s%s" % ("Файл:", os.path.abspath(__file__ )))
print ("%-25s%s" % ("Текущий рабочий каталог:", os.getcwdO ))
print("%-25s%s" % ("Каталог для импорта:", sys.path[0]))
print("%-25s%s" % ("Путь к файлу:", os.path.abspath("file.txt")))
print("-" * 40)
import folderl.modulel as m
m.get_cwd()

Файл C:\book\folder1\__init__.ру создаем пустым. Как вы уже знаете, этот файл указывает
интерпретатору Python, что каталог, в котором он находится, является пакетом с модулями.
Содержимое файла C:\book\folder1\module1.py приведено в листинге 16.2.

Листинг 16.2. Содержимое файла C:\book\foldeii\module1.py

# coding: utf-8
import os, sys
def get_cwd():

print("%-25s%s" % ("Файл:", os.path.abspathf__file__)))
print ("%-25s%s" % ("Текущий рабочий каталог:", os.getcwdO))
print("%-25s%s" % ("Каталог для импорта:", sys.path[0]))
print ("%-25s%s" % ("Путь к файлу:", os.path.abspathCfile.txt")))

Запускаем командную строку, переходим в каталог C:\book и запускаем файл test.py:

С:\>cd C:\book

С : \ b o o k > t e s t .ру

Файл: С:\ b o o k \te s t.ру

Текущий рабочийкаталог: С:\book

Каталог для импорта: С:\book

Путь к файлу: C :\book\file.txt

Файл: C:\book\folderl\modulel.py

Текущий рабочийкаталог: C:\book

Каталог для импорта: С :\book

Путь к файлу: C:\book\file.txt

290 Гпава 16

В этом примере текущий рабочий каталог совпадает с каталогом, в котором расположен
файл test.py. Однако обратите внимание на текущий рабочий каталог внутри модуля
modulel .ру — если внутри этого модуля в функции open () указать имя файла без пути, по-
иск файла будет произведен в каталоге C:\book, а не C:\book\folder1.

Теперь перейдем в корень диска С: и опять запустим файл test.py:

C:\book>cd С:\

С :\>С:\book\test.ру

Файл: C:\book\test.py

Текущий рабочийкаталог: С:\

Каталог для импорта: C:\book

Путь к файлу: C:\file.txt

Файл: C:\book\folderl\modulel.py

Текущий рабочийкаталог: С :\

Каталог для импорта: C:\book

Путь к файлу: C:\file.txt

В этом случае текущий рабочий каталог не совпадает с каталогом, в котором расположен
файл test.py. Если внутри файлов test.py и modulel .ру в функции open () указать имя файла
без пути, поиск файла будет производиться в корне диска С:, а не в каталогах с этими фай-
лами.

Чтобы поиск файла всегда производился в каталоге с исполняемым файлом, необходимо
этот каталог сделать текущим с помощью функции chdir () из модуля os. Для примера соз-
дадим файл test2.py (листинг 16.3).

Листинг 16.3. Пример использования функции ch d ir ()

# coding: utf-8 -*-
import os, sys
# Делаем каталог с исполняемым файлом текущим
os.chdir(os.path.dirname(os.path.abspath(__file )))
print("%-25s%s" % ("Файл:", __file__))
print("%-25s%s" % ("Текущий рабочий каталог:”, os .getcwd()))
print("%-25s%s" % ("Каталог для импорта:", sys.path[0]))
print ("%-25s%s" % ("Путь к файлу:", os.path.abspathffile.txt")))

Обратите внимание на четвертую строку. С помощью атрибута__file__мы получаем путь
к исполняемому файлу вместе с именем файла. Атрибут_file__не всегда содержит пол-
ный путь к файлу. Например, если запуск осуществляется следующим образом:

С:\book>C:\Python36\python test2.py

то атрибут будет содержать только имя файла без пути. Чтобы всегда получать полный путь
к файлу, следует передать значение атрибута в функцию abspath () из модуля os .path. Далее
мы извлекаем путь (без имени файла) с помощью функции dirname () и передаем его функ-
ции chdir о. Теперь, если в функции open о указать название файла без пути, поиск будет
производиться в каталоге с этим файлом. Запустим файл test2.py с помощью командной
строки:

Работа с файлами и каталогами 291

С :\>С:\book\test2.ру

Файл: C:\book\test2.py

Текущий рабочийкаталог: C:\book

Каталог для импорта: C:\book

Путь к файлу: C:\book\file.txt

Функции, предназначенные для работы с каталогами, мы еще рассмотрим подробно в сле-
дующих разделах. Сейчас же важно запомнить, что текущим рабочим каталогом будет
каталог, из которого запускается файл, а не каталог, в котором расположен исполняе-

мый файл. Кроме того, пути поиска файлов не имеют никакого отношения к путям поиска
модулей.

Необязательный параметр mode в функции open () может принимать следующие значения:

♦ г — только чтение (значение по умолчанию). После открытия файла указатель устанав-
ливается на начало файла. Если файл не существует, возбуждается исключение

FileNotFoundError;

♦ г+ — чтение и запись. После открытия файла указатель устанавливается на начало фай-
ла. Если файл не существует, то возбуждается исключение FileNotFoundError;

♦ w — запись. Если файл не существует, он будет создан. Если файл существует, он будет
перезаписан. После открытия файла указатель устанавливается на начало файла;

♦ w+— чтение и запись. Если файл не существует, он будет создан. Если файл существует,
он будет перезаписан. После открытия файла указатель устанавливается на начало файла;

♦ а — запись. Если файл не существует, он будет создан. Запись осуществляется в конец
файла. Содержимое файла не удаляется;

♦ а+ — чтение и запись. Если файл не существует, он будет создан. Запись осуществляется
в конец файла. Содержимое файла не удаляется;

♦ х — создание файла для записи. Если файл уже существует, возбуждается исключение

FileExistsError;

♦ х+ — создание файла для чтения и записи. Если файл уже существует, возбуждается ис-
ключение FileExistsError.

После указания режима может следовать модификатор:

♦ b — файл будет открыт в бинарном режиме. Файловые методы принимают и возвраща-
ют объекты типа bytes;

♦ t — файл будет открыт в текстовом режиме (значение по умолчанию в Windows). Фай-
ловые методы принимают и возвращают объекты типа str. В этом режиме будет автома-
тически выполняться обработка символа конца строки — так, в Windows при чтении
вместо символов \ г \ п будет подставлен символ \п. Для примера создадим файл file.txt
и запишем в него две строки:

» > f = open(r"file.txt", "w") # Открываем файл на запись

» > f .write ("Stringl\nString2") # Записываем две строки в файл

15

» > f. close () # Закрываем файл

Поскольку мы указали режим w, то, если файл не существует, он будет создан, а если
существует, то будет перезаписан.

292 Глава 16

Теперь выведем содержимое файла в бинарном и текстовом режимах:

» > # Бинарный режим (символ \г остается)
» > with open(r"file.txt", "rb") as f:

for line in f:
print(repr(line))

b'Stringl\r\n'

b'String2'

» > # Текстовый режим (символ \r удаляется)

» > with open(r"file.txt", "r") as f:

for line in f:

print(repr(line))

'StringlNn'

'String2'

Для ускорения работы производится буферизация записываемых данных. Информация из
буфера записывается в файл полностью только в момент закрытия файла или после вызова
функции или метода flush о . В необязательном параметре buffering можно указать размер
буфера. Если в качестве значения указан о, то данные будут сразу записываться в файл
(значение допустимо только в бинарном режиме). Значение 1 используется при построчной
записи в файл (значение допустимо только в текстовом режиме), другое положительное
число задает примерный размер буфера, а отрицательное значение (или отсутствие значе-
ния) означает установку размера, применяемого в системе по умолчанию. По умолчанию
текстовые файлы буферизуются построчно, а бинарные — частями, размер которых интер-
претатор выбирает самостоятельно в диапазоне от 4096 до 8192 байтов.

При использовании текстового режима (задается по умолчанию) при чтении производится
попытка преобразовать данные в кодировку Unicode, а при записи выполняется обратная
операция — строка преобразуется в последовательность байтов в определенной кодировке.
По умолчанию назначается кодировка, применяемая в системе. Если преобразование невоз-
можно, возбуждается исключение. Указать кодировку, которая будет использоваться при
записи и чтении файла, позволяет параметр encoding. Для примера запишем данные в коди-
ровке UTF-8:

> » f = open(r"file.txt", "w", encoding="utf-8")

» > f.write("Строка") # Записываем строку в файл

6

» > f. close () # Закрываем файл

Для чтения этого файла следует явно указать кодировку при открытии файла:

» > with open(r"file.txt", "г", encoding="utf-8") as f:

for line in f:

print(line)

Строка

При работе с файлами в кодировках UTF-8, UTF-16 и UTF-32 следует учитывать, что в на-
чале файла могут присутствовать служебные символы, называемые сокращенно BOM (Byte
Order Mark, метка порядка байтов). Для кодировки UTF-8 эти символы являются необяза-
тельными, и в предыдущем примере они не были добавлены в файл при записи. Чтобы
символы ВОМ были добавлены, в параметре encoding следует указать значение utf-8-sig.
Запишем строку в файл в кодировке UTF-8 с ВОМ:

Работа с файлами и каталогами 293

» > f = open(r"file.txt", "w", encoding="utf-8-sig”)

» > f .write ("Строка") # Записываем строку в файл

6

» > f. close () # Закрываем файл

Теперь прочитаем файл с разными значениями в параметре encoding:

» > with open(r"file.txt", "г", encoding="utf-8") as f:
for line in f:
print(repr(line))

’XufeffOrpoKa'
» > with open(r"file.txt", "r", encoding="utf-8-sig") as f:

for line in f:
print(repr(line))

1Строка'

В первом примере мы указали значение utf-8, поэтому маркер ВОМ был прочитан из файла
вместе с данными. Во втором примере указано значение utf-8-sig, поэтому маркер ВОМ не
попал в результат. Если неизвестно, есть ли маркер в файле, и необходимо получить данные
без маркера, то следует всегда указывать значение utf-8-sig при чтении файла в кодировке
UTF-8.

Для кодировок UTF-16 и UTF-32 маркер ВОМ является обязательным. При указании значе-
ний utf-l6 и utf-32 в параметре encoding обработка маркера производится автоматически:
при записи данных маркер автоматически вставляется в начало файла, а при чтении он не
попадает в результат. Запишем строку в файл, а затем прочитаем ее из файла:

» > with open(r"file.txt", "w", encoding="utf-16") as f:

f.write("Строка")

6
» > with open(r"file.txt", "r", encoding="utf-16") as f:

for line in f:
print(repr(line))

'Строка1

При использовании значений utf-16-le, utf-16-be, utf-32-le и utf-32-be маркер ВОМ
необходимо самим добавить в начало файла, а при чтении удалить его.

В параметре errors можно указать уровень обработки ошибок. Возможные значения:
"strict" (при ошибке возбуждается исключение vaiueError— значение по умолчанию),
"replace" (неизвестный символ заменяется символом вопроса или символом с кодом
\ufffd), "ignore" (неизвестные символы игнорируются), "xmlcharrefreplace" (неизвестный
символ заменяется последовательностью &#хххх;) и "backsiashrepiace" (неизвестный сим-
вол заменяется последовательностью \uxxxx).

Параметр newline задает режим обработки символов конца строк. Поддерживаемые им зна-
чения таковы:

♦ None (значение по умолчанию) — выполняется стандартная обработка символов конца
строки. Например, в Windows при чтении символы \г\п преобразуются в символ \п,
а при записи производится обратное преобразование;

294 Глава 16

♦ "" (пустая строка) — обработка символов конца строки не выполняется;
♦ "сспециальный симвсш>" — указанный специальный символ используется для обозначе-

ния конца строки, и никакая дополнительная обработка не выполняется. В качестве спе-
циального символа можно указать лишь \г\п, \г и \п.

16.2. Методы для работы с файлами

После открытия файла функция open () возвращает объект, с помощью которого произво-
дится дальнейшая работа с файлом. Тип объекта зависит от режима открытия файла и
буферизации. Рассмотрим основные методы:

♦ c lo s e () — закрывает файл. Так как интерпретатор автоматически удаляет объект, когда
на него отсутствуют ссылки, в небольших программах файл можно не закрывать явно.
Тем не менее, явное закрытие файла является признаком хорошего стиля программи-
рования. Кроме того, при наличии незакрытого файла генерируется предупреждающее
сообщение: "R e s o u rc ew a rn in g : u n c lo s e d f i l e " .

Язык Python поддерживает протокол менеджеров контекста. Этот протокол гарантирует
закрытие файла вне зависимости от того, произошло исключение внутри блока кода или
нет:

with open(r"file.txt", "w", encoding="cpl251") as f:

f.write("Строка") # Записываем строку в файл

# Здесь файл уже закрыт автоматически

♦ w r i t e (< д а н н ы е > ) — записывает данные в файл. Если в качестве параметра указана стро-
ка, файл должен быть открыт в текстовом режиме, а если указана последовательность
байтов — в бинарном. Помните, что нельзя записывать строку в бинарном режиме и по-
следовательность байтов в текстовом режиме. Метод возвращает количество записанных
символов или байтов. Вот пример записи в файл:

» > # Текстовый режим

> » f = open(r"file.txt", "w", encoding="cpl251")

> » f .write ("Строка1\пСтрока2") # Записываем строку в файл

15

» > f. close () # Закрываем файл

» > # Бинарный режим

» > f = open(r"file.txt", "wb")

» > f .write (bytes ("Строка1\пСтрока2", "cpl251"))

15

» > f.w r ite (b y te a r r a y (" \n C T p o K a 3 " , " c p l2 5 1 " ))

8

» > f.closet)

♦ writelines (Последовательность;*) — записывает последовательность в файл. Если все
элементы последовательности являются строками, файл должен быть открыт в тексто-
вом режиме. Если все элементы являются последовательностями байтов, то файл должен

быть открыт в бинарном режиме. Вот пример записи элементов списка:

» > # Текстовый режим
» > f = open(r"file.txt", "w", encoding="cpl251")
> » f .writelines (["Строка1\п”, "Строка2"])

Работа с файлами и каталогами 295

» > f.close ()
» > # Бинарный режим
» > f = open(r"file.txt", Mwb")
» > arr = [bytes("Строка1\п”, "cpl251")/ bytes ("Строка2", "cpl251")]
» > f.writelines (arr)
» > f.closeO

♦ w r it a b le () — возвращает T ru e, если файл поддерживает запись, и F a ls e — в противном
случае:

» > f = open(r"file.txt", "г") # Открываем файл для чтения
» > f.writable () "w") # Открываем файл для записи
False
» > f = open(r"file.txt",
» > f.writable ()
True

♦ read( [<Количество>]) — считывает данные из файла. Если файл открыт в текстовом
режиме, возвращается строка, а если в бинарном— последовательность байтов. Если

параметр не указан, возвращается содержимое файла от текущей позиции указателя до
конца файла:

» > # Текстовый режим
» > with open(r"file.txt", "г", encoding="cpl251") as f:

f .read()

'Строка1\пСтрока2' as f:
» > # Бинарный режим
» > with open(r"file.txt", "rb")

f .read()

b ' \xdl\xf2\xf0\xee\xea\xe01\n\xdl\xf2\xf0\xee\xea\xe02'

Если в качестве параметра указать число, то за каждый вызов будет возвращаться ука-
занное количество символов или байтов. Когда достигается конец файла, метод возвра-

щает пустую строку:

» > # Текстовый режим "г", encoding="cpl251")
» > f = open(r"file.txt", # Считываем 8 символов
» > f.read(8)
'Строка1\п' # Считываем 8 символов
» > f.read(8)
'Строка21 # Достигнут конец файла
» > f.read(8)

II

» > f.closeO

♦ readline ( [<Количество>]) — считывает из файла одну строку при каждом вызове. Если
файл открыт в текстовом режиме, возвращается строка, а если в бинарном — последова-
тельность байтов. Возвращаемая строка включает символ перевода строки. Исключени-
ем является последняя строка — если она не завершается символом перевода строки, то
таковой добавлен не будет. При достижении конца файла возвращается пустая строка:

296 Гпава 16

» > # Текстовый режим

» > f = open (r"file.txt", "r", encoding="cpl251")

» > f. readline (), f. readline ()

('Строка1\п', 'Строка2')

> » f. readline () # Достигнут конец файла

II

» > f. close ()

» > # Бинарный режим

» > f = operi(r"file.txt", "rb")

» > f. readline (), f.readlineO

(b1\xdl\xf2\xf0\xee\xea\xe01\n', b'\xdl\xf2\xf0\xee\xea\xe02')

» > f.readlineO # Достигнут конец файла

b'

» > f. close ()

Если в необязательном параметре указано число, считывание будет выполняться до тех

пор, пока не встретится символ новой строки (\п), символ конца файла или из файла не
будет прочитано указанное количество символов. Иными словами, если количество сим-
волов в строке меньше значения параметра, то будет считана одна строка, а не указанное
количество символов, а если количество символов в строке больше, то возвращается
указанное количество символов:

» > f = open(r"file.txt", "r", encoding="cpl251")

> » f .readline (2), f .readline (2)

('Ct ', 'po')

» > f .readline (100) # Возвращается одна строка, а не 100 символов

'ка1\п'

» > f. close ()

♦ readlines о — считывает все содержимое файла в список. Каждый элемент списка бу-

дет содержать одну строку, включая символ перевода строки. Исключением является
последняя строка — если она не завершается символом перевода строки, таковой добав-
лен не будет. Если файл открыт в текстовом режиме, возвращается список строк, а если
в бинарном — список объектов типа bytes:

» > # Текстовый режим "г", encoding="cpl251") as f:
» > with open(r"file.txt", "rb") as f:

f .readlines ()
['Строка1\п', 'Строка2']
» > # Бинарный режим
» > with open(r"file.txt",

f.readlines()

[b1\xdl\xf2\xf0\xee\xea\xe01\n', b'\xdl\xf2\xf0\xee\xea\xe021]

♦ _n e x t_ () — считывает одну строку при каждом вызове. Если файл открыт в текстовом
режиме, возвращается строка, а если в бинарном— последовательность байтов. При
достижении конца файла возбуждается исключение stopiteration:

» > # Текстовый режим
» > f = open(r"file.txt", "r", encoding="cpl251")
» > f.__next__(), f.__ next__()
('Строка1\п', 'Строка2')

Работа с файлами и каталогами 297

» > f.__next__() # Достигнут конец файла
Traceback (most recent call last):

File "<pyshell#26>", line 1, in <module>
f.__next__() # Достигнут конец файла

Stoplteration
» > f.close()

Благодаря методу_next_() мы можем перебирать файл построчно в цикле for. Цикл
for на каждой итерации будет автоматически вызывать метод_next_(). Для примера
выведем все строки, предварительно удалив символ перевода строки:

» > f = open(r"file.txt", "г", encoding="cpl251")
» > for line in f: print (line, rstrip ("\n"), end=" ")

Строка1 Строка2
» > f. close ()

♦ flush () — принудительно записывает данные из буфера на диск;

♦ fileno о — возвращает целочисленный дескриптор файла. Возвращаемое значение все-
гда будет больше числа 2, т. к. число 0 закреплено за стандартным вводом stdin, 1— за
стандартным выводом stdout, а 2 — за стандартным выводом сообщений об ошибках

stderr:

» > f = open(r"file.txtn, "r", encoding="cpl251")

» > f. fileno () # Дескриптор файла

3

» > f. close ()

♦ truncate ( [<количество>]) — обрезает файл до указанного количества символов (если
задан текстовый режим) или байтов (в случае бинарного режима). Метод возвращает
новый размер файла:

» > f = open(r"file.txt", "r+", encoding="cpl251")
» > f.read()
'Строка1\пСтрока21
» > f .truncate (5)
5
> » f.closed
» > with open(r"file.txt", "r", encoding="cpl251") as f:

f .read()

'С т р о к 1

♦ tell () — возвращает позицию указателя относительно начала файла в виде целого чис-
ла. Обратите внимание: в Windows метод tell о считает символ \г как дополнительный
байт, хотя этот символ удаляется при открытии файла в текстовом режиме:

» > with open(r"file.txt", "w", encoding="cpl251") as f:
f.write("Stringl\nString2")

15

» > f = open(r"file.txt", "r", encoding="cpl251")

» > f.telld .# Указатель расположен в начале файла

О

298 Гпава 16

> » f .readline О # Перемещаем указатель
'Stringl\n' # Возвращает 9 (8 + '\г'), а не 8 !!!
» > f. tell ()
9
» > f. close ()

Чтобы избежать этого несоответствия, следует открывать файл в бинарном режиме, а не
в текстовом:

» > f = open(r"file.txt", "rb")

» > f. readline () # Перемещаем указатель

b' Stringl\r\n'

» > f.tellf) # Теперь значение соответствует

9

» > f.close()

♦ seek (<Смещение> [, <позиция>]) — устанавливает указатель в позицию, имеющую за-

данное <смещение> относительно параметра <позиция>. В качестве параметра <позиция>
могут быть указаны следующие атрибуты из модуля io или соответствующие им зна-

чения:

• io.s e e k s e t или о — начало файла (значение по умолчанию);

• io.SEEK CUR или 1 — текущая позиция указателя. Положительное значение смещения
вызывает перемещение к концу файла, отрицательное — к его началу;

• io .s e e k e n d или 2 — конец файла.

Выведем значения этих атрибутов:

» > import io
» > io.SEEK_SET, io.SEEK_CUR, io.SEEK_END
(0, 1, 2)

Вот пример использования метода seek ():

» > import io
» > f = open(r"file.txt", "rb")
» > f.seek(9, io.SEEK_CUR) # 9 байтов от указателя
9
» > f.tell ()
9
» > f.seek(0, io.SEEK_SET) # Перемещаем указатель в начало'
0
» > f.tell ()
0
» > f.seek(-9, io.SEEK_END) # -9 байтов от конца файла
7
» > f .tell ()
7
» > f. close О

♦ seekable () — возвращает True, если указатель файла можно сдвинуть в другую пози-
цию, и False — в противном случае:

» > f = open(r"C:\temp\new\file.txt", "г")
» > f. seekable ()
True

Работа с файлами и каталогами 299

Помимо методов, объекты файлов поддерживают несколько атрибутов:
♦ name — имя файла;
♦ mode — режим, в котором был открыт файл;
♦ closed — возвращает True, если файл был закрыт, и False — в противном случае:

» > f = open(r"file.txt", "r+b")
» > f.name, f.mode, f.closed

('file.txt', 'rb+', False)
» > f.closeO

» > f.closed

True

♦ encoding— название кодировки, которая будет использоваться для преобразования
строк перед записью в файл или при чтении. Атрибут доступен только в текстовом
режиме. Обратите также внимание на то, что изменить значение атрибута нельзя, по-
скольку он доступен только для чтения:

» > f = open (г"file.txt", "a", encoding="cpl251")
» > f.encoding
'cpl251'
» > f.closeO

Стандартный вывод stdout также является файловым объектом. Атрибут encoding этого
объекта всегда содержит кодировку устройства вывода, поэтому строка преобразуется
в последовательность байтов в правильной кодировке. Например, при запуске с помощью

двойного щелчка на значке файла атрибут encoding будет иметь значение "срвбб", а при

запуске в окне Python Shell редактора IDLE — значение "cpi25l":

» > import sys
» > sys.stdout.encoding
'cpl251'

♦ buffer — позволяет получить доступ к буферу. Атрибут доступен только в текстовом
режиме. С помощью этого объекта можно записать последовательность байтов в тексто-

вый поток:

» > f = open(r"file.txt", "w", encoding="cpl251")
» > f.buffer.write (bytes ("Строка", "cpl251"))
6
» > f.closeO

16.3. Доступ к файлам с помощью модуля os

Модуль os содержит дополнительные низкоуровневые функции, позволяющие работать
с файлами. Функциональность этого модуля зависит от используемой операционной систе-

мы. Получить название используемой версии модуля можно с помощью атрибута name.
В любой из поддерживаемых Python версий операционной системы Windows этот атрибут

возвращает значение "nt":

» > import os # Значение в ОС Windows 8
» > os.name
'nt'


Click to View FlipBook Version