Chapter 2: Язык программирования Python

Язык программирования Python

Python

О Python

Python является универсальным языком программирования высокого уровня. Его философия построения придает особое значение продуктивности программиста и читаемости кода. Он имеет минимальный базовый синтаксис с небольшим набором основных команд и простой семантикой, но он также имеет большую и всеобъемлющую стандартную библиотеку, включая интерфейс прикладного программирования - Application Programming Interface (API)

API
для многих из лежащих в основе операционной системы (ОС) функций. Python код, при всей своей минималистичности, определяет встроенные объекты, такие как связанные списки (list), кортежи (tuple), хэш-таблицы (dict), и сколь угодно длинные целые (long).

Python поддерживает несколько парадигм программирования, в том числе объектно-ориентированное (class), императивное (def), и функциональное (lambda) программирование. Python имеет динамическую систему типов и автоматическое управление памятью с использованием подсчета ссылок (похожей на Perl, Ruby, и Scheme).

Python был впервые выпущен Гвидо ван Россумом в 1991 году. Язык имеет открытую, основанную на сообществе модель разработки под управлением некоммерческой организацией Python Software Foundation. Есть много интерпретаторов и компиляторов, которые реализуют язык Python, в том числе один на Java (Jython), но в этом кратком обзоре, мы обратимся к реализации Python на языке C, созданной Гвидо.

Вы можете найти множество учебных пособий, официальной документации и библиотек к данному языку на официальном сайте Python.[python]

Для получения дополнительных литературы по Python, мы можем порекомендовать книги, расположенные по ссылкам [guido] и [lutz] .

Вы можете пропустить эту главу, если вы уже знакомы с языком Python.

Начало работы

shell

Бинарные дистрибутивы web2py для Microsoft Windows или Apple OS X поставляются в комплекте с интерпретатором Python, встроенным в файл дистрибутива.

Вы можете запустить его на Windows, с помощью следующей команды (наберите в командной строке DOS):

1
web2py.exe -S welcome

На Apple OS X, введите следующий тип команды в окне терминала (если вы находитесь в одной папке с web2py.app):

1
./web2py.app/Contents/MacOS/web2py -S welcome

На Linux или другой Unix системе, есть вероятность, что Python уже установлен. Если да, то ведите в оболочке командной строки:

1
python web2py.py -S welcome

Если у вас нет установленного Python 2.7, вам придется скачать и установить его перед запуском web2py.

Опция командной строки -S welcome предписывает web2py запустить интерактивную оболочку, как если бы эти команды были выполнены в контроллере для приложения welcome, скаффолдингового приложения web2py. Это открывает почти все web2py классы, объекты и функции для вас. Это единственное различие между интерактивной командной строки web2py и нормальной командной строки Python.

Интерфейс администратора также предоставляет веб-оболочку для каждого приложения. Вы можете получить доступ к одному приложению "welcome" следующим образом.

1
http://127.0.0.1:8000/admin/shell/index/welcome

Вы можете попробовать все примеры в этой главе с помощью обычной оболочки или веб-оболочки.

Команды help, dir

help
dir

Язык Python предоставляет две команды для получения документации об объектах, определенных в текущей области видимости, обе являются встроенные и определяемые пользователем.

Мы можем запросить help для любого объекта, например для цифры "1":

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
>>> help(1)
Help on int object:

class int(object)
 |  int(x[, base]) -> integer
 |
 |  Convert a string or number to an integer, if possible.  A floating point
 |  argument will be truncated towards zero (this does not include a string
 |  representation of a floating point number!)  When converting a string, use
 |  the optional base.  It is an error to supply a base when converting a
 |  non-string. If the argument is outside the integer range a long object
 |  will be returned instead.
 |
 |  Methods defined here:
 |
 |  __abs__(...)
 |      x.__abs__() <==> abs(x)
...

и, так как цифра "1" представляет собой целое число, то мы получаем описание класса int и всех его методов. Здесь вывод был укорочен, потому что он очень длинный и подробный.

