Главная страница

Курс лекций по Python. Курс лекций по Python (ru). 1. Лекция Введение в программирование на языке Python


Скачать 0.77 Mb.
Название1. Лекция Введение в программирование на языке Python
АнкорКурс лекций по Python
Дата16.09.2022
Размер0.77 Mb.
Формат файлаpdf
Имя файлаКурс лекций по Python (ru).pdf
ТипДокументы
#679637
страница14 из 14
1   ...   6   7   8   9   10   11   12   13   14
Library). Численные расчеты можно выполнять с помощью пакета Numeric и т.д.
Pychecker
Одним из наиболее интересных инструментов для анализа исходного кода Python программы является Pychecker. Как и lint для языка C, Pychecker позволяет выявлять слабости в исходном коде на языке Python. Можно рассмотреть следующий пример с использованием Pychecker:
Листинг import re, string import re a = «a b c»
def test(x, y):
from string import split a = «x y z»
print split(a) + x test(['d'], 'e')
Pychecker выдаст следующие предупреждения:
Листинг badcode.py:1: Imported module (string) not used badcode.py:2: Imported module (re) not used badcode.py:2: Module (re) re–imported badcode.py:5: Parameter (y) not used
badcode.py:6: Using import and from … import for (string)
badcode.py:7: Local variable (a) shadows global defined on line 3
badcode.py:8: Local variable (a) shadows global defined on line 3
В первой строке импортирован модуль, который далее не применяется, то же самое с модулем re. Кроме того, модуль re импортирован повторно. Другие проблемы с кодом:
параметр y не использован; модуль string применен как в операторе import, так и во from–
import; локальная переменная a затеняет глобальную, которая определена в третьей строке.
Можно переписать этот пример так, чтобы Pychecker выдавал меньше предупреждений:
Листинг import string a = «a b c»
def test(x, y):
a1 = «x y z»
print string.split(a1) + x test(['d'], 'e')
Теперь имеется лишь одно предупреждение:
Листинг goodcode.py:4: Parameter (y) not used
Такое тоже бывает. Программист должен лишь убедиться, что он не сделал ошибки.
Исследование объекта
Даже самые примитивные объекты в языке программирования Python имеют возможности, общие для всех объектов: можно получить их уникальный идентификатор (с помощью функции id()), представление в виде строки — даже в двух вариантах (функции str() и repr()); можно узнать атрибуты объекта с помощью встроенной функции dir() и во многих случаях пользоваться атрибутом __dict__ для доступа к словарю имен объекта. Также можно узнать, сколько других объектов ссылается на данный с помощью функции sys.getrefcount(). Есть еще сборка мусора, которая применяется для освобождения памяти от объектов, которые более не используются, но имеют ссылки друг на друга (циклические ссылки). Сборкой мусора (garbage collection) можно управлять из модуля gc.
Все это подчеркивает тот факт, что объекты в Python существуют не сами по себе, а являются частью системы: они и их отношения строго учитываются интерпретатором.
Сразу же следует оговориться, что Python имеет две стороны интроспекции:
«официальную», которую поддерживает описание языка и многие его реализации, и
«неофициальную», которая использует особенности той или иной реализации. С помощью
«официальных» средств интроспекции можно получить информацию о принадлежности объекта тому или иному классу (функция type()), проверить принадлежность экземпляра классу (isinstance()), отношение наследования между классами (issubclass()), а также получить информацию, о которой говорилось чуть выше. Это как бы приборная доска машины. С помощью «неофициальной» интроспекции (это то, что под капотом) можно получить доступ к чему угодно: к текущему фрейму исполнения и стеку, к байт–коду функции, к некоторым механизмам интерпретатора (от загрузки модулей до полного контроля над внутренней средой исполнения). Сразу же стоит сказать, что этот механизм следует рассматривать (и тем более вносить изменения) очень деликатно: разработчики
языка не гарантируют постоянство этих механизмов от версии к версии, а некоторые полезные модули используют эти механизмы для своих целей. Например, упомянутый ранее ускоритель выполнения Python–кода psyco очень серьезно вмешивается во фреймы исполнения, заменяя их своими объектами. Кроме того, разные реализации Python могут иметь совсем другие внутренние механизмы.
Сказанное стоит подкрепить примерами.
В первом примере исследуется объект с помощью «официальных» средств. В качестве объекта выбрана обычная строка:
Листинг
>>> s = «abcd»
>>> dir(s)
['__add__', '__class__', '__contains__', '__delattr__', '__doc__',
'__eq__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__',
'__getslice__', '__gt__', '__hash__', '__init__', '__le__', '__len__',
'__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__',
'__str__', 'capitalize', 'center', 'count', 'decode',
'encode', 'endswith', 'expandtabs', 'find', 'index', 'isalnum', 'isalpha',
'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust',
'lower', 'lstrip', 'replace', 'rfind', 'rindex', 'rjust', 'rstrip',
'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title',
'translate', 'upper', 'zfill']
>>> id(s)
1075718400
>>> print str(s)
abcd
>>> print repr(s)
'abcd'
>>> type(s)

