Практика по языку Python/Примеры программ на языке Python
Иллюстрация основных элементов синтаксиса
[править]- Условный оператор
if
(если). При наличии нескольких условий и альтернатив применяется необязательный блокelif
(сокр. от else if) который может повторяться в коде неограниченное число раз. Если ни одно из условий не было соблюдено, то выполняется необязательный блокelse
(иначе)[1].
if условие_1: команды_1 elif условие_2: команды_2 elif условие_3: команды_3 ... elif условие_N: команды_N else: команды
- Оператор цикла
while
[2]:
while условие: команды
- Оператор цикла
for
[3]:
for элемент in последовательность: команды
- Операторы обработки исключений
try
—except
—else
—finally
[4]:
try: # основной код except Тип_ошибки_1: # вспомогательный код except Тип_ошибки_2: # вспомогательный код ... except Тип_ошибки_N: # вспомогательный код else: # код для случая, когда ошибки не было finally: # код, который выполняется всегда
Примеры программ на языке программирования Python.
[править]В этой статье собраны примеры небольших программ на языке программирования Python, демонстрирующих его синтаксис и некоторые из возможностей.
Замена значений переменных местами
[править]a, b = -1, 42 # Множественное присваивание
print(a, b) # (-1, 42)
a, b = b, a # Множественное присваивание
print(a, b) # (42, -1)
a = -1
b = 42
c = a #c = a(-1)
a = b #-1 = 42==(a(-1)=42 b=42)
b = c #42 = c(a(-1)) == b=-1(a)
Print(a,b)
Если a = b значит после этого b = a будет b = b потому что a = b поэтому нужно назначить третью переменную(c) и присвоить ему значение a. После b=c значит что b=a потому что c=a
Нахождение 10 наиболее частых слов на web странице
[править]Данный пример чисто демонстрационный, так как его можно значительно улучшить.
from urllib2 import urlopen # из модуля urllib2 импортируем функцию urlopen
u = urlopen("http://python.org") # открываем URL на чтение
words = {} # связываем имя words с пустым словарём
# (словарь — неупорядоченный [[ассоциативный массив]])
for line in u: # читаем u по строкам
line = line.strip(" \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(key=lambda x: x[1], reverse=True) # сортируем по убыванию второго элемента пары
for p in pairs[:10]: # печатаем первые 10 элементов списка
print(p[0], p[1])
####################################################################
# пример использования Counter из стандартной библиотеки
from urllib2 import urlopen
from collections import Counter
from re import findall
page = urlopen("http://python.org").read()
words = findall(r"\w+", page)
counter = Counter(words)
print(counter.most_common(10))
#[('a', 417), ('class', 354), ('li', 304), ('href', 211), ('span', 142), ('2', 135), ('title', 135), ('tier', 125), ('element', 122), ('role', 120)]
Примеры работы с последовательностями
[править]Иллюстрируют особенности индексации элементов и срезов: при взятии среза нумеруются не сами элементы, а промежутки между ними.
>>> 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(),
поддерживают то же правило (для версий языка 2.x):
>>> 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]]
Нахождение факториала
[править]factorial = lambda x: factorial(x - 1) * x if x > 1 else 1
Использование различных функций при группировке
[править]Данный пример демонстрирует возможность группировки DataFrame с использованием метода agg
import pandas as pd
df = pd.DataFrame({'first_col': ['a', 'a', 'c'], 'second_col': [11, 12, 12], 'third_col': [22, 23, 24]})
result_df = df.groupby(['first_col']).agg({'second_col': ['count'], 'third_col': ['mean', 'sum']})
Использование различных функций при группировке
[править]Данный пример демонстрирует возможность группировки DataFrame (pandas) с использованием метода agg
import pandas as pd
df = pd.DataFrame({'first_col': ['a', 'a', 'c'], 'second_col': [11, 12, 12], 'third_col': [22, 23, 24]})
result_df = df.groupby(['first_col']).agg({'second_col': ['count'], 'third_col': ['mean', 'sum']})
Символьные вычисления
[править]Символьные вычисления используются для аналитического решения математических задач. Базовые операции представлены ниже.
import sympy as sp
sp.init_printing()
x = sp.symbols('x') # В переменную x записали символ 'x'
f = x**2 - 2*x - 3 # Пример функции
# Построим график
%matplotlib inline
sp.plot(f, (x, -2, 4));
function_nulls = sp.solve(f, x) # Нули функции
df = f.diff(x) # Производная функции
df.subs(x, 0) # Вычисляет значение производной в нуле
Примечания
[править]- ↑ Васильев, 2017, pp. 87—97
- ↑ Васильев, 2017, pp. 97—106
- ↑ Васильев, 2017, pp. 106—116
- ↑ Васильев, 2017, pp. 116—126
Литература
[править]- Васильев А. Н. Python на примерах. — 2. — Санкт-Петербург: Наука и Техника, 2017. — 432 с.