InfoCity
InfoCity - виртуальный город компьютерной документации
Реклама на сайте







Размещение сквозной ссылки

 

Глава 10. Недавние добавления в версии 1.1


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


10.1 Последнее напечатанное выражение


В интерактивном режиме, последнее напечатанное выражение присваивается переменной _ . Это значит, что когда Вы используете Python как настольный калькулятор, стало проще продолжать вычисления, например:

>>> tax = 17.5 / 100
>>> price = 3.50
>>> price * tax
0.6125
>>> price + _
4.1125
>>> round (_, 2)
4.11
>>>

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


10.2 Строковые литералы



10.2.1 Двойные кавычки


Python может теперь использовать также и двойные кавычки для выделения строковых литералов, например: "this doesn't hurt a bit". Нет никакой семантической разницы между строками, заключенными в одинарные или двойные кавычки.


10.2.2 Продолжение строковых литералов


Строковые литералы могут охватывать несколько строк, путем записи символа новой строки и обратного слеша, например,

hello = "Это довольно длинная строка, содержащая\n\
несколько строк текста, точно также, как Вы сделали бы в С.\n\
      Пробелы в начале строки имеют значение.\n"
print hello
выведет следующее:
Это довольно длинная строка, содержащая
несколько строк текста, точно также, как Вы сделали бы в С.
      Пробелы в начале строки имеют значение.

10.2.3 Строки в тройных кавычках


В некоторых случаях, когда Вам нужно включить действительно длинную строку (например, содержащую несколько параграфов информативного текста), раздражает необходимость завершения каждой строки символами \n\ , особенно, если Вам хотелось бы время от времени переформатировать текст более мощным текстовым редактором, подобным Emacs. В таких ситуациях можно использовать строки, заключенные в тройные кавычки:

hello = """

     Эта строка (стринг) ограничена тремя двойными кавычками (3 раза ").
Новые строчки можно записывать без символов новой строки, хотя, \
все еще можно\n пользоваться всеми нормальными escape-последовательностями.

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

     Эта строка (стринг) заканчивается на новой строке.
"""

Так же можно воспользоваться тремя одинарными кавычками, без всякой семантической разницы.


10.2.4 Сопоставление строковых литералов


Один последний изворот: Вы можете сопоставить многочисленные строковые литералы. Два или более смежных строковых литерала (но не произвольные выражения!) разделенные только пробелом будут конкатенированы (без промежуточного пробела) в один строковый объект во время компилирования. Это делает возможным продолжение длинной строки (string) со следующей строки (line), без жертвования абзацным отступом или эффективностью, в отличие от использования оператора конкатенации строк + или продолжения в следующей строке самого литерала (так как передний пробел имеет значение внутри всех типов строковых литералов). Эта особенность, подобно всем строковым особенностям, за исключением строк в тройных кавычках, позаимствована из стандартного С.


10.3 Оператор форматирования



10.3.1 Основное употребление


Глава о форматировании вывода, в действительности, устарела: сейчас существует почти полный интерфейс форматирования в стиле С. Это сделано перегрузкой оператора взятия остатка (%) для левого операнда, являющегося строкой:

>>> import math
>>> print 'Значение PI приблизительно равно %5.3f.' % math.pi
Значение PI приблизительно равно 3.142.
>>>

Если имеется более одного форматирования в строке, то передавайте тьюпл в качестве правого операнда:

>>> table = {'Sjoerd':4127, 'Jack':4098, 'Dcab':8637678}
>>> for name, phone in table.items():
...     print '%-10s ==> %10d' % (name, phone)
...
Jack       ==>       4098
Dcab       ==>    8637678
Sjoerd     ==>       4127
>>>

Большая часть форматирования работает точно как в С и требует чтобы Вы передавали подходящий тип (иначе Вы получите исключение). Форматирование с помощью %s менее требовательно: если переданный аргумент - не строковый объект, он конвертируется в строку используя встроенную функцию str(). Поддерживается использование * для передачи ширины или точности как отдельный (целочисленный) аргумент. Форматы С %n и %p не поддерживаются.