>>> isinstance(s, basestring)
True
>>> isinstance(s, int)
False
>>> issubclass(str, basestring)
True
«Неофициальные» средства интроспекции в основном работают в области представления объектов в среде интерпретатора. Ниже будет рассмотрено, как главная (на настоящий момент) реализация Python может дать информацию об определенной пользователем функции:
Листинг
>>> def f(x, y=0):
… ""«Function f(x, y)»""
… global s
… return t + x + y


>>> f.secure = 1 # присваивается дополнительный атрибут
>>> f.func_name # имя 'f'
>>> f.func_doc # строка документации 'Function f(x, y)'
>>> f.func_defaults # значения по умолчанию
(0,)
>>> f.func_dict # словарь атрибутов функции
{'secure': 1}
>>> co = f.func_code # кодовый объект
>>> co
", line 1>
Кодовые объекты имеют свои атрибуты:
Листинг
>>> co.co_code # байт–код 't\x00\x00|\x00\x00\x17|\x01\x00\x17Sd\x01\x00S'
>>> co.co_argcount # число аргументов
2
>>> co.co_varnames # имена переменных
('x', 'y')
>>> co.co_consts # константы
(None,)
>>> co.co_names # локальные имена
('t', 'x', 'y')
>>> co.co_name # имя блока кода (в нашем случае — имя функции)
'f'
и так далее. Более правильно использовать для получения всех этих сведений модуль inspect.
Модуль inspect
Основное назначение модуля inspect — давать приложению информацию о модулях,
классах, функциях, трассировочных объектах, фреймах исполнения и кодовых объектах.
Именно модуль inspect позволяет заглянуть «на кухню» интерпретатора Python.
Модуль имеет функции для проверки принадлежности объектов различным типам, с которыми он работает:
Функция Проверяемый тип inspect.isbuiltin Встроенная функция inspect.isclass Класс inspect.iscode Код inspect.isdatadescriptor Описатель данных inspect.isframe Фрейм inspect.isfunction Функция inspect.ismethod Метод inspect.ismethoddescriptor Описатель метода inspect.ismodule Модуль
inspect.isroutine Функция или метод inspect.istraceback Трассировочный объект
Пример:
Листинг
>>> import inspect
>>> inspect.isbuiltin(len)
True
>>> inspect.isroutine(lambda x: x+1)
True
>>> inspect.ismethod(''.split)
False
>>> inspect.isroutine(''.split)
True
>>> inspect.isbuiltin(''.split)
True
Объект типа модуль появляется в Python–программе благодаря операции импорта. Для получения информации о модуле имеются некоторые функции, а объект–модуль обладает определенными атрибутами, как продемонстрировано ниже:
Листинг
>>> import inspect
>>> inspect.ismodule(inspect)
True
>>> inspect.getmoduleinfo('/usr/local/lib/python2.3/inspect.pyc')
('inspect', '.pyc', 'rb', 2)
>>> inspect.getmodulename('/usr/local/lib/python2.3/inspect.pyc')
'inspect'
>>> inspect.__name__
'inspect'
>>> inspect.__dict__