Аналогичным образом, мы можем получить список методов объекта "1" используюя команду dir:

1
2
>>> dir(1)
['__abs__', ..., '__xor__']

Типы данных

type

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

1
2
3
4
5
6
7
8
9
>>> a = 3
>>> print type(a)
<type 'int'>
>>> a = 3.14
>>> print type(a)
<type 'float'>
>>> a = 'hello python'
>>> print type(a)
<type 'str'>

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

Строки str

str
ASCII
UTF8
Unicode
encode

Python поддерживает использование двух различных типов строк: ASCII строки и Unicode строки. ASCII строки выделяются при помощи одинарных кавычек вида '...', "..." или тройных кавычек вида '..' или """...""". Тройными кавычками выделяются многострочные строки. Юникод строки начинаются со знака u перед кавычками, за которым следует строка, содержащая символы в формате Юникод. Строка в формате Юникод может быть преобразована в строку ASCII посредством выбранного способа кодирования, например:

1
2
3
>>> a = 'this is an ASCII string'
>>> b = u'This is a Unicode string'
>>> a = b.encode('utf8')

После выполнения этих трех команд, полученная переменная a будет представлять ASCII строку, содержащую символы в UTF-8 кодировке. По проекту, web2py использует внутренне кодированные UTF-8 строки.

Кроме того, можно записать переменные внутрь строки различными способами:

1
2
3
4
5
6
>>> print 'number is ' + str(3)
number is 3
>>> print 'number is %s' % (3)
number is 3
>>> print 'number is %(number)s' % dict(number=3)
number is 3

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

Многие объекты Python, например, числа, можно сериализовать в строки используя str или repr. Эти две команды очень похожи, но производят несколько иной вывод. Например:

1
2
3
4
>>> for i in [3, 'hello']:
        print str(i), repr(i)
3 3
hello 'hello'

Для определенных пользователем классов, функции str и repr могут быть определены/переопределены с помощью специальных операторов __str__ and __repr__. Они вкратце описаны в дальнейшем; для получения дополнительной информации, обратитесь к официальной Python документации [pydocs] . repr всегда является значением по умолчанию.

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

1
2
3
4
5
6
7
>>> for i in 'hello':
        print i
h
e
l
l
o

Списки list

list

Основными методами списка Python являются методы добавления, вставки и удаления:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
>>> a = [1, 2, 3]
>>> print type(a)
<type 'list'>
>>> a.append(8)
>>> a.insert(2, 7)
>>> del a[0]
>>> print a
[2, 7, 3, 8]
>>> print len(a)
4

Списки могут быть разрезаны:

1
2
3
4
5
6
>>> print a[:3]
[2, 7, 3]
>>> print a[1:]
[7, 3, 8]
>>> print a[-2:]
[3, 8]

и объединены:

1
2
3
4
>>> a = [2, 3]
>>> b = [5, 6]
>>> print a + b
[2, 3, 5, 6]

Списки являются итерируемыми, вы можете использовать их в цикле:

1
2
3
4
5
6
>>> a = [1, 2, 3]
>>> for i in a:
        print i
1
2
3

Элементы списка не обязательно должны быть одного и того же типа; они могут быть объектами Python любого типа.

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

1
2
3
4
5
6
7
>>> a = [1,2,3,4,5]
>>> b = []
>>> for x in a:
        if x % 2 == 0:
            b.append(x * 3)
>>> b
[6, 12]

Этот код явно обрабатывает список элементов, выбирает и изменяет подмножество во входном списке, и создает новый результирующий список, и этот код может быть полностью заменен следующим осмысленным списком:

1
2
3
4
>>> a = [1,2,3,4,5]
>>> b = [x * 3 for x in a if x % 2 == 0]
>>> b
[6, 12]

Кортежи tuple

tuple

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

1
>>> a = (1, 2, 3)

То, что работает для списка:

1
2
3
4
>>> a = [1, 2, 3]
>>> a[1] = 5
>>> print a
[1, 5, 3]

Не работает для кортежа - присвоение значения элементу:

1
2
3
4
5
6
7
>>> a = (1, 2, 3)
>>> print a[1]
2
>>> a[1] = 5
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

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

1
2
3
4
5
6
>>> a = (1)
>>> print type(a)
<type 'int'>
>>> a = (1,)
>>> print type(a)
<type 'tuple'>

Кортежи являются очень полезными для эффективной упаковки объектов в силу их неизменности, и скобки часто бывают необязательными:

1
2
3
4
5
6
>>> a = 2, 3, 'hello'
>>> x, y, z = a
>>> print x
2
>>> print z
hello

Словари dict

dict

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

1
2
3
4
5
6
7
8
9
>>> a = {'k':'v', 'k2':3}
>>> a['k']
v
>>> a['k2']
3
>>> a.has_key('k')
True
>>> a.has_key('v')
False

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

1
2
3
4
5
>>> a = dict(k='v', h2=3)
>>> a['k']
v
>>> print a
{'k':'v', 'h2':3}

Полезными методами являются has_key, keys, values и items:

1
2
3
4
5
6
7
>>> a = dict(k='v', k2=3)
>>> print a.keys()
['k', 'k2']
>>> print a.values()
['v', 3]
>>> print a.items()
[('k', 'v'), ('k2', 3)]

Метод items создает список кортежей, каждый из которых содержит ключ и его соответствующее значение.

Элементы словаря и элементы списка могут быть удалены с помощью команды del:

1
2
3
4
5
6
7
8
>>> a = [1, 2, 3]
>>> del a[1]
>>> print a
[1, 3]
>>> a = dict(k='v', h2=3)
>>> del a['h2']
>>> print a
{'k':'v'}

Внутренне, Python использует оператор hash для преобразования объектов в целое число, и использует это число для определения места хранения значения.

1
2
>>> hash("hello world")
-1500746465

Об отступах

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

1
2
3
4
5
6
7
8
>>> i = 0
>>> while i < 3:
>>>    print i
>>>    i = i + 1
>>>
0
1
2

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

Цикл for...in

for

В Python, вы можете использовать цикл над итерируемыми объектами:

1
2
3
4
5
6
7
>>> a = [0, 1, 'hello', 'python']
>>> for i in a:
        print i
0
1
hello
python

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

1
2
3
4
5
6
>>> for i in xrange(0, 4):
        print i
0
1
2
3

Это эквивалентно синтаксису в C/C++/C#/Java:

1
for(int i=0; i<4; i=i+1) { print(i); }

Еще одна полезная команда enumerate, которая пронумерует элементы во время цикла:

1
2
3
4
5
6
7
>>> a = [0, 1, 'hello', 'python']
>>> for i, j in enumerate(a):
        print i, j
0 0
1 1
2 hello
3 python

Существует также оператор range(a, b, c) который возвращает последовательность целых чисел в виде списка, который начинается со значения a, с шагом между числами c, и заканчивая последним значением меньше b, по умолчанию a равняется 0, c равняется 1. Оператор xrange похож, но фактически не генерирует список, а возвращает итератор списка, поэтому его лучше всего использовать для циклов.

Вы можете выскочить из цикла используя оператор break

1
2
3
4
>>> for i in [1, 2, 3]:
         print i
         break
1

Вы можете перескочить к следующей итерации цикла, не выполняя весь блок кода используя continue

1
2
3
4
5
6
7
>>> for i in [1, 2, 3]:
         print i
         continue
         print 'test'
1
2
3

Цикл while

while

Цикл while в Python работает так же, как это делается во многих других языках программирования, данный цикл выполняет итерацию неопределенное количество раз с проверку условий перед каждой итерацией. Если условия ложные - False, то цикл завершается.

1
2
3
4
5
>>> i = 0
>>> while i < 10:
        i = i + 1
>>> print i
10

Это не loop...until конструкция в Python.

Условный оператор if...elif...else