10.3.2 Ссылка на переменные по именам


Если у Вас действительно длинная форматируемая строка, которую не хочется разбивать на части, было бы хорошо если мы могли сослаться на форматируемые переменные по имени, а не по позиции. Это можно сделать используя расширение форматов С в следующем виде: %(name)format , например:

>>> table = {'Sjoerd':4127, 'Jack':4098, 'Dcab':8637678}
>>> print 'Jack: %(Jack)d; Sjoerd: %(Sjoerd)d; Dcab: %(Dcab)d' % table
Jack: 4098; Sjoerd: 4127; Dcab: 8637678
>>>

Это особенно полезно в комбинации с новой встроенной функцией vars(), которая возвращает словарь, содержащий все локальные переменные.


10.4 Необязательные аргументы функций


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


10.4.1 Значение аргумента по умолчанию


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

def ask_ok (prompt, retries=4, complaint=’Да или нет, пожалуйста!’):
    while 1:
        ok = raw_input (prompt)
        if ok in ( ‘д’, ‘да’ ) : return 1
        if ok in ( ‘н’, ‘не’, ‘нет’ ) : return 0
        retries = retries - 1
        if retries < 0 : raise IOError, 'пользователь-отказник'
        print compliant

Эту функцию можно вызвать либо так:

ask_ok ( ‘Вы действительно хотите выйти ?’ )

либо вот так:

ask_ok ( ‘Переписать файл ?’, 2 )

Значения по умолчанию оцениваются в точке определения функции, в области определения, так что,

i = 5
def f ( arg=i ) : print arg
i = 6
f()
выведет 5 .

10.4.2 Списки произвольных аргументов


Также возможно уточнить, что функцию можно будет вызывать с произвольным количеством аргументов. Эти аргументы будут "завернуты" в тьюпл. Переде переменным набором аргументов может встретиться ноль, или более нормальных аргументов:

def fprintf ( file, format, *args ) :
       file.write ( format % args )

Эта особенность может быть использована с предыдущей:

def but_is_it_useful ( required, optional=None, *remains ) :
    print "I don’t know"

10.5 Лямбда и инструменты функционального программирования



10.5.1 Лямбда формы


По народному требованию, несколько особенностей, обычно находимых в языках функционального программирования и Lisp, были добавлены в Python. С помощью ключевого слова lambda может быть создана маленькая анонимная функция. Вот функция, возвращающая сумму двух своих аргументов: lambda a, b : a+b . В любом месте, где требуется объект-функция можно использовать лямбда формы. Синтаксически они ограничены простым выражением. Семантически, они - упрощенная форма для нормального определения функции. Лямбда формы не могут ссылаться на переменные из области своего содержания, но это можно обойти посредством разумного использования значения аргументов по умолчанию, например:

def make_incrementor (n) :
     return lambda x, incr = n : x + incr

10.5.2 Map, Reduce и Filter


Три новые встроенные функции для последовательностей являются хорошими кандидатами для того, чтобы передать им лямбда формы.

Map.
map ( function, sequence ) вызывает функцию function (item) для каждого элемента (item) последовательности (sequence) и возвращает список полученных значений. Например, для вычисления нескольких кубов:

>>> map ( lambda x: x*x*x, range(1,11) )
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
>>>

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

Комбинируя два этих особых случая, мы видим, что map (None, list1, list2) - удобный способ преобразования пары списков в вписок пар. Например:

>>> seq = range (8)
>>> map ( None, seq, map ( lambda x : x*x , seq ) )
[(0,0), (1,1), (2,4), (3,9), (4,16), (5,25), (6,36), (7,49)]
>>>
Filter.
filter ( function, sequence ) возвращает последовательность (такого же типа, если возможно), содержащую те элементы item последовательности sequence , для которых функция function (item) - истина. Например, для вычисления некоторых простых чисел:

>>> filter ( lambda x: x%2 != 0 and x%3 != 0 , range (2, 25) )
[5, 7, 11, 13, 17, 19, 23]
>>>
Reduce.
reduce (function, sequence) возвращает простое значение, составленное путем вызова функции function сначала с первыми двумя элементами последовательности sequence , с результатом и следующим элементом, и так далее. Например, для вычисления суммы чисел от 1 до 10:

>>> reduce (lambda x, y: x+y, range(1,11))
55
>>>

Если в последовательности только один элемент, возвращается это значение; если последовательность пуста, возникает исключение.

Можно передать и третий аргумент, для установки начального значения. В этом случае, начальное значение возвращается для пустой последовательности, и функция сначала применяется к стартовому значению и первому элементу последовательности, а затем к результату и следующему элементу, и так далее. Например:

>>> def sum (seq):
...     return reduce (lambda x, y: x+y, seq, 0)
...
>>> sum (range (1, 11))
55
>>> sum ([])
0
>>>

10.6 Продолжение строк без обратного слеша


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

Например:

month_names = [‘Январь’, ‘Февраль’, ‘Март’,
               ‘Апрель’, ‘Май’, ‘Июнь’, ‘Июль’,
               ‘Август’, ‘Сентябрь’, ‘Октябрь’,
               ‘Ноябрь’, ‘Декабрь’]

и

CopyInternalHyperLinks (self.context.hyperlinks,
                        copy.context.hyperlinks,
                        uidremap)

10.7 Регулярные выражения


Хотя printf-стиль С форматирования вывода, встроенный в Python, достаточен для большинства случаев форматирования вывода, scanf-стиль С форматированного ввода не очень полезен. Вместо scanf-стиля ввода, Python предлагает Emacs-стиль регулярных выражений, как мощный механизм ввода и сканирования. Для полного описания читайте соответствующую секцию в "Справочнике библиотеки Python".


10.8 Обобщенные словари


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

Словари теперь имею два новых метода: d.values() возвращает список значений словаря, и d.items() возвращает пары (ключ, значение) словаря. Подобно d.keys() , эти операции работают медленно для больших словарей. Примеры:

>>> d = { 100:’сто’, 1000:’тысяча’, 10:’десять’ }
>>> d.keys()
{100, 10, 1000]
>>> d.values()
[‘сто’, ‘десять’, ‘тысяча’]
>>> d.items()
[(100, ‘сто’), (10, ‘десять’), (1000, ‘тысяча’)]
>>>

10.9 Разнообразные новые встроенные функции


Функция vars() возвращает словарь, содержащий текущие локальные переменные. С модулем в качестве аргумента, она возвращает глобальные переменные того модуля. Старая функция dir(x) возвращает var(x).keys() .