>>> inspect.__doc__
«Get useful information from live Python objects.\n\nThis module encapsulates
.…
Интересны некоторые функции, которые предоставляют информацию об исходном коде объектов:
Листинг
>>> import inspect
>>> inspect.getsourcefile(inspect) # имя файла исходного кода '/usr/local/lib/python2.3/inspect.py'
>>> inspect.getabsfile(inspect) # абсолютный путь к файлу '/usr/local/lib/python2.3/inspect.py'
>>> print inspect.getfile(inspect) # файл кода модуля
/usr/local/lib/python2.3/inspect.pyc
>>> print inspect.getsource(inspect) # исходный текст модуля (в виде строки)
# -*- coding: iso–8859–1 -*- ""«Get useful information from live Python objects.


>>> import smtplib
>>> # Комментарий непосредственно перед определением объекта:
>>> inspect.getcomments(smtplib.SMTPException)
'# Exception classes used by this module.\n'
>>> # Теперь берем строку документирования:
>>> inspect.getdoc(smtplib.SMTPException)
'Base class for all exceptions raised by this module.'
С помощью модуля inspect можно узнать состав аргументов некоторой функции с помощью функции inspect.getargspec():
Листинг
>>> import inspect
>>> def f(x, y=1, z=2):
… return x + y + z

>>> def g(x, *v, **z):
… return x

>>> print inspect.getargspec(f)
(['x', 'y', 'z'], None, None, (1, 2))
>>> print inspect.getargspec(g)
(['x'], 'v', 'z', None)
Возвращаемый кортеж содержит список аргументов (кроме специальных), затем следуют имена аргументов для списка позиционных аргументов (*) и списка именованных аргументов (**), после чего — список значений по умолчанию для последних позиционных аргументов. Первый аргумент–список может содержать вложенные списки, отражая структуру аргументов:
Листинг
>>> def f((x1,y1), (x2,y2)):
… return 1

>>> print inspect.getargspec(f)
([['x1', 'y1'], ['x2', 'y2']], None, None, None)
Классы (как вы помните) - тоже объекты, и о них можно кое–что узнать:
Листинг
>>> import smtplib
>>> s = smtplib.SMTP
>>> s.__module__ # модуль, в котором был определен объект 'smtplib'
>>> inspect.getmodule(s) # можно догадаться о происхождении объекта

