- Примеры программ на языке Python
-
Примеры программ на языке Python
Примеры программ на языке программирования Python.
В этой статье собраны примеры небольших программ на языке программирования Python, демонстрирующих его синтаксис и некоторые из возможностей.
Нахождение 10 наиболее частых слов на web странице
Данный пример чисто демонстрационный, так как его можно значительно улучшить.
import string # импортируем модуль для работы со строками from urllib2 import urlopen # из модуля urllib2 импортируем функцию urlopen u = urlopen("http://python.org") # открываем URL на чтение words = {} # связываем имя words с пустым словарём # (словарь — неупорядоченный [[ассоциативный массив]]) for line in u.readlines(): # читаем u по строкам line = string.strip(line, " \n") # отбрасываем начальные и конечные пробелы for word in line.split(" "): # режем каждую строку на слова, ограниченные пробелами try: # блок обработки исключений words[word] += 1 # пытаемся увеличить words[word] на единицу except KeyError: # если не получилось (раньше words[word] не было) words[word] = 1 # присваиваем единицу # теперь словарь words содержит частоту встречаемости каждого слова. # Например, words может содержать {"яблоко":5, "апельсин": 12, "груша": 8} pairs = words.items() # делаем из словаря список пар # pairs == [("яблоко",5), ("апельсин",12), ("груша",8)] pairs.sort(lambda a, b: b[1]-a[1]) # сортируем по убыванию второго элемента пары for p in pairs[:10]: # печатаем первые 10 элементов списка print p[0], p[1]
Примеры работы с последовательностями
Иллюстрируют особенности индексации элементов и срезов: при взятии среза нумеруются не сами элементы, а промежутки между ними.
>>> l = ['A', 'B', 'C', 'D', 'E'] # исходный список >>> # 0 1 2 3 4 5 # пронумерованные промежутки между элементами >>> # -5 -4 -3 -2 -1 # нумерация с конца >>> l[0:2] # срез от нулевого до второго промежутка ['A', 'B'] >>> l[1:-2] # срез от второго до второго с конца элемента ['B','C'] >>> l[1::2] # каждый второй элемент начиная с первого ['B', 'D'] >>> l[::-1] # все элементы в обратном порядке ['E', 'D', 'C', 'B', 'A']
Функции подобные
range()
поддерживают то же правило:>>> range(2, 5) [2, 3, 4] >>> range(5) [0, 1, 2, 3, 4]
Реализация перегрузки функций
Это пример простой реализации поддержки перегрузки функций на Python. Она демонстрирует, как, используя уже имеющиеся в Python средства, можно обойти одно из ограничений базовой реализации. Поддерживается минимум возможностей (только фиксированное количество позиционных аргументов, нет именованных аргументов, нет приведения типов (например
int -> float
) и т. п.), но работает достаточно быстро.import sys # для получения объектов из вышележащих фрэймов стека class CannotResolve(Exception):pass # класс исключения для случая ненахождения функции class Resolver(object): # класс, реализующий разрешение на этапе исполнения emess = "Can't found appropriate signature of func %s() for call with" + \ " params %r" # сообщение об ошибке def __init__(self,name): # конструктор self.function_map = {} # словарь, отображающий типы параметров на функции self.default = None # функция по умолчанию self.name = name # имя функции для вывода сообщений об ошибках def __call__(self,*dt): # имитируем функцию, принимающую любое количество # позиционных параметров cls = tuple(map(type,dt)) # создаем tuple из типов переданных аргументов # функция type возвращает тип своего параметра # map вызовет type для каждого элемента из dt try: x = self.function_map[cls] # пытаемся получить функцию из словаря except KeyError: # если подходящей нет, if self.default is not None: # используем функцию по умолчанию x = self.default else: # если её нет - возбуждаем исключение raise CannotResolve(self.emess % (self.name,cls)) return x(*dt) # вызываем функцию и возвращаем результат def overload(*dt): # декоратор для перегрузки в качестве параметров # принимает типы параметров def closure(func): name = func.__name__ # получаем имя функции fr = sys._getframe(1).f_locals.get(name,Resolver(name)) # опускаемся на один шаг вниз по стеку и находим # локальную переменную с именем функции # если же ее нет, то используем новый # Resolver-объект fr.function_map[dt] = func # добавляем новую функцию к словарю # разрешения вызовов return fr return closure def overdef(func): # для создания функции по умолчанию name = func.__name__ # аналогично как и в функции overload fr = sys._getframe(1).f_locals.get(name,Resolver(name)) fr.default = func return fr # теперь воспользуемся полученными декораторами @overdef # это будет функция по умолчанию def f(*dt,**mp): print "Default call" # если нет явного return, то вернется None @overload(int) # единственный параметр - целое def f(x): return x + 1 @overload(str) # единственный параметр - строка def f(x): return x + "1" @overload(str,int) # строка и целое def f(x,y): return x + str(y) print f(1) # напечатает : 2 print f("1") # напечатает : 11 f(2,2) # напечатает : Default call
Управление контекстом выполнения
Следующий пример из PEP343 иллюстрирует применение оператора
with
для защиты блока кода от одновременного выполнения двумя потоками:from __future__ import with_statement # задействует оператор with в коде from contextlib import contextmanager from threading import Lock # Описание менеджера контекста @contextmanager def locked(lock): lock.acquire() try: yield finally: lock.release() # Определение блокировки myLock = Lock() # Применение оператора with locked(myLock): # print "Охраняемый блок кода. Блокировка будет освобождена при любом выходе из этого блока." #
Генератор чисел Фибоначчи
Пример генератора чисел Фибоначчи и его использования:
def fibonacci(max): # генератор (а не функция, т.к. оператор return заменён на yield) a, b = 0, 1 while a < max: yield a # return a, + запоминаем место рестарта для следующего вызова a, b = b, a + b # параллельное присваивание, которое выполняется одновременно и параллельно for n in fibonacci(100): # используем генератор fibonacci() как итератор print n, # печатаем все числа Фибоначчи меньшие 100 через пробел
Альтернативный синтаксис доступа к элементам словаря
Можно определить словарь, который в дополнение к обычному синтаксису доступа к значению по ключу
d[key]
может предоставлять синтаксически более наглядный доступ к атрибутуd.key
в случае алфавитно-цифровых ключей:class Entity(dict): # наследуем класс от __builtin__.dict def __getattr__(self, key): # этот метод будет вызван, если атрибут # с именем key не будет найден у экземпляра класса try: return self[key] # пытаемся вернуть элемент словаря except KeyError, k: # если такого элемента нет, то возбуждаем raise AttributeError, k # исключение AttributeError # по договоренности __getattr__ # не должно возбуждать других исключений def __setattr__(self, key, value): # этот метод будет вызван при присвоении self[key] = value # атрибуту key значения value def __delattr__(self, key): # а этот при удалении атрибута try: # с помощью del mydict.g del self[key] except KeyError, k: raise AttributeError, k def __repr__(self): # используется функцией repr return self.__class__.__name__ + "(" + dict.__repr__(self) + ")" d = Entity(a=1) d.b_100 = 100 assert d.a == d['a'] and d.b_100 == d['b_100']
Функтор с генерацией байтокода
Пример эффективной реализации функтора, основанный на генерации байтокода во время исполнения. Этот пример демонстрирует следующие возможности/особенности Python:
- Возможность реализации специфических средств функционального программирования наработками, уже имеющимися в языке
- Работать с байтокодом в Python достаточно просто
- Зачастую генерация байтокода способна значительно ускорить исполнение.
Это только пример, он реализует всего одну операцию — сложение и имеет несколько других ограничений.
#------------------------------------------------------------------------------- import byteplay # специальный модуль для удобной работы с Python-байтокодом import new # для создания функции во время исполнения import functools # для update_wrapper import inspect # для получения информации о параметрах, принимаемых функцией #------------------------------------------------------------------------------- class FastFunctor(object): def __init__(self,func,code = None): self.func = None # здесь будем хранить результирующую функцию self.ofunc = func # а здесь исходную(original) функцию if code is None: # конструируем байтокод для вызова функции self.code = [(byteplay.LOAD_CONST,func)] rparams = inspect.getargspec(func)[0] # получаем список параметров, принимаемых функцией self.code.extend((byteplay.LOAD_FAST,i) for i in rparams) self.code.append((byteplay.CALL_FUNCTION,len(rparams))) else: # если же функтор создан из другого функтора, # то только копируем переданный байтокод self.code = code # создаем новый объект кода self.ocode = bp.Code.from_code(func.func_code) def __add__(self,obj): # этот метод вызывается для операции '+' code = self.code[:] # копируем байтокод if isinstance(obj,FastFunctor): # если прибавляемый объект - функтор # просто дописываем его код к нашему # после своего исполнения он "оставит" в вершине стека результат code.extend(obj.code) else: # иначе загружаем объект в стек code.append((byteplay.LOAD_CONST,obj)) # дописываем байтокод, складывающий два верхних элемента в стеке code.append((byteplay.BINARY_ADD ,None )) # создаем новый функтор, с байтокодом получения суммы return self.__class__(self.ofunc,code = code) def __call__(self,*dt,**mp): # этот метод будет вызван для операции вызова object() return self.fast()(*dt,**mp) # конструируем и вызываем функцию def fast(self): # конструируем функцию из байтокода if self.func is None: # если функция не была создана раннее code = self.code + [(bp.RETURN_VALUE,None)] # добавляем байтокод возврата oc = self.ocode # создаем объект кода из байтокода и другой информации bin_code = byteplay.Code(code, oc.freevars, oc.args, oc.varargs, oc.varkwargs, oc.newlocals, "<just_code_%s>" % id(self), "<auto_gen_%s>" % id(self), 0, "auto_generated code") # конструируем новую функцию из объекта кода self.func = new.function(bin_code.to_code(),globals()) # после этой операции для всех средств интроспекции # созданная функция будет выглядеть как оригинальная self.func = functools.update_wrapper(self.func,self.ofunc) return self.func
# Ниже представлено тестирование скорости объектов FastFunctor и SlowFunctor # (статья "Функциональное программирование на Python", см. сноску после блока кода) # из IPython (для удобства чтения лог немного изменен) # строки, начинающиеся с "In [XX]:" вводятся, остальные — вывод интерпретатора In [1]: import fastfunctor In [2]: func = lambda x : x + 1 # Создаем очень простую функцию In [3]: vl = 1 # Переменная, для предотвращения оптимизации In [4]: functor = fastfunctor.Functor(func) In [5]: %timeit (functor + functor + 1)(vl) # Тестируем "лобовой" способ 1000 loops, best of 3: 661 mks per loop # Очень медленно In [6]: functor2 = (functor + functor + 1) # Конструируем функтор один раз In [7]: %timeit functor2(vl) # и тестируем только непосредственно вызов 100000 loops, best of 3: 4.52 mks per loop # Значительно лучше In [8]: functor3 = (functor + functor + 1).fast() # Получаем результирующую функцию In [9]: %timeit functor3(vl) 1000000 loops, best of 3: 1.42 mks per loop In [10]: def of(vl): return x(vl) + x(vl) + 1 # Создаем функцию "вручную" In [11]: %timeit of(vl) 1000000 loops, best of 3: 1.42 mks per loop # Скорость полностью совпадает со # скоростью функтора In [12]: sfunctor = SlowFunctor(func) # Простая реализация функтора In [13]: sfunctor = sfunctor + sfunctor + 1 # In [14]: %timeit sfunctor(vl) # 100000 loops, best of 3: 12.6 mks per loop # Примерно в 9 раз медленнее, чем статический # вариант
Код SlowFunctor можно посмотреть здесь.
Приведенные значения времени следует рассматривать только в сравнении друг с другом.
ipython — расширение интерпретатора Python для интерактивной работы.Используя эту технику, можно создать полноценный функтор, добавив функции для других операций (
__sub__, __div__
и другие) и расширив его на случай нескольких входных функций с разными аргументами.Транспонирование матрицы
Пример лаконичной реализации операции транспонирования матриц с использованием парадигмы функционального программирования.
from pprint import pprint # модуль pprint используется для удобного вывода на экран matrix = [[0.5, 0, 0, 0, 0], [ 1, 0.5, 0, 0, 0], [ 1, 1, 0.5, 0, 0], [ 1, 1, 1, 0.5, 0], [ 1, 1, 1, 1, 0.5]] matrix_t = list(zip(*matrix)) # непосредственно транспонирование pprint(matrix) pprint(matrix_t)
Вывод:
[[0.5, 0, 0, 0, 0], [1, 0.5, 0, 0, 0], [1, 1, 0.5, 0, 0], [1, 1, 1, 0.5, 0], [1, 1, 1, 1, 0.5]] [[0.5, 1, 1, 1, 1], [0, 0.5, 1, 1, 1], [0, 0, 0.5, 1, 1], [0, 0, 0, 0.5, 1], [0, 0, 0, 0, 0.5]]
Wikimedia Foundation. 2010.