if
elif
else
Использование условного опреатора в Python является интуитивно понятным:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
>>> for i in range(3):
>>>     if i == 0:
>>>         print 'zero'
>>>     elif i == 1:
>>>         print 'one'
>>>     else:
>>>         print 'other'
zero
one
other

"elif" означает "else if". Оба выражения elif и else не являются обязательными. В конструкции может быть более одногоelif, но только одно выражение else в конце конструкции. Сложные условия могут быть созданы с помощью операторов not, and и or.

1
2
3
>>> for i in range(3):
>>>     if i == 0 or (i == 1 and i + 1 == 2):
>>>         print '0 or 1'

Обработка исключений try...except...else...finally

try
except
finally
Exception
Python может выбросить - пардон, возбудить - Исключение:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
>>> try:
>>>     a = 1 / 0
>>> except Exception, e:
>>>     print 'oops: %s' % e
>>> else:
>>>     print 'no problem here'
>>> finally:
>>>     print 'done'
oops: integer division or modulo by zero
done

Если исключение возбуждается, то оно перехватывается выражением except, которое выполняется, а выражение else - нет. Если исключение не возбуждается, то выражение except не выполняется, но выполняется выражение else. Выражение finally выполняется всегда.

В конструкции может быть несколько выражений except для всевозможных исключений:

1
2
3
4
5
6
7
>>> try:
>>>     raise SyntaxError
>>> except ValueError:
>>>     print 'value error'
>>> except SyntaxError:
>>>     print 'syntax error'
syntax error

Выражения else и finally являются необязательными.

Вот список встроенных исключений Python + HTTP (определены в web2py)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
BaseException
 +-- HTTP (defined by web2py)
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- Exception
      +-- GeneratorExit
      +-- StopIteration
      +-- StandardError
      |    +-- ArithmeticError
      |    |    +-- FloatingPointError
      |    |    +-- OverflowError
      |    |    +-- ZeroDivisionError
      |    +-- AssertionError
      |    +-- AttributeError
      |    +-- EnvironmentError
      |    |    +-- IOError
      |    |    +-- OSError
      |    |         +-- WindowsError (Windows)
      |    |         +-- VMSError (VMS)
      |    +-- EOFError
      |    +-- ImportError
      |    +-- LookupError
      |    |    +-- IndexError
      |    |    +-- KeyError
      |    +-- MemoryError
      |    +-- NameError
      |    |    +-- UnboundLocalError
      |    +-- ReferenceError
      |    +-- RuntimeError
      |    |    +-- NotImplementedError
      |    +-- SyntaxError
      |    |    +-- IndentationError
      |    |         +-- TabError
      |    +-- SystemError
      |    +-- TypeError
      |    +-- ValueError
      |    |    +-- UnicodeError
      |    |         +-- UnicodeDecodeError
      |    |         +-- UnicodeEncodeError
      |    |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning

Для получения подробного описания каждого из них, обратитесь к официальной документации по Python.

web2py выставляет только одно новое исключение, вызываемое HTTP. Когда исключение поднимается, то оно вызывает программу для возврата HTTP страницы ошибки (более подробное описание в Главе 4).

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

Функции def...return

def
return

Функции объявляются с помощью def. Вот типичная функция Python:

1
2
3
4
>>> def f(a, b):
        return a + b
>>> print f(4, 2)
6

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

Функции обладают первой синтаксической особенностью кода, описанной в этой главе, которая вводит понятие область видимости (scope), или пространство имен (namespace). В приведенном выше примере, идентификаторы a и b не определены вне области видимости функции f:

1
2
3
4
5
6
7
8
9
>>> def f(a):
        return a + 1
>>> print f(1)
2
>>> print a
Traceback (most recent call last):
  File "<pyshell#22>", line 1, in <module>
    print a
NameError: name 'a' is not defined

Идентификаторы, определенные за пределами области видимости функции доступны внутри функции; понаблюдайте, как идентификатор a обрабатывается в следующем коде:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
>>> a = 1
>>> def f(b):
        return a + b
>>> print f(1)
2
>>> a = 2
>>> print f(1) # new value of a is used
3
>>> a = 1 # reset a
>>> def g(b):
        a = 2 # creates a new local a
        return a + b
>>> print g(2)
4
>>> print a # global a is unchanged
1

Если a изменяется, последующие вызовы функции будут использовать новое значение глобальной переменной a потому что определение функции связывает место хранения идентификатора a, а не собственно значение a в момент объявления функции; Однако, если a присваивается к внутренней функции g, то глобальная переменная a не затрагивается, потому что новая локальная переменная a скрывает глобальное значение. Отправка ссылки во внешнюю область видимости может быть использована в создании замыканий (closures):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
>>> def f(x):
        def g(y):
            return x * y
        return g
>>> doubler = f(2) # doubler is new function
>>> tripler = f(3) # tripler is new function
>>> quadrupler = f(4) # quadrupler is new function
>>> print doubler(5)
10
>>> print tripler(5)
15
>>> print quadrupler(5)
20

Функция f создает новые функции; и обратите внимание, что область видимости имени g является полностью внутренней по отношению к f. Замыкания являются чрезвычайно мощным приемом программирования.

Аргументы функций могут иметь значения по умолчанию, и могут возвращать несколько результатов:

1
2
3
4
5
6
7
>>> def f(a, b=2):
        return a + b, a - b
>>> x, y = f(5)
>>> print x
7
>>> print y
3

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

1
2
3
4
5
6
7
>>> def f(a, b=2):
        return a + b, a - b
>>> x, y = f(b=5, a=2)
>>> print x
7
>>> print y
-3

Функции могут также принимать во время выполнения переменное число аргументов:

1
2
3
4
5
6
7
>>> def f(*a, **b):
        return a, b
>>> x, y = f(3, 'hello', c=4, test='world')
>>> print x
(3, 'hello')
>>> print y
{'c':4, 'test':'world'}

Здесь не передаваемые по имени аргументы (3, 'hello') хранятся в кортеже a, а переданные по имени аргументы (c и test) хранятся в словаре b.

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

1
2
3
4
5
>>> def f(a, b):
        return a + b
>>> c = (1, 2)
>>> print f(*c)
3

Словарь также может быть распакован с подставкой аргументов по ключевым словам:

1
2
3
4
5
>>> def f(a, b):
        return a + b
>>> c = {'a':1, 'b':2}
>>> print f(**c)
3

Функция lambda

lambda

Функция lambda дает возможность очень легко создать короткую безымянную функцию:

1
2
3
>>> a = lambda b: b + 2
>>> print a(3)
5

Выражение "lambda [a]:[b]" буквально читается как "функция с аргументами [a] возвращает [b]". Выражение lambda само по себе безымянное, но функция приобретает имя при присвоении идентификатору a. Правила видимости для def применимы также и к функции lambda, и фактически вышеприведенный код, в отношении a, идентичен коду функции, заданной с использованием def:

1
2
3
4
>>> def a(b):
        return b + 2
>>> print a(3)
5

Единственная пользой от lambda является краткость; тем не менее, краткость может быть очень удобна в определенных ситуациях. Рассмотрим функцию называемую map которая применяет функцию для всех элементов в списке, создавая новый список:

1
2
3
>>> a = [1, 7, 2, 5, 4, 8]
>>> map(lambda x: x + 2, a)
[3, 9, 4, 7, 6, 10]

Этот код был бы в два раза больше, если бы def был использован вместо lambda. Основным недостатком lambda является то, что (в реализации Python) синтаксис допускает только одно выражение; Тем не менее, для более длинных функций, может быть использован def и лишние затраты на предоставление имени функции уменьшаются по мере роста длины функции. Просто как и def, lambda функция может быть использована для curry функций: новая функция может быть создана путем оборачивания существующих функций таким образом, что новая функция будет нести различный набор аргументов:

1
2
3
4
>>> def f(a, b): return a + b
>>> g = lambda a: f(a, 3)
>>> g(2)
5