Для визуализации дерева классов может быть полезна функция inspect.getclasstree().
Она возвращает иерархически выстроенный в соответствии с наследованием список вложенных списков классов, указанных в списке–параметре. В следующем примере на основе списка всех встроенных классов–исключений создается дерево их зависимостей по
наследованию:
Листинг import inspect, exceptions def formattree(tree, level=0):
«"«Вывод дерева наследований.
tree — дерево, подготовленное с помощью inspect.getclasstree(),
которое представлено списком вложенных списков и кортежей.
В кортеже entry первый элемент — класс, а второй — кортеж с его базовыми классами. Иначе entry — вложенный список.
level — уровень отступов
«""
for entry in tree:
if type(entry) is type(()):
c, bases = entry print level * " ", c.__name__, \
«(" + ", ".join([b.__name__ for b in bases]) + ")»
elif type(entry) is type([]):
formattree(entry, level+1)
v = exceptions.__dict__.values()
exc_list = [e for e in v if inspect.isclass(e) and issubclass(e, Exception)]
formattree(inspect.getclasstree(exc_list))
С помощью функции inspect.currentframe() можно получить текущий фрейм исполнения.
Атрибуты фрейма исполнения дают информацию о блоке кода, исполняющегося в точке вызова метода. При вызове функции (и в некоторых других ситуациях) на стек кладется соответствующий этому фрейму блок кода. При возврате из функции текущим становится фрейм, хранившийся в стеке. Фрейм содержит контекст выполнения кода: пространства имен и некоторые другие данные. Получить эти данные можно через атрибуты фреймового объекта:
Листинг import inspect def f():
fr = inspect.currentframe()
for a in dir(fr):
if a[:2] != "__":
print a, ":", str(getattr(fr, a))[:70]
f()
В результате получается
Листинг f_back :
f_builtins : {'help': Type help() for interactive help, or help(object) for help ab
f_code : ", line 11>
f_exc_traceback : None f_exc_type : None f_exc_value : None f_globals : {'f': , '__builtins__': f_lasti : 68
f_lineno : 16
f_locals : {'a': 'f_locals', 'fr': }
f_restricted : 0
f_trace : None
Здесь f_back — предыдущий фрейм исполнения (вызвавший данный фрейм), f_builtins
пространство встроенных имен, как его видно из данного фрейма, f_globals —
пространство глобальных имен, f_locals — пространство локальных имен, f_code — кодовый объект (в данном случае — байт–код функции f()), f_lasti — индекс последней выполнявшейся инструкции байт–кода, f_trace — функция трассировки для данного фрейма
(или None), f_lineno — текущая строка исходного кода, f_restricted — признак выполнения в ограничительном режиме.
Получить информацию о стеке интерпретатора можно с помощью функции inspect.stack(). Она возвращает список кортежей, в которых есть следующие элементы:
Листинг
(фрейм–объект, имя_файла, строка_в_файле, имя_функции,
список_строк_исходного_кода, номер_строки_в_коде)
Трассировочные объекты также играют важную роль в интроспективных возможностях языка Python: с их помощью можно отследить место возбуждения исключения и обработать его требуемым образом. Для работы с трассировками предусмотрен даже специальный модуль — traceback.
Трассировочный объект представляет содержимое стека исполнения от места возбуждения исключения до места его обработки. В обработчике исключений связанный с исключением трассировочный объект доступен посредством функции sys.exc_info() (это третий элемент возвращаемого данной функцией кортежа).
Трассировочный объект имеет следующие атрибуты:
tb_frame Фрейм исполнения текущего уровня.
tb_lineno и tb_lasti Номер строки и инструкции, где было возбуждено исключение.
tb_next Следующий уровень стека (другой трассировочный объект).
Одно из наиболее частых применений модуля traceback — «мягкая» обработка исключений с выводом отладочной информации в удобном виде (в лог, на стандартный вывод ошибок и т.п.):
Листинг
#!/usr/bin/python def dbg_except():
«"«Функция для отладки операторов try–except»""
import traceback, sys, string print sys.exc_info()
print " ".join(traceback.format_exception(*sys.exc_info()))
def bad_func2():
raise StandardError def bad_func():
bad_func2()
try:
bad_func()
except:
dbg_except()
В результате получается примерно следующее:
Листинг
(,
,
)
Traceback (most recent call last):
File «pr143.py», line 17, in ?
bad_func()
File «pr143.py», line 14, in bad_func bad_func2()
File «pr143.py», line 11, in bad_func2
raise StandardError
StandardError
Функция sys.exc_info() дает кортеж с информацией о возбужденном исключении (класс исключения, объект исключения и трассировочный объект). Элементы этого кортежа передаются как параметры функции traceback.format_exception(), которая и печатает информацию об исключении в уже знакомой форме. Модуль traceback содержит и другие функции (о них можно узнать из документации), которые помогают форматировать те или иные части информации об исключении.
Разумеется, это еще не все возможности модуля inspect и свойств интроспекции в
Python, а лишь наиболее интересные функции и атрибуты. Подробнее можно прочитать в документации или даже в исходном коде модулей стандартной библиотеки Python.
Заключение
С помощью возможностей интроспекции удается рассмотреть фазы работы транслятора Python: лексический анализ, синтаксический разбор и генерации кода для интерпретатора, саму работу интерпретатора можно видеть при помощи отладчика.
Вместе с тем, в этой лекции было дано представление об использовании профайлера для исследования того, на что больше всего тратится процессорное время в программе, а также затронуты некоторые аспекты оптимизации Python–программ и варианты оптимизации кода на Python по скорости.
Наконец, интроспекция позволяет исследовать не только строение программы, но и объектов, с которыми работает эта программа. Были рассмотрены возможности Python по получению информации об объектах — этом основном строительном материале, из которого складываются данные любой Python–программы.
1   ...   6   7   8   9   10   11   12   13   14


написать администратору сайта