Chapter 2: Язык программирования Python
Язык программирования Python
О Python
Python является универсальным языком программирования высокого уровня. Его философия построения придает особое значение продуктивности программиста и читаемости кода. Он имеет минимальный базовый синтаксис с небольшим набором основных команд и простой семантикой, но он также имеет большую и всеобъемлющую стандартную библиотеку, включая интерфейс прикладного программирования - Application Programming Interface (API)
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.
Начало работы
Бинарные дистрибутивы web2py для Microsoft Windows или Apple OS X поставляются в комплекте с интерпретатором Python, встроенным в файл дистрибутива.
Вы можете запустить его на Windows, с помощью следующей команды (наберите в командной строке DOS):
web2py.exe -S welcome
На Apple OS X, введите следующий тип команды в окне терминала (если вы находитесь в одной папке с web2py.app):
./web2py.app/Contents/MacOS/web2py -S welcome
На Linux или другой Unix системе, есть вероятность, что Python уже установлен. Если да, то ведите в оболочке командной строки:
python web2py.py -S welcome
Если у вас нет установленного Python 2.7, вам придется скачать и установить его перед запуском web2py.
Опция командной строки -S welcome
предписывает web2py запустить интерактивную оболочку, как если бы эти команды были выполнены в контроллере для приложения welcome, скаффолдингового приложения web2py. Это открывает почти все web2py классы, объекты и функции для вас. Это единственное различие между интерактивной командной строки web2py и нормальной командной строки Python.
Интерфейс администратора также предоставляет веб-оболочку для каждого приложения. Вы можете получить доступ к одному приложению "welcome" следующим образом.
http://127.0.0.1:8000/admin/shell/index/welcome
Вы можете попробовать все примеры в этой главе с помощью обычной оболочки или веб-оболочки.
Команды help, dir
Язык Python предоставляет две команды для получения документации об объектах, определенных в текущей области видимости, обе являются встроенные и определяемые пользователем.
Мы можем запросить help
для любого объекта, например для цифры "1":
>>> 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
:
>>> dir(1)
['__abs__', ..., '__xor__']
Типы данных
Python является языком с динамическим определением типа, это означает, что переменные не имеют типа и, следовательно, не должны быть объявлены. Значения переменной, с другой стороны, имеют определенный тип. Вы можете запросить у переменной тип значения, которое она содержит:
>>> 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
Python поддерживает использование двух различных типов строк: ASCII строки и Unicode строки. ASCII строки выделяются при помощи одинарных кавычек вида '...', "..." или тройных кавычек вида '..' или """...""". Тройными кавычками выделяются многострочные строки. Юникод строки начинаются со знака u
перед кавычками, за которым следует строка, содержащая символы в формате Юникод. Строка в формате Юникод может быть преобразована в строку ASCII посредством выбранного способа кодирования, например:
>>> a = 'this is an ASCII string'
>>> b = u'This is a Unicode string'
>>> a = b.encode('utf8')
После выполнения этих трех команд, полученная переменная a
будет представлять ASCII строку, содержащую символы в UTF-8 кодировке. По проекту, web2py использует внутренне кодированные UTF-8 строки.
Кроме того, можно записать переменные внутрь строки различными способами:
>>> 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
. Эти две команды очень похожи, но производят несколько иной вывод. Например:
>>> for i in [3, 'hello']:
print str(i), repr(i)
3 3
hello 'hello'
Для определенных пользователем классов, функции str
и repr
могут быть определены/переопределены с помощью специальных операторов __str__
and __repr__
. Они вкратце описаны в дальнейшем; для получения дополнительной информации, обратитесь к официальной Python документации [pydocs] . repr
всегда является значением по умолчанию.
Другой важной характеристикой строк Python является то, что они, как и списки, являются итерируемыми объектами.
>>> for i in 'hello':
print i
h
e
l
l
o
Списки list
Основными методами списка Python являются методы добавления, вставки и удаления:
>>> 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
Списки могут быть разрезаны:
>>> print a[:3]
[2, 7, 3]
>>> print a[1:]
[7, 3, 8]
>>> print a[-2:]
[3, 8]
и объединены:
>>> a = [2, 3]
>>> b = [5, 6]
>>> print a + b
[2, 3, 5, 6]
Списки являются итерируемыми, вы можете использовать их в цикле:
>>> a = [1, 2, 3]
>>> for i in a:
print i
1
2
3
Элементы списка не обязательно должны быть одного и того же типа; они могут быть объектами Python любого типа.
Существует очень распространенная ситуация, которая может быть использована для осмысления списка. Рассмотрим следующий код:
>>> a = [1,2,3,4,5]
>>> b = []
>>> for x in a:
if x % 2 == 0:
b.append(x * 3)
>>> b
[6, 12]
Этот код явно обрабатывает список элементов, выбирает и изменяет подмножество во входном списке, и создает новый результирующий список, и этот код может быть полностью заменен следующим осмысленным списком:
>>> a = [1,2,3,4,5]
>>> b = [x * 3 for x in a if x % 2 == 0]
>>> b
[6, 12]
Кортежи tuple
Кортеж похож на список, но его размер и элементы являются неизменяемыми, в то время как в списке они изменяемые. Если элемент кортежа является объектом, то атрибуты объекта могут изменяться. Кортеж выделяется круглыми скобками.
>>> a = (1, 2, 3)
То, что работает для списка:
>>> a = [1, 2, 3]
>>> a[1] = 5
>>> print a
[1, 5, 3]
Не работает для кортежа - присвоение значения элементу:
>>> 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
Кортеж, как и список, является итерируемым объектом. Обратите внимание, что кортеж, состоящий из одного элемента должен обязательно содержать запятую, как показано ниже:
>>> a = (1)
>>> print type(a)
<type 'int'>
>>> a = (1,)
>>> print type(a)
<type 'tuple'>
Кортежи являются очень полезными для эффективной упаковки объектов в силу их неизменности, и скобки часто бывают необязательными:
>>> a = 2, 3, 'hello'
>>> x, y, z = a
>>> print x
2
>>> print z
hello
Словари dict
Python словари dict
представляет собой хэш-таблицу, которая связывает объект ключа с объектом значения. Например:
>>> a = {'k':'v', 'k2':3}
>>> a['k']
v
>>> a['k2']
3
>>> a.has_key('k')
True
>>> a.has_key('v')
False
Ключи могут быть любыми объектами хэшируемого типа (int, string, или любой объект, чей класс реализует метод __hash__
). Значения могут быть любого типа. Различные ключи и значения в одном и том же словаре не обязательно должны быть одного и того же типа. Если ключи состоят из буквенно-цифровых символов, то словарь также может быть объявлен с альтернативным синтаксисом:
>>> a = dict(k='v', h2=3)
>>> a['k']
v
>>> print a
{'k':'v', 'h2':3}
Полезными методами являются has_key
, keys
, values
и items
:
>>> 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
:
>>> 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
для преобразования объектов в целое число, и использует это число для определения места хранения значения.
>>> hash("hello world")
-1500746465
Об отступах
Python использует отступы для выделения блоков кода. Блок начинается со строки, заканчивающейся двоеточием, и продолжается для всех строк, которые имеют такой же или более высокий отступ на следующей строке. Например:
>>> i = 0
>>> while i < 3:
>>> print i
>>> i = i + 1
>>>
0
1
2
Общепринято использовать четыре пробела для каждого уровня отступа. Это хорошая политика, чтобы не перепутать табы с пробелами, которые могут привести к (невидимой) путанице.
Цикл for...in
В Python, вы можете использовать цикл над итерируемыми объектами:
>>> a = [0, 1, 'hello', 'python']
>>> for i in a:
print i
0
1
hello
python
Одним из распространенных операторов является оператор xrange
, который генерирует итерируемый диапазон без сохранения всего списка элементов.
>>> for i in xrange(0, 4):
print i
0
1
2
3
Это эквивалентно синтаксису в C/C++/C#/Java:
for(int i=0; i<4; i=i+1) { print(i); }
Еще одна полезная команда enumerate
, которая пронумерует элементы во время цикла:
>>> 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
>>> for i in [1, 2, 3]:
print i
break
1
Вы можете перескочить к следующей итерации цикла, не выполняя весь блок кода используя continue
>>> for i in [1, 2, 3]:
print i
continue
print 'test'
1
2
3
Цикл while
Цикл while
в Python работает так же, как это делается во многих других языках программирования, данный цикл выполняет итерацию неопределенное количество раз с проверку условий перед каждой итерацией. Если условия ложные - False
, то цикл завершается.
>>> i = 0
>>> while i < 10:
i = i + 1
>>> print i
10
Это не loop...until
конструкция в Python.
Условный оператор if...elif...else
>>> 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
.
>>> for i in range(3):
>>> if i == 0 or (i == 1 and i + 1 == 2):
>>> print '0 or 1'
Обработка исключений try...except...else...finally
>>> 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
для всевозможных исключений:
>>> try:
>>> raise SyntaxError
>>> except ValueError:
>>> print 'value error'
>>> except SyntaxError:
>>> print 'syntax error'
syntax error
Выражения else
и finally
являются необязательными.
Вот список встроенных исключений Python + HTTP (определены в web2py)
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
. Вот типичная функция Python:
>>> def f(a, b):
return a + b
>>> print f(4, 2)
6
Здесь нет необходимости (или способа) указывать тип принимаемых аргументов или возвращаемого типа(ов). В этом примере для функции f
определено, что она может принимать два аргумента.
Функции обладают первой синтаксической особенностью кода, описанной в этой главе, которая вводит понятие область видимости (scope), или пространство имен (namespace). В приведенном выше примере, идентификаторы a
и b
не определены вне области видимости функции f
:
>>> 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
обрабатывается в следующем коде:
>>> 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):
>>> 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
. Замыкания являются чрезвычайно мощным приемом программирования.
Аргументы функций могут иметь значения по умолчанию, и могут возвращать несколько результатов:
>>> def f(a, b=2):
return a + b, a - b
>>> x, y = f(5)
>>> print x
7
>>> print y
3
Аргументы функции могут быть переданы явно по имени, и это означает, что порядок аргументов, указанный в месте вызова может отличаться от порядка аргументов, с которым функция была определена:
>>> def f(a, b=2):
return a + b, a - b
>>> x, y = f(b=5, a=2)
>>> print x
7
>>> print y
-3
Функции могут также принимать во время выполнения переменное число аргументов:
>>> 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
.
В противном случае, список или кортеж может быть передан в функцию, которая требует позиционное задание отдельных аргументов, в распакованном виде:
>>> def f(a, b):
return a + b
>>> c = (1, 2)
>>> print f(*c)
3
Словарь также может быть распакован с подставкой аргументов по ключевым словам:
>>> def f(a, b):
return a + b
>>> c = {'a':1, 'b':2}
>>> print f(**c)
3
Функция lambda
Функция lambda
дает возможность очень легко создать короткую безымянную функцию:
>>> a = lambda b: b + 2
>>> print a(3)
5
Выражение "lambda
[a]:[b]" буквально читается как "функция с аргументами [a] возвращает [b]". Выражение lambda
само по себе безымянное, но функция приобретает имя при присвоении идентификатору a
. Правила видимости для def
применимы также и к функции lambda
, и фактически вышеприведенный код, в отношении a
, идентичен коду функции, заданной с использованием def
:
>>> def a(b):
return b + 2
>>> print a(3)
5
Единственная пользой от lambda
является краткость; тем не менее, краткость может быть очень удобна в определенных ситуациях. Рассмотрим функцию называемую map
которая применяет функцию для всех элементов в списке, создавая новый список:
>>> 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 функций: новая функция может быть создана путем оборачивания существующих функций таким образом, что новая функция будет нести различный набор аргументов:
>>> def f(a, b): return a + b
>>> g = lambda a: f(a, 3)
>>> g(2)
5
Есть много ситуаций, когда currying полезен, но один из них является непосредственно полезным в web2py: кэширование. Предположим, у вас есть дорогая функцию, которая проверяет, является ли ее аргумент первичным:
def isprime(number):
for p in range(2, number):
if (number % p) == 0:
return False
return True
Эта функция, что очевидно, потребляет время.
Предположим, что у вас есть функция кэширования cache.ram
которая принимает три аргумента: ключ, функция и число секунд.
value = cache.ram('key', f, 60)
В первый раз, когда она вызывается, она вызывает функцию f()
, сохраняет в памяти выходные данных в словаре (допустим "d"), и возвращает его так, что значение:
value = d['key']=f()
Во второй раз когда она вызывается, если ключ находится в словаре и не старше, чем количество указанных секунд (60), она возвращает соответствующее значение без выполнения вызова функции.
value = d['key']
Как бы вы кэшировали вывод функции isprime для любого входа? А вот как:
>>> 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
Поскольку Python является динамическим типизированным, классы Python и объекты могут показаться странными. На самом деле, вам не нужно определять переменные-члены (атрибуты) при объявлении класса, и различные экземпляры одного класса могут иметь различные атрибуты. Атрибуты, как правило, связаны с экземпляром класса, а не самим классом (за исключением случаев объявления "атрибутов класса", которые являются такими же, как и "static member variables" в C++/Java).
Вот пример:
>>> class MyClass(object): pass
>>> myinstance = MyClass()
>>> myinstance.myvariable = 3
>>> print myinstance.myvariable
3
Обратите внимание на то, что pass
является ничего не делающей командой. В этом случае используется для определения класса MyClass
что не содержит ничего. MyClass()
вызывает конструктор класса (в данном случае конструктор по умолчанию) и возвращает объект, экземпляр класса. (object)
в определении класса указывает на то, что наш класс расширяет встроенный object
класса. Это не является обязательным, но это хорошая практика.
Вот более сложный класс:
>>> 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__
Они могут быть использованы, например, для создания объекта контейнера, который действует как список:
>>> 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__
.
Чтение/запись данных в файл
В Python вы можете открыть и записать в файл используюя:
>>> file = open('myfile.txt', 'w')
>>> file.write('hello world')
>>> file.close()
Кроме того, вы можете прочитать обратно из файла:
>>> file = open('myfile.txt', 'r')
>>> print file.read()
hello world
В качестве альтернативы, вы можете прочитать файл в двоичном режиме с атрибутом "rb", записать в двоичном режиме с "wb", и открыть файл в режим добавления с "a", используя стандартную C нотацию.
Команда read
принимает необязательный аргумент, который указывает количество байтов. Вы также можете перейти в любое место в файле с помощью seek
.
Вы можете прочитать обратно из файла с методом read
>>> print file.seek(6)
>>> print file.read()
world
и вы можете закрыть файл с методом:
>>> file.close()
В стандартном дистрибутиве Python, который известен как CPython, переменные обладают счетчиком ссылок, включая и те, которые содержат дескриптор файла, таким образом CPython знает, что когда счетчик ссылок открытого дескриптора файла уменьшается до нуля, то файл может быть закрыт и переменная освобождена. Однако в других реализациях Python, таких как PyPy, вместо подсчета ссылок используется сборщик мусора, и это означает, что вполне возможно, что там может накопиться слишком много открытых дескрипторов файлов в одно время, в результате чего, по ошибке, перед сборщиком мусора появляется шанс закрыть и освободить их всех. Поэтому лучше явно закрывать дескрипторы файла, когда они больше не нужны. web2py предоставляет две вспомогательные функции read_file()
и write_file()
внутри gluon.fileutils
, пространство имен, инкапсулирует доступ к файлам и убеждается, что для дескриптора файла используются правильное закрытие.
При использовании web2py, вы не знаете, где находится текущий каталог, потому что это зависит от того, как настроен web2py. Переменная
request.folder
содержит путь к текущему приложению. Пути могут быть объединены с помощью командыos.path.join
, обсуждаемой ниже.
Функции exec
, eval
В отличие от Java, Python является поистине интерпретируемым языком. Это означает, что он имеет возможность исполнения операторов Python, хранящихся в строках. Например:
>>> a = "print 'hello world'"
>>> exec(a)
'hello world'
Что сейчас произошло? Функция exec
говорит интерпретатору вызвать саму себя и выполнить содержимое строки, переданную в качестве аргумента. Кроме того, можно выполнить содержимое строки в пределах определенного символами контекста в словаре:
>>> a = "print b"
>>> c = dict(b=3)
>>> exec(a, {}, c)
3
Здесь интерпретатор, при выполнении строки a
, видит символы, определенные в c
(b
в примере), но не видит самих c
или a
. В этом состоит отличие, чем ограничивается среда, поскольку exec
не ограничивает то, что внутренний код может сделать; он просто определяет набор переменных видимых для кода.
Родственная функция eval
, которая работает очень похоже на exec
за исключением того, она ожидает оценку аргумента до значения, и возвращает это значение.
>>> a = "3*4"
>>> b = eval(a)
>>> print b
12
Импорт модулей import
Например, если вам нужно использовать генератор случайных чисел, вы можете сделать:
>>> import random
>>> print random.randint(0, 9)
5
Данный код печатает случайное целое число от 0 до 9 (включая 9), в результате получается 5 в примере. Функция randint
определяется в модуле random
. Кроме того, можно импортировать объект из модуля в текущем пространстве имен:
>>> from random import randint
>>> print randint(0, 9)
или импортировать все объекты из модуля в текущем пространстве имен:
>>> from random import *
>>> print randint(0, 9)
или импортировать все с определением нового пространства имен:
>>> import random as myrand
>>> print myrand.randint(0, 9)
В остальной части этой книги, мы будем в основном использовать объекты, определенные в модулях os
, sys
, datetime
, time
и cPickle
.
Все web2py объекты доступны через модуль под названием
gluon
, который является предметом обсуждения в последующих главах. Внутренне, web2py использует множество модулей Python (напримерthread
),но редко возникает необходимость обращаться к ним напрямую.
В следующих подразделах мы рассмотрим те модули, которые наиболее полезны.
Модуль os
Этот модуль обеспечивает интерфейс для API операционной системы. Например:
>>> import os
>>> os.chdir('..')
>>> os.unlink('filename_to_be_deleted')
Некоторые функции из модуля
os
, такие какchdir
, НЕ ДОЛЖНЫ использоваться в web2py, потому что они не потокобезопасные.
Функция os.path.join
очень полезна; она позволяет выполнить конкатенацию путей независимым от операционной системы способом:
>>> import os
>>> a = os.path.join('path', 'sub_path')
>>> print a
path/sub_path
Системные переменные окружения могут быть доступны через:
>>> print os.environ
который представляет собой словарь только для чтения.
Модуль sys
Модуль sys
содержит множество переменных и функций, но тот, который мы используем больше всего это sys.path
. Он содержит список путей, где Python производит поиск для модулей. Когда мы пытаемся импортировать модуль, Python ищет его во всех папках, перечисленных в sys.path
. Если вы установили дополнительные модули в каком-то месте и хотите чтобы Python найшел их, вам нужно добавить путь к этому месту в sys.path
.
>>> import sys
>>> sys.path.append('path/to/my/modules')
При запуске web2py, Python становится постоянным жильцом в памяти, и есть только один sys.path
, в то время как есть много потоков, обслуживающих HTTP запросы. Для того, чтобы избежать утечки памяти, лучше всего проверить существует ли путь перед добавлением:
>>> path = 'path/to/my/modules'
>>> if not path in sys.path:
sys.path.append(path)
Модуль datetime
Использование модуля даты и времени лучше всего иллюстрируется некоторыми примерами:
>>> import datetime
>>> print datetime.datetime.today()
2008-07-04 14:03:90
>>> print datetime.date.today()
2008-07-04
Иногда вам может понадобиться данные для выставления времени на основе времени UTC, а не по местному времени. В этом случае вы можете использовать следующую функцию:
>>> import datetime
>>> print datetime.datetime.utcnow()
2008-07-04 14:03:90
Модуль datetime содержит различные классы: date, datetime, time и timedelta. Разница между двумя date или двумя datetime или двумя time объектами составляет timedelta:
>>> 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 отличается от date
и datetime
потому что он предоставляет время в секундах от эпохи (начиная с 1970).
>>> import time
>>> t = time.time()
1215138737.571
Обратитесь к документации по Python для функций преобразования между временем в секундах и временем в datetime
.
Модуль cPickle
Это очень мощный модуль. Он предоставляет функции, которые могут сериализовать практически любой объект Python, включая автореферентные объекты. Например, давайте создадим таинственный объект:
>>> class MyClass(object): pass
>>> myinstance = MyClass()
>>> myinstance.x = 'something'
>>> a = [1 ,2, {'hello':'world'}, [3, 4, [myinstance]]]
а теперь:
>>> import cPickle
>>> b = cPickle.dumps(a)
>>> c = cPickle.loads(b)
В этом примере, b
является строковым представлением a
, c
является копией a
, генерируемой при десериализации b
.
cPickle может также сериализовать в файл и десериализовать из файла:
>>> cPickle.dump(a, open('myfile.pickle', 'wb'))
>>> c = cPickle.load(open('myfile.pickle', 'rb'))