Функция round(x) возвращает вещественное число, округленное к ближайшему целому (но все еще выраженный как число с плавающей точкой). Например, round(3.4)==3.0 и round(3.5)==4.0 . Со вторым аргументом, она округляет до указанного числа разрядов: round(math.pi, 4==3.1416 или даже round(123.4, -2)==100.0 .

Функция hash(x) возвращает хэш-значение для объекта. Все типы объектов, приемлемые в качестве ключей словаря, имеют хэш-значение (и это как раз то хэш-значение, которое используется реализацией словаря).

Функция id(x) возвращает уникальный идентификатор объекта. Для двух объектов x и y, id(x)==id(y) тогда и только тогда, когда x и y - один и тот же объект. (Фактически, используются адреса объектов.)

Функция hasattr(x,name) возвращает истинное значение ( 1 ) если объект имеет атрибут с данным именем name (строковое значение). Функция getattr(x,name) возвращает объектовый атрибут с данным именем. Функция setattr(x,name,value) присваивает значение value объектовому атрибуту с данным именем name . Эти три функции полезны если имена атрибутов не известны заранее.

Примечание: getattr(s,’spam’) эквивалентно x.spam=y . По определению, hasattr(x,name) возвращает истину тогда и только тогда, когда getattr(x,name) не вызывает исключения.


10.10 Пункт else для оператора try


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

for arg in sys.argv:
        try:
               f = open (arg, ‘r’)
        except IOError:
               print ‘Не могу открыть’, arg
        else:
               print arg, ‘содержит’, len ( f.readlines() ), ‘строк’
               f.close()

10.11 Новые особенности классов в версии 1.1


С классами произошли некоторые изменения: механизм перегрузки операторов стал более гибким, предоставляющим улучшенную поддержку для нечислового использования операторов (включая вызов объекта, как будто бы это функция), и можно запереть доступ к атрибутам.


10.11.1 Новая перегрузка операторов


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

В порядке создания возможности реализации двоичных операторов у которых правая часть - экземпляр класса, а левая часть - нет, без использования принуждения, версии правых частей двоичных операторов могут быть определены. При этом ‘r’ предшествует имени оператора, например: __radd__ .

В качестве примера здесь приводится очень простой класс, представляющий время (time). Время инициализируется числом секунд (как time.time()). И выводится в следующем формате: Wed Mar 15 12:28:48 1995 . Вычитание двух time дает разницу в секундах. Сложение или вычитание time и числа дает новое time . Нельзя складывать два time и вычитать time из числа.

import time

class Time:
     def __init__ (self, seconds):
         self.seconds = seconds
     def __repr__ (self):
         return time.ctime (self.seconds)
     def __add__ (self, x):
         return Time(self.seconds + x)
     __radd__ = __add__ # поддержка для x + t
     def __sub__ (self, x):
         if hasattr (x, ‘seconds’): # проверка того, что x - это Time
              return self.seconds - x.seconds
         else:
              return self.seconds - x

now = Time (time.time())
tomorrow = 24*3600 + now
yesterday = now - today
print tomorrow - yesterday   # выведет 172800

10.11.2 Доступ к закрытому атрибуту


Теперь Вы можете определить три новых "волшебных" метода: __getattr__(self, name) , __setattr__(self, name, value) и __delattr__(self, name) .

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

Методы __setattr__ и __delattr__ вызываются при попытке присваивания, соответственно, удаления атрибута. Они получают управление вместо нормального действия (которое состоит во вставке или удалении атрибута из словаря экземпляра). Если в задачу этих методов также входит установка или удаление атрибута, они смогут это сделать только прямо используя словарь экземпляра - self.__dict__ - иначе они будут вызваны рекурсивно.

Для примера здесь приводится близкий к универсальному класс "Wrapper", который передает доступ ко всем своим атрибутам другому объекту. Обратите внимание на то, как метод __init__ вставляет "оборачиваемый" объект в self.__dict__ , во избежание бесконечной рекурсии (__setattr__ вызвал бы __getattr__ , который продолжил бы вызывать рекурсивно сам себя).

class Wrapper:
    def __init__(self, wrapper):
        self.__dict__[‘wrapper’] = wrapped
    def __getattr__(self, name):
        return getattr(self.wrapped, name)
    def __setattr__(self, name, value):
        setattr(self.wrapped, name, value)
    def __delattr__(self, name):
        delattr(self.wrapped, name)

import sys
f = Wrapper (sys.stdout)
f.write (‘hello world\n’) # выводит ‘hello world’

А вот простой пример для __getattr__ , который при каждом обращении к нему обрабатывает атрибут для доступа:

from math import pi

class Circle:
    def __init__(self, radius):
        self.radius = radius
    def __getattr__(self, name):
        if name==’circumference’:
            return 2*pi*self.radius
        if name==’diameter’:
            return 2*self.radius
        if name==’area’:
            return pi*pow(self.radius, 2)
        raise AttributeError, name

10.11.3 Вызов экземпляра класса


Если класс определяет метод __call__ , то можно вызывать его экземпляры так, как будто они являются функциями, к примеру:

class PresetSomeArguments:
    def __init__(self, func, *args):
        self.func, self.args = func, args
    def __call__(self, *args):
        return apply(self.func, self.args + args)

f = PresetSomeArguments (pow, 2) # f(i) подсчитывает степени числа z
for i in range(10): print f(i), # печатает 1 2 4 8 16 32 64 128 256 512
print   # переход на новую строку

[Назад][Содержание][Вперед]


Реклама на InfoCity

Яндекс цитирования



Финансы: форекс для тебя








1999-2009 © InfoCity.kiev.ua