Замыкание
Идея настолько оказалась заманчивой для разработчиков, что была перенесена в некоторые нефункциональные языки. Замыканием в программировании называется процедура с привязанной к ней совокупностью данных. Смысл замыкания состоит в том, что определение функции «замораживает» окружающий ее контекст на момент определения.
Пример:
Def multi(n): - возвращает саму функцию
def mul(k):
return n*k
return mul
mul3 = multi(3)
print(mul3(3), mul3(5))
Вывод:
9 15
Другой способ:
n = 3
def mult(l, mul = n):
return mul * k
n = 7
print(mult(3))
n = 13
print(mult(5))
n = 10
mult = lambda k, mul = n: mul *k
print (mult(3))
Вывод:
Пояснение: как видно из примера, никакие последующие присвоения параметра по умолчанию не приводят к изменению ранее определенной функции. Но сама функция может быть переопределена (то есть в предпоследней строчке было предопределение).
Функтор
Функтор – это не функция, а объект класса, в которой определена функция _call_(). При этом для экземпляра такого объекта может применяться вызов так же, как и для функции.
В следующем примере демонстрируется замыкание частичного определения функции и функтора.
Def multi(n):
Def mul(k):
Return n*k
Return mul
Mul3 = multi(3)
# частично определение функции
From functools import partical
Def milpart(a,b):
Return a*b
Par3 = partial(mulpart, 3)
# функтор
Class mulfunc:
Def _init_(self, val1):
Self.val1 = val1
Def _init_(self, val1=2):
Return self.val1 * val2
Fun3 = mulfunc(3)
# пример
Print(mul3(5), par3(5), fun3(5))
Вывод:
15 15 15
Карринг (каррирование), curring
Калинг – это преобразование функции многих переменных в функцию, берущую свои аргументы по одному. Названа в честь математика Хаскелла Карри. Все языки, поддерживающие замыкание, позволяют записывать каррированные функции.
def spam(x,y):
|
Print(x,y)
spam1 = lambda x: lambda y: spam(x,y)
def spam2(x):
sef new_spam(y):
return spam(x,y)
return new_spam
spam1(2) (3)
spam2(2) (3)
Вывод:
2 3
2 3
ООП Python
Основные понятия ООП: объект, класс, инкапсуляция, полиморфизм, композиция, наследование:
· С точки зрения ООП класс представляет собой коллекцию данных. Основная идея ООП – использование абстрактного подхода.
· Объект – это экземпляр класса, является совокупностью данных и методов, которые над этими данными выполняются.
· Инкапсуляция – сокрытие внутренних подробностей работы объекта от окружающего мира. В его основе лежит использование атрибутов внутри класса. Значение атрибутов в определенный момент времени определяют состояние объекта.
· Полиморфизм – для разных объектов одна и та же операция может выполнять различные функции. Например, «+», он полиморфичен, для чисел – он их складывает, для строк – проводит конкатенацию.
· Наследование – возможность создавать одни классы на основе других.
· Композиция – способность объекта включать в себя другие объекты.
Объектно-ориентированный подход основан на следующем алгоритме:
1) Описывается проблема на обычном языке алгоритмов.
2) На основе понятий формируются классы.
3) На основе их действий формируются методы.
4) Реализуются методы и атрибуты.
Объектно-ориентированный подход хорош тогда, когда проект большой, подразумевает долгосрочное развитие, состоит из большого количества библиотек и внутренних связей. Наиболее важные особенности классов в питоне:
1) Множественное наследование.
2) Производный класс может переопределить любые методы базовых классов.
|
3) Метод базового класса с тем же именем можно вызвать в любом месте.
4) Все атрибуты класса являются public, все методы виртуальны, то есть перегружают базовые.
5) Для создания класса используется ключевое слово class.
(class A:
Def _init_(self)
Print(1) # объект класса
a = A() # инстанс класса
Вывод
)
Есть так же другой тип объектов – instance class. Объект класса и инстанс класса – два разных объекта. Первый объявляется на этапе объявления, второй - при вызове имени класса. Объект – только один, инстансов сколько угодно.
При определении класса создается новое пространство имён, создается объект class, являющийся оболочкой для всех инструкций. Инструкциями, как правило, являются определения функций. Объекты класса поддерживают два вида операций: доступ к атрибутам и создание экземпляров. Атрибуты бывают двух типов: атрибуты-данные и атрибуты-методы.
Class A:
‘Simple class’
var = 87
def f(x):
return ‘Hello world’
def __init__(self):
print(1)
print(A.__doc__)
print(A.var)
Атрибут А можно сделать приватным, то есть недоступным извне. Для этого нужно поставить «__» перед его названием.
def method(self,x,y):
return x + y
class A:
‘Simple class’
__var = 87
def f(self,x):
return self.__var
def __init__(self):
print(1)
print(A.__doc__)
a = A()
print(A.var)
Вывод:
x + y
Другой пример:
class A:
pass
a = A()
a.ff = method
print(a.ff(a,2,3))
#То есть класс а – заготовка.
Вывод:
…однако, это имя не имеет какого-то значения. Self используется для доступа к атрибутам класса.
Наследование
Для определения производного класса:
Class Simple:
Def_itit_(self):
Self.list = []
Def.f(self.e)
Self.list.append(e)
Def rem (self, idx):
Del self.list(idx)
S = simple()
S.add(1)
S.add(2)
S.add(8)
S.rem(1)
|
Class too simple(simple):
Разрешение или атрибут работает сверху-вниз. Если атрибут не найден в текущем классе, поиск продолжается в базовом классе и так далее по рекурсии:
Class too simple(simple):
Def add(self, e):
Self.list.insert (e, o)
В питоне существует ограничение поддержка множественного наследования
Class simple2
Основные свойства ООП: полимер, наследования, инкапсуляция отличие ООП в языке питон в том, что первый параметр self, являющийся ссылкой на истанс (экземпляр) класса передается явно.
Инкапсуляция в питоне достигается за счет того, что доступ к атрибутам осуществляется не на напрямую, а через методы.
Спец. Методы (вставка: атрибутов) классов в питоне.
Такие методы имеют двойной символ подчерк в качестве префиксов. С их помощью реализовываются такие вещи как конструкторы, слоты и т.д.
Имя класса __name__
Имя модуля __modul__
Словарь атрибутов __dict__
Кортеж базовых классов __bases__
Строка документации __doc__
Экземпляр (инстанс) класса возвращается при вызове объекта-класса. Объект у класса может быть один, экземпляров (или инстансов) — несколько. Экземпляры имеют следующие атрибуты:
__dict__ — словарь атрибутов класса, можно изменять этот словарь напрямую;
__class__ — объект-класс, экземпляром которого является данный инстанс;
__init__ — конструктор. Если в базовом классе есть конструктор, конструктор производного класса должен вызвать его;
__del__ — деструктор. Если в базовом классе есть деструкор, деструктор производного класса должен вызвать его;
__cmp__ — вызывается для всех операций сравнения;
__hash__ — возвращает хеш-значение объекта, равное 32-битному числу;
__getattr__ — возвращает атрибут, недоступный обычным способом;
__setattr__ — присваивает значение атрибуту;
__delattr__ — удаляет атрибут;
__call__ — срабатывает при вызове экземпляра класса.
Так же экземпляры классов можно использовать для эмуляции последовательностей. Для такой реализации есть встроенные методы:
__len__ — возвращает длину последовательности;
__getitem__ — получение элемента по индексу или ключу;
__setitem__ — присваивание элемента с данным ключом или индексом;
__delitem__ — удаление элемента с данным ключом или индексом;
__getslice__ — возвращает вложенную последовательность;
__setslice__ — заменяет вложенную последовательность;
__delslice__ — удаляет вложенную последовательность;
__contains__ — реализует оператор in.
Объекты классов можно привести к строковому или числовому типу.
__repr__ — возвращает формальное строковое представление объекта;
__str__ — возвращает строковое представление объекта;
__oct__, __hex__, __complex__, __int__, __long__, __float__ — возвращают строковое представление в соответствующей системе счисления.
Bound и unbound с кнопкой и без.
Рассмотрим конкретный пример. Есть базовый класс Cat, и есть производный от него класс Barsik:
class Cat: def __init__(self): self.hungry = True def eat(self): if self.hungry: print 'I am hangry...' self.hungry = False else: print 'No, thanks!' class Barsik(Cat): def __init__(self): self.sound = 'Aaaammm!' print self.sound |
Создаем экземпляр производного класса:
>>> brs = Barsik() Aaaammm! >>> brs.eat() AttributeError: Barsik instance has no attribute 'hungry' |
На первый взгляд — странная ошибка, поскольку атрибут hungry есть в базовом классе. На самом деле, конструктор производного класса — перегруженный, при этом конструктор базового класса не вызывается, и его нужно явно вызвать. Это можно сделать двумя путями. Первый вариант считается устаревшим:
_metaclass_ = type class Barsik(Cat): def __init__(self): Super(Barsik, self).__init__() self.sound = 'Aaaammm!' print (self.sound) |
Комментарии от Жукова: стандартный метод super возвращает иснтанс объект для базового конструктора.
Статические методы:
Статический метод — функция, определенная вне класса и не имеющая атрибута self ИЛИ Статический метод может быть определен и внутри класса — для этого используется ключевое слово staticmethod, причем метод может быть вызван как статически, так и через инстанс:
class Multi:
def imeth(self, x):
print self, x
def smeth(x):
print x
def cmeth(cls, x):
print cls, x
smeth = staticmethod(smeth)
cmeth = classmethod(cmeth)
>>> Multi.smeth(3)
>>> obj=Multi()
>>> obj.smeth(5)
Методы класса определяются с помощью ключевого слова classmethod — здесь автоматически питон передает в качестве первого параметра сам класс (cls):
>>> Multi.cmeth(7) __main__.Multi 7 >>> obj.cmeth(10) __main__.Multi 10 |
Итератор
Итераторы хороши там, где списки не подходят в силу того, что занимают много памяти, а итератор возвращает его конкретное значение. В классе нужно определить два стандартных метода — __iter__ и next. Метод __iter__ будет возвращать объект через метод next:
class Reverse: def __init__(self, data): self.data = data self.index = len(data) def __iter__(self): return self def next(self): if self.index == 0: raise StopIteration self.index = self.index - 1 return self.data[self.index] >>> for char in Reverse('12345'): >>> print char |
Итератор можно сконвертировать в список:
>>> rvr = list(Reverse('12345')) >>> rvr ['5', '4', '3', '2', '1'] |
ВСЕ ЛЕКЦИИ КОНЧИЛИСЬ.
ДАЛЕЕ ТО ЧТО БЫЛО НА САЙТЕ, ОТКУДА ОН ДИКТОВАЛ.
Property
Property — атрибут класса, возвращаемый через стандартную функцию property, которая в качестве аргументов принимает другие функции класса:
class DateOffset: def __init__(self): self.start = 0 def _get_offset(self): self.start += 5 return self.start offset = property(_get_offset) >>> d = DateOffset() >>> d.offset >>> d.offset |
Singleton
Данный паттерн позволяет создать всего один инстанс для класса. Используется метод __new__:
class Singleton(object): def __new__(cls, *args, **kw): if not hasattr(cls, '_instance'): orig = super(Singleton, cls) cls._instance = orig.__new__(cls, *args, **kw) return cls._instance >>> one = Singleton() >>> two = Singleton() >>> id(one) >>> id(two) |
Слоты
Слоты — это список атрибутов, задаваемый в заголовке класса с помощью __slots__. В инстансе необходимо назначить атрибут, прежде чем пользоваться им:
class limiter(object): __slots__ = ['age', 'name', 'job'] >>> x=limiter() >>> x.age = 20 |
Функтор
Функтор — это класс, имеющий метод __call__ — при этом объект можно вызвать как функцию.
Пример. Пусть у нас имеется класс Person, имеется коллекция объектов этого класса- people, нужно отсортировать эту коллекцию по фамилиям. Для этого можно использовать функтор Sortkey:
class SortKey: def __init__(self, *attribute_names): self.attribute_names = attribute_names def __call__(self, instance): values = [] for attribute_name in self.attribute_names: values.append(getattr(instance, attribute_name)) return values class Person: def __init__(self, forename, surname, email): self.forename = forename self.surname = surname self.email = email >>> people=[] >>> p=Person('Petrov','','') >>> people.append(p) >>> p=Person('Sidorov','','') >>> people.append(p) >>> p=Person(u'Ivanov','','') >>> people.append(p) >>> for p in people: ... print p.forename Petrov Sidorov Ivanov >>> people.sort(key=SortKey("forename")) >>> for p in people: ... print p.forename Ivanov Petrov Sidorov |
Дескриптор
Дескриптор — это класс, который хранит и контролирует атрибуты других классов. Вообще любой класс, который имплементирует один из специальных методов — __get__, __set__, __delete__, является дескриптором.
Пример:
class ExternalStorage: __slots__ = ("attribute_name",) __storage = {} def __init__(self, attribute_name): self.attribute_name = attribute_name def __set__(self, instance, value): self.__storage[id(instance), self.attribute_name] = value def __get__(self, instance, owner=None): if instance is None: return self return self.__storage[id(instance), self.attribute_name] class Point: __slots__ = () x = ExternalStorage("x") y = ExternalStorage("y") def __init__(self, x=0, y=0): self.x = x self.y = y >>> p1=Point(1,2) >>> p2=Point(3,4) |
В данном случае класс Point не имеет собственных атрибутов x, y, хотя вызывает их так, как будто они есть — на самом деле они хранятся в дескрипторе ExternalStorage.
Sequence
Последовательность реализуется с помощью методов __getitem__, __setitem__. В данном примере класс MySequence возвращает по индексу элемент последовательности неопределенной длины, представляющей собой арифметическую прогрессию вида: 1 3 5 7... Здесь нельзя применить стандартные методы __del__, __len__:
class MySequence: def __init__(self, start=0, step=1): self.start = start self.step = step self.changed = {} def __getitem__(self, key): return self.start + key*self.step def __setitem__(self, key, value): self.changed[key] = value >>> s = MySequence(1,2) >>> s[0] >>> s[1] >>> s[100] |