справочник по Python. мм isbn 9785932861578 9 785932 861578
Скачать 4.21 Mb.
|
foo(3,-2) 5 >>> foo(-5,2) Traceback (most recent call last): File “ File “meta.py”, line 19, in call def assert_call(*args,**kwargs): AssertionError: a must be positive (Перевод: Трассировочная информация (самый последний вызов – самый нижний): Файл “ Файл “meta.py”, строка 19, в вызове def assert_call(*args,**kwargs): 776 Приложение A. Python 3 AssertionError: a должно быть положительным ) >>> Только именованные аргументы Ф ункцию можно определить так, что она будет принимать некоторые аргу- менты только как именованные аргументы. Достигается это за счет добав- ления дополнительных аргументов после первого аргумента со звездочкой в имени. Например: def foo(x, *args, strict=False): инструкции В вызове такой функции аргумент strict может быть указан только как именованный. Например: a = foo(1, strict=True) Любые дополнительные позиционные аргументы будут помещаться в кор- теж args и не будут использоваться для установки значения аргумента strict . Если нежелательно, чтобы функция принимала переменное чис- ло аргументов, но желательно, чтобы некоторые аргументы принимались только как именованные, этого можно добиться, добавив одиночный сим- вол * в список аргументов. Например: def foo(x, *, strict=False): инструкции Ниже приводится пример использования: foo(1,True) # Ошибка. TypeError: foo() takes 1 positional argument # TypeError: foo() принимает 1 позиционный аргумент foo(1,strict=True) # Ok. Объекты класса Ellipsis как выражения Объект класса Ellipsis object (...) теперь может использоваться в качестве выражения. Это позволяет сохранять такие объекты в контейнерах и в пе- ременных. Например: >>> x = ... # Присвоит объект класса Ellipsis >>> x Ellipsis >>> a = [1,2,...] >>> a [1, 2, Ellipsis] >>> ... in a True >>> x is ... True >>> Способ интерпретации многоточий остается за использующим их приложе- нием. Эта особенность позволяет использовать многоточие (...) как часть син- Новые возможности языка 777 таксиса в библиотеках и фреймворках (например, как шаблонный символ, как признак продолжения и для обозначения других подобных понятий). Цепочки исключений Теперь имеется возможность объединять исключения в цепочки. По сути это обеспечивает возможность передать в текущем исключении информа- цию о предыдущем исключении. Для явного объединения исключений ис- пользуется инструкция raise с квалификатором from. Например: try: инструкции except ValueError as e: raise SyntaxError(“Couldn’t parse configuration”) from e В случае возбуждения исключения SyntaxError будет выведено следующее сообщение об ошибке, содержащее информацию о двух исключениях: Traceback (most recent call last): File “ ValueError: invalid literal for int() with base 10: ‘nine’ ёёё The above exception was the direct cause of the following exception: ёёё Traceback (most recent call last): File “ SyntaxError: Couldn’t parse configuration (Перевод: Трассировочная информация (самый последний вызов – самый нижний): Файл “ ValueError: недопустимый литерал в вызове int() с основанием 10: ‘nine’ ёёё Исключение, приведенное выше, стало причиной следующего исключения: ёёё Трассировочная информация (самый последний вызов – самый нижний): Файл “ SyntaxError: Ошибка разбора файла с настройками ) Объекты исключений обладают атрибутом __cause__, в котором сохраняет- ся предыдущее исключение. Квалификатор from в инструкции raise уста- навливает значение этого атрибута. Более сложный пример объединения исключений связан с возбуждением исключения в обработчике другого исключения. Например: def error(msg): print(m) # Обратите внимание: преднамеренная ошибка (имя m не определено) ёёё try: инструкции except ValueError as e: error(“Couldn’t parse configuration”) Если попытаться выполнить этот фрагмент в Python 2, будет выведено сообщение о единственном исключении NameError, возникшем в функции 778 Приложение A. Python 3 error() . В Python 3 предыдущее обрабатываемое исключение будет объеди- Python 3 предыдущее обрабатываемое исключение будет объеди- 3 предыдущее обрабатываемое исключение будет объеди- нено с результатом. Например, будет получено следующее сообщение: Traceback (most recent call last): File “ ValueError: invalid literal for int() with base 10: ‘nine’ ёёё During handling of the above exception, another exception occurred: ёёё Traceback (most recent call last): File “ File “ NameError: global name ‘m’ is not defined (Перевод: Трассировочная информация (самый последний вызов – самый нижний): Файл “ ValueError: недопустимый литерал в вызове int() с основанием 10: ‘nine’ ёёё В процессе обработки исключения, приведенного выше, возникло другое исключение: ёёё Трассировочная информация (самый последний вызов – самый нижний): Файл “ Файл “ NameError: глобальное имя ‘m’ не определено ) При неявном объединении ссылка на предыдущее исключение сохраняет- ся в атрибуте __context__ экземпляра последнего исключения. Улучшенная реализация функции super() Функция super() производит поиск методов в базовых классах. В Python 3 она может вызываться без аргументов. Например: class C(A,B): def bar(self): return super().bar() # Вызовет метод bar() базового класса В Python 2 пришлось бы вызывать эту функцию как super(C,self).bar(). Старый синтаксис по-прежнему поддерживается, но он выглядит менее удобочитаемым. Улучшенные метаклассы В Python 2 имеется возможность определять метаклассы, которые изменя- Python 2 имеется возможность определять метаклассы, которые изменя- 2 имеется возможность определять метаклассы, которые изменя- ют поведение других классов. Важной особенностью реализации являет- ся то, что обработка метаклассом выполняется только после того, как все тело класса будет выполнено интерпретатором. То есть после того, как ин- терпретатор выполнит все тело класса и заполнит словарь. Как только сло- варь будет заполнен, он передается конструктору метакласса (после того, как тело класса уже будет выполнено). В Python 3 метаклассы могут выполнять дополнительные операции перед тем, как интерпретатор приступит к выполнению тела класса. Это достига- ется за счет определения в метаклассе специального метода с именем __pre- pre- Новые возможности языка 779 pare__( cls, name, bases, **kwargs) . В качестве результата данный метод дол- жен возвращать словарь. Этот словарь будет заполняться интерпретатором по мере выполнения определений в теле класса. Ниже приводится пример, иллюстрирующий простейшую обработку: class MyMeta(type): @classmethod def __prepare__(cls,name,bases,**kwargs): print(“подготавливается”,name,bases,kwargs) return {} def __new__(cls,name,bases,classdict): print(“создается”,name,bases,classdict) return type.__new__(cls,name,bases,classdict) В Python 3 для определения метаклассов можно использовать альтерна- Python 3 для определения метаклассов можно использовать альтерна- 3 для определения метаклассов можно использовать альтерна- тивный синтаксис. Например, ниже приводится определение класса, ис- пользующего метакласс MyMeta: class Foo(metaclass=MyMeta): print(“Начало определения методов”) def __init__(self): pass def bar(self): pass print(“Конец определения методов”) Выполнив предыдущий программный код, вы увидите следующие резуль- таты, иллюстрирующие порядок выполнения: подготавливается Foo () {} Начало определения методов Конец определения методов создается Foo () {‘__module__’: ‘__main__’, ‘bar’: ‘__init__’: Методу __prepare__() метакласса передаются дополнительные именован- ные аргументы, которые указываются в списке базовых классов инструк- ции class. Например, при использовании инструкции class Foo(metaclass= MyMeta,spam=42,blah=”Hello”) метод MyMeta.__prepare__() получит именован- ные аргументы spam и blah. Это соглашение может использоваться для пере- дачи метаклассу произвольных параметров настройки. Чтобы заставить новый метод __prepare__() метаклассов произвести какие- то полезные действия, его можно обязать возвращать объект словаря, об- ладающий дополнительными свойствами. Например, если необходимо реализовать дополнительную обработку, которая должна выполняться в процессе определения класса, следует объявить класс, производный от класса dict, и переопределить в нем метод __setitem__(), в котором реали- зовать обработку операций присваивания элементам словаря класса. Сле- дующий пример иллюстрирует такую возможность, объявляя метакласс, который сообщает об ошибках, если какой-либо метод или атрибут класса объявляется несколько раз. 780 Приложение A. Python 3 class MultipleDef(dict): def __init__(self): self.multiple= set() def __setitem__(self,name,value): if name in self: self.multiple.add(name) dict.__setitem__(self,name,value) ёёё class MultiMeta(type): @classmethod def __prepare__(cls,name,bases,**kwargs): return MultipleDef() def __new__(cls,name,bases,classdict): for name in classdict.multiple: print(name,”повторное определение”) if classdict.multiple: raise TypeError(“Присутствует несколько определений “) return type.__new__(cls,name,bases,classdict) Е сли применить этот метакласс к определению другого класса, он будет сообщать об ошибке при обнаружении повторного определения любого из методов. Например: class Foo(metaclass=MultiMeta): def __init__(self): pass def __init__(self,x): # Ошибка. __init__ повторное определение. pass Типичные ошибки Если вы планируете переход с версии Python 2 на версию Python 3, имейте в виду, что Python 3 – это не только новый синтаксис и возможности язы- Python 3 – это не только новый синтаксис и возможности язы- 3 – это не только новый синтаксис и возможности язы- ка. При переделке основных частей ядра и библиотеки в некоторых слу- чаях вносились неожиданные, не ставящие своей целью обеспечение со- вместимости, изменения. Некоторые аспекты Python 3 могут выглядеть, как ошибки для программистов, использующих Python 2. Некоторые кон- Python 2. Некоторые кон- 2. Некоторые кон- струкции, «простые» в использовании в версии Python 2, теперь считают- Python 2, теперь считают- 2, теперь считают- ся недопустимыми. В этом разделе отмечаются некоторые наиболее серьезные ошибки, кото- рые часто допускаются программистами, привыкшими использовать Py- Py- thon 2. Текст и байты В Python 3 проводится очень строгое разграничение между текстовыми строками (символы) и двоичными данными (байты). Такие литералы, как “hello” , представляют текстовые данные и хранятся в виде строк Юникода, а литералы, такие как b”hello”, представляет строки байтов (в данном слу- чае строку, содержащую символы ASCII). Типичные ошибки 781 В Python 3 не допускается смешивать типы str и bytes. Например, если по- пытаться выполнить конкатенацию текстовой строки и строки байтов, бу- дет возбуждено исключение TypeError. Это важное отличие от Python 2, где строка байтов автоматически была бы преобразована в строку Юникода. Чтобы преобразовать текстовую строку s в строку байтов, необходимо вы- звать метод s.encode(encoding). Например, вызов s.encode(‘utf-8’) преобразу- ет s в строку байтов в кодировке UTF-8. Чтобы строку байтов t преобразовать обратно в текстовую строку, необходимо вызвать метод t.decode(encoding). Методы encode() и decode() можно рассматривать как своеобразные опера- ции «приведения типов» между текстовыми строками и строками байтов. Наличие четкого разделения между текстом и двоичными данными мож- но считать удачным решением – правила смешивания строковых типов в Python 2 по меньшей мере были неясными и трудными для понимания. Однако одно из последствий такого разграничения состоит в том, что стро- ки байтов в Python 3 не обеспечивают такой же гибкости в представлении «текста». Несмотря на наличие стандартных строковых методов, таких как split() и replace(), многие другие особенности строк байтов отличаются от тех, что имеются в Python 2. Например, если вывести строку байтов с помо- Python 2. Например, если вывести строку байтов с помо- 2. Например, если вывести строку байтов с помо- щью функции print(), вывод будет произведен с помощью функции repr() в виде b’содержимое’. Точно так же ни одна из строковых операций формати- рования (%, .format()) не будет работать со строками байтов. Например: x = b’Hello World’ print(x) # Выведет b’Hello World’ print(b”You said ‘%s’” % x) # TypeError: % operator not supported # TypeError: оператор % не поддерживается Отсутствие некоторых возможностей, присущих текстовым строкам, явля- ется потенциальной ловушкой для системных программистов. Несмотря на повсеместное распространение Юникода, во многих случаях бывает не- обходимо работать с текстом, представленным однобайтовыми символа- ми, такими как ASCII. Чтобы избежать лишних сложностей, связанных с Юникодом, может появиться желание использовать тип bytes. Однако на самом деле это только осложнит обработку такого текста. Ниже приводит- ся пример, иллюстрирующий потенциальную проблему: >>> # Создать сообщение ответа, используя строки (Юникод) >>> status = 200 >>> msg = “OK” >>> proto = “HTTP/1.0” >>> response = “%s %d %s” % (proto, status, msg) >>> print(response) HTTP/1.0 200 OK ёёё >>> # Создать сообщение ответа, используя строки байтов (ASCII) >>> status = 200 >>> msg = b”OK” >>> proto = b”HTTP/1.0” >>> response = b”%s %d %s” % (proto, status, msg) Traceback (most recent call last): File “ 782 Приложение A. Python 3 TypeError: unsupported operand type(s) for %: ‘bytes’ and ‘tuple’ (Перевод: Трассировочная информация (самый последний вызов – самый нижний): Файл “ TypeError: неподдерживаемые типы операндов для %: ‘bytes’ и ‘tuple’ ) ёёё >>> response = proto + b” “ + str(status) + b” “ + msg Traceback (most recent call last): File “ TypeError: can’t concat bytes to str (Перевод: Трассировочная информация (самый последний вызов – самый нижний): Файл “ TypeError: невозможно объединить данные типов bytes и str ) ёёё >>> bytes(status) b’\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00....’ ёёё >>> bytes(str(status)) Traceback (most recent call last): File “ TypeError: string argument without an encoding (Перевод: Трассировочная информация (самый последний вызов – самый нижний): Файл “ TypeError: не указана кодировка для строкового аргумента ) ёёё >>> bytes(str(status),’ascii’) b’200’ ёёё >>> response = proto + b” “ + bytes(str(status),’ascii’) + b” “ + msg >>> print(response) b’HTTP/1.0 200 OK’ ёёё >>> print(response.decode(‘ascii’)) HTTP/1.0 200 OK >>> В примере можно видеть, насколько четко в Python 3 проведена грань между текстом и двоичными данными. Даже операции, которые на пер- вый взгляд должны выполняться очень просто, такие как преобразование целых чисел в символы ASCII, оказываются намного сложнее при работе с байтами. В итоге, если предполагается выполнять какие-либо операции по обработ- ке или форматированию текста, практически всегда лучше использовать стандартные текстовые строки. Если по завершении обработки необходи- мо получить строку байтов, для преобразования строки Юникода в строку байтов можно воспользоваться методом s.encode(‘latin-1’). Различие между текстом и двоичными данными может быть менее четким при использовании различных библиотечных модулей. Некоторые модули Типичные ошибки 783 одинаково хорошо работают как с текстовыми строками, так и со строками байтов, тогда как другие вообще не могут работать с байтами. В некоторых случаях поведение модуля может зависеть от типа входных данных. На- пример, функция os.listdir(dirname) возвращает имена файлов, которые могут быть успешно декодированы как строки Юникода, только если в ар- гументе dirname передается текстовая строка. Если в аргументе dirname пе- редать строку байтов, все возвращаемые имена файлов будут представлены строками байтов. Новая система ввода-вывода В Python 3 была реализована совершенно новая система ввода-вывода, описание которой приводится в главе 19 «Службы операционной систе- мы», в разделе с описанием модуля io. Новая система ввода-вывода также отражает существенные различия между текстом и двоичными данными, представленными в виде строк. Если предполагается выполнять какие-либо операции ввода-вывода с тек- стовыми данными, в Python 3 потребуется открывать файлы в «текстовом режиме» и указывать кодировку, если кодировка по умолчанию (обычно UTF-8) по каким-либо причинам не подходит. При выполнении операций ввода-вывода с двоичными данными файлы должны открываться в «дво- ичном режиме», и операции должны выполняться над строками байтов. Типичной ошибкой является попытка вывести данные в файл или в поток ввода-вывода, открытый не в том режиме. Например: >>> f = open(“foo.txt”,”wb”) >>> |