Есть много ситуаций, когда currying полезен, но один из них является непосредственно полезным в web2py: кэширование. Предположим, у вас есть дорогая функцию, которая проверяет, является ли ее аргумент первичным:

1
2
3
4
5
def isprime(number):
    for p in range(2, number):
        if (number % p) == 0:
            return False
    return True

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

Предположим, что у вас есть функция кэширования cache.ram которая принимает три аргумента: ключ, функция и число секунд.

1
value = cache.ram('key', f, 60)

В первый раз, когда она вызывается, она вызывает функцию f(), сохраняет в памяти выходные данных в словаре (допустим "d"), и возвращает его так, что значение:

1
value = d['key']=f()

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

1
value = d['key']

Как бы вы кэшировали вывод функции isprime для любого входа? А вот как:

1
2
3
4
5
6
>>> number = 7
>>> seconds = 60
>>> print cache.ram(str(number), lambda: isprime(number), seconds)
True
>>> print cache.ram(str(number), lambda: isprime(number), seconds)
True

Выход всегда одинаковый, но в первый раз вызывается cache.ram, вызывается isprime; второй раз это не так.

Python функции, созданные при помощи def или lambda, позволяют выполнить рефакторинг существующих функций с точки зрения другого набора аргументов. cache.ram и cache.disk являются web2py функциями кэширования.

Классы class

class

Поскольку Python является динамическим типизированным, классы Python и объекты могут показаться странными. На самом деле, вам не нужно определять переменные-члены (атрибуты) при объявлении класса, и различные экземпляры одного класса могут иметь различные атрибуты. Атрибуты, как правило, связаны с экземпляром класса, а не самим классом (за исключением случаев объявления "атрибутов класса", которые являются такими же, как и "static member variables" в C++/Java).

Вот пример:

1
2
3
4
5
>>> class MyClass(object): pass
>>> myinstance = MyClass()
>>> myinstance.myvariable = 3
>>> print myinstance.myvariable
3

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

Вот более сложный класс:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
>>> class MyClass(object):
>>>    z = 2
>>>    def __init__(self, a, b):
>>>        self.x = a
>>>        self.y = b
>>>    def add(self):
>>>        return self.x + self.y + self.z
>>> myinstance = MyClass(3, 4)
>>> print myinstance.add()
9

Функции, объявленные внутри класса, являются методами. Некоторые методы имеют специальные зарезервированные имена. Например, __init__ является конструктором класса. Все переменные являются локальными переменными метода, кроме переменных, объявленных вне метода. Например, z это переменная класса, она эквивалентна статической переменной-члену в C++, которая имеет одинаковое значение для всех экземпляров класса.

Обратите внимание, что __init__ принимает 3 аргумента и add принимает один аргумент, и все же мы вызываем их с 2 и 0 соответственно аргументами. Первый аргумент, по соглашению, представляет собой локальное имя, используемое внутри метода, чтобы обратиться к текущему объекту. Здесь мы используем self для ссылки на текущий объект, но мы могли бы использовать и любое другое имя. self играет ту же роль, что и *this в C++ или this в Java, но self не является зарезервированным ключевым словом.

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

Специальные атрибуты, методы и операторы

Атрибуты класса, методы и операторы, которые начинаются с двойного подчеркивания, как правило, предназначены для частного использования (т.е. для использования внутри, а не вне класса), хотя это соглашение, которое не исполняется интерпретатором.

Некоторые из них являются зарезервированными ключевыми словами и имеют особое значение.

Здесь, в качестве примера, три из них:

  • __len__
  • __getitem__
  • __setitem__

Они могут быть использованы, например, для создания объекта контейнера, который действует как список:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
>>> class MyList(object):
>>>     def __init__(self, *a): self.a = list(a)
>>>     def __len__(self): return len(self.a)
>>>     def __getitem__(self, i): return self.a[i]
>>>     def __setitem__(self, i, j): self.a[i] = j
>>> b = MyList(3, 4, 5)
>>> print b[1]
4
>>> b.a[1] = 7
>>> print b.a
[3, 7, 5]

Другие специальные операторы включают в себя __getattr__ и __setattr__, которые определяют методы получения и установки атрибутов для класса, а также __sum__ и __sub__, который перегружают арифметические операторы. Для использования этих операторов мы отсылаем читателя к более продвинутым книгам по этой теме. Мы уже упоминали специальные операторы __str__ и __repr__.

Чтение/запись данных в файл

file.read
file.write

В Python вы можете открыть и записать в файл используюя:

1
2
3
>>> file = open('myfile.txt', 'w')
>>> file.write('hello world')
>>> file.close()

Кроме того, вы можете прочитать обратно из файла:

1
2
3
>>> file = open('myfile.txt', 'r')
>>> print file.read()
hello world

В качестве альтернативы, вы можете прочитать файл в двоичном режиме с атрибутом "rb", записать в двоичном режиме с "wb", и открыть файл в режим добавления с "a", используя стандартную C нотацию.

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

file.seek

Вы можете прочитать обратно из файла с методом read

1
2
3
>>> print file.seek(6)
>>> print file.read()
world

и вы можете закрыть файл с методом:

1
>>> file.close()

В стандартном дистрибутиве Python, который известен как CPython, переменные обладают счетчиком ссылок, включая и те, которые содержат дескриптор файла, таким образом CPython знает, что когда счетчик ссылок открытого дескриптора файла уменьшается до нуля, то файл может быть закрыт и переменная освобождена. Однако в других реализациях Python, таких как PyPy, вместо подсчета ссылок используется сборщик мусора, и это означает, что вполне возможно, что там может накопиться слишком много открытых дескрипторов файлов в одно время, в результате чего, по ошибке, перед сборщиком мусора появляется шанс закрыть и освободить их всех. Поэтому лучше явно закрывать дескрипторы файла, когда они больше не нужны. web2py предоставляет две вспомогательные функции read_file() и write_file() внутри gluon.fileutils, пространство имен, инкапсулирует доступ к файлам и убеждается, что для дескриптора файла используются правильное закрытие.

При использовании web2py, вы не знаете, где находится текущий каталог, потому что это зависит от того, как настроен web2py. Переменная request.folder содержит путь к текущему приложению. Пути могут быть объединены с помощью команды os.path.join, обсуждаемой ниже.

Функции exec, eval

exec
eval

В отличие от Java, Python является поистине интерпретируемым языком. Это означает, что он имеет возможность исполнения операторов Python, хранящихся в строках. Например:

1
2
3
>>> a = "print 'hello world'"
>>> exec(a)
'hello world'

Что сейчас произошло? Функция exec говорит интерпретатору вызвать саму себя и выполнить содержимое строки, переданную в качестве аргумента. Кроме того, можно выполнить содержимое строки в пределах определенного символами контекста в словаре:

1
2
3
4
>>> a = "print b"
>>> c = dict(b=3)
>>> exec(a, {}, c)
3

Здесь интерпретатор, при выполнении строки a, видит символы, определенные в c (b в примере), но не видит самих c или a. В этом состоит отличие, чем ограничивается среда, поскольку exec не ограничивает то, что внутренний код может сделать; он просто определяет набор переменных видимых для кода.

Родственная функция eval, которая работает очень похоже на exec за исключением того, она ожидает оценку аргумента до значения, и возвращает это значение.

1
2
3
4
>>> a = "3*4"
>>> b = eval(a)
>>> print b
12

Импорт модулей import

import
random
Реальная мощь Python заключается в его библиотеке модулей. Они обеспечивают большой и последовательный набор интерфейсов прикладного программирования (API) для многих системных библиотек (зачастую путем, независящим от операционной системы).

Например, если вам нужно использовать генератор случайных чисел, вы можете сделать:

1
2
3
>>> import random
>>> print random.randint(0, 9)
5

Данный код печатает случайное целое число от 0 до 9 (включая 9), в результате получается 5 в примере. Функция randint определяется в модуле random. Кроме того, можно импортировать объект из модуля в текущем пространстве имен:

1
2
>>> from random import randint
>>> print randint(0, 9)

или импортировать все объекты из модуля в текущем пространстве имен:

1
2
>>> from random import *
>>> print randint(0, 9)

или импортировать все с определением нового пространства имен:

1
2
>>> import random as myrand
>>> print myrand.randint(0, 9)

В остальной части этой книги, мы будем в основном использовать объекты, определенные в модулях os, sys, datetime, time и cPickle.

Все web2py объекты доступны через модуль под названием gluon, который является предметом обсуждения в последующих главах. Внутренне, web2py использует множество модулей Python (например thread),но редко возникает необходимость обращаться к ним напрямую.

В следующих подразделах мы рассмотрим те модули, которые наиболее полезны.

Модуль os

os
os.path.join
os.unlink

Этот модуль обеспечивает интерфейс для API операционной системы. Например:

1
2
3
>>> import os
>>> os.chdir('..')
>>> os.unlink('filename_to_be_deleted')
Некоторые функции из модуля os, такие как chdir, НЕ ДОЛЖНЫ использоваться в web2py, потому что они не потокобезопасные.

Функция os.path.join очень полезна; она позволяет выполнить конкатенацию путей независимым от операционной системы способом:

1
2
3
4
>>> import os
>>> a = os.path.join('path', 'sub_path')
>>> print a
path/sub_path

Системные переменные окружения могут быть доступны через:

1
>>> print os.environ

который представляет собой словарь только для чтения.

Модуль sys

sys
sys.path

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

1
2
>>> import sys
>>> sys.path.append('path/to/my/modules')

При запуске web2py, Python становится постоянным жильцом в памяти, и есть только один sys.path, в то время как есть много потоков, обслуживающих HTTP запросы. Для того, чтобы избежать утечки памяти, лучше всего проверить существует ли путь перед добавлением:

1
2
3
>>> path = 'path/to/my/modules'
>>> if not path in sys.path:
        sys.path.append(path)

Модуль datetime

date
datetime
time

Использование модуля даты и времени лучше всего иллюстрируется некоторыми примерами:

1
2
3
4
5
>>> import datetime
>>> print datetime.datetime.today()
2008-07-04 14:03:90
>>> print datetime.date.today()
2008-07-04

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

1
2
3
>>> import datetime
>>> print datetime.datetime.utcnow()
2008-07-04 14:03:90

Модуль datetime содержит различные классы: date, datetime, time и timedelta. Разница между двумя date или двумя datetime или двумя time объектами составляет timedelta:

1
2
3
4
5
>>> a = datetime.datetime(2008, 1, 1, 20, 30)
>>> b = datetime.datetime(2008, 1, 2, 20, 30)
>>> c = b - a
>>> print c.days
1

В web2py, date и datetime используются для хранения соответствующих типов SQL при передаче или возвращении из базы данных.

Модуль time

time

Модуль time отличается от date и datetime потому что он предоставляет время в секундах от эпохи (начиная с 1970).

1
2
3
>>> import time
>>> t = time.time()
1215138737.571

Обратитесь к документации по Python для функций преобразования между временем в секундах и временем в datetime.

Модуль cPickle

cPickle

Это очень мощный модуль. Он предоставляет функции, которые могут сериализовать практически любой объект Python, включая автореферентные объекты. Например, давайте создадим таинственный объект:

1
2
3
4
>>> class MyClass(object): pass
>>> myinstance = MyClass()
>>> myinstance.x = 'something'
>>> a = [1 ,2, {'hello':'world'}, [3, 4, [myinstance]]]

а теперь:

1
2
3
>>> import cPickle
>>> b = cPickle.dumps(a)
>>> c = cPickle.loads(b)

В этом примере, b является строковым представлением a, c является копией a, генерируемой при десериализации b.

cPickle может также сериализовать в файл и десериализовать из файла:

1
2
>>> cPickle.dump(a, open('myfile.pickle', 'wb'))
>>> c = cPickle.load(open('myfile.pickle', 'rb'))
 top