Наследование и Дескрипторы
Разбор Д/З (если нужен)
Наследование
- видимость имён
Откуда берётся и что видит d.fun()?
- Применение методов родительского класса к объектам дочернего
что делать, если мы хотим, доопределить метод (в первую очередь .__init__())?
Порождение новых объектов: c = a + b, какого типа должен быть c, если .__add__() определён в родительском классе?
вместо C(a+b) надо писать type(a)(a+b)
Как не перебить случайно поле родительского класса?
спецполя с именами вида ._что-то-там по договорённости непубличные
спецполя с именами вида .__что-то-там, их раскрытие в ._имя-класса__что-то-там
исключение: спецполя вида .__что-то-там__
Множественное наследование, MRO — TODO
- сеть вместо дерева (ромбическое наследование) — проблемы
Дескрипторы
см. https://docs.python.org/3/howto/descriptor.html
Вместо .__dict__ — механизм с getter-ами и setter-ами.
Протокол дескриптора — объект с методами .__get__(), .__set__() и .__delete__() (если без __set__(), значит, это не данные, а, скажем, функция)
Это поле класса
⇒ одно на все экземпляры класса
конкретный экземпляр передаётся вторым параметром (None, если вызывается как метод класса), так что при желании можно различить
Имеет преимущество перед полем экземпляра (в отличие от обычных полей класса)
1 class Dsc:
2 value = None
3 def __get__(self, obj, cls):
4 print("Get from {0} class {1}".format(obj, cls))
5 return self.value
6
7 def __set__(self, obj, val):
8 print("Set {0} for {1}".format(val, obj))
9 self.value = val
10
11 def __delete__(self, obj):
12 print("Del from {0}".format(obj))
13 self.value = None
14
15 class C:
16 data = Dsc()
17
18 def __init__(self, name):
19 self.name = name
20
21 def __str__(self):
22 return self.name
23
24 d = C("De")
25 print(d.data)
26 d.data = 1
27 print(d.data)
28 del d.data
Обратите внимание: дескриптор — поле класса, так что
даст
Set 100500 for De Get from De class <class '__main__.C'> 100500 Get from Ye class <class '__main__.C'> 100500
В методах дескриптора всегда известно, через какой объект идёт доступ, поэтому при желании можно брать id(объект) и на этом основании делать что-то разное.
TODO
декораторы и property(), classmethod(), staticmethod()
weakref/метаклассы/слоты/что-то ещё — след. семестр?
Д/З
- Прочитать
про наследование в учебнике
про дескрипторы в HowTo
EJudge: MegaStroka 'Усиленная строка'
Написать класс Stroka, унаследованииый от str, экземпляр которого s дополнительно поддерживает следующие операции:
объект типа Stroka, содержащий символы s в обратном порядке: -s
объект типа Stroka, содержащий все пары символов из объектов s и t в порядке следования — s*t (t может быть просто строкой)
объект типа Stroka, равный s*s*…(n-1 раз)…*s — s**n (s**0 — пустая Stroka)
werASDF 12121212 DF wAwSwDwFeAeSeDeFrArSrDrF 11121112111221222122111221222122 w:w,1:1,w:w,2:2,e:e,1:1,e:e,2:2,r:r,1:1,r:r,2:2,
EJudge: GuessABC 'Угадайка'
Задать три класса — A, B и C, свойства которых неизвестны, но для которых верен пример.
1 a, b, c, d = A(2), B(3), C(4), C("Op") 2 print(a,b,c, a+b+c, a*b*c, a*b*d) 3 print(*(o+p for o in (a,b,c) for p in (a,b,c) if not o==b==p)) 4 print(*(o*p for o in (a,b,c,d) for p in (a,b,c,d) if not o==a==p and not o==d==p)) 5 print(*(isinstance(e,T) for e in (a,b,c) for T in (A,B,C))) 6 print(*(a in T.__dict__.keys() for a in ('__add__','__mul__','__str__') for T in (A,B,C)))
/2/ |3| |4| /9/ |24| |OpOpOpOpOpOp| /4/ /5/ /6/ /5/ |7| |6| |7| |8| |6| |8| |OpOp| |6| |9| |12| |OpOpOp| |8| |12| |16| |OpOpOpOp| |OpOp| |OpOpOp| |OpOpOpOp| True False False False True False True True True True False False False True False True True False
EJudge: SemDescriptor 'Семафор'
Реализовать с помощью дескрипторов абстракцию «простой семафор», используемую при разделении некроторого ресурса. При обращении к семафору либо происходит захват ресурса (семафор взводится), либо возвращается объект, который этот ресурс захватил. Взведённый семафор может сбросить только объект, который его захватил.
1 a, b = Sem("A"), Sem("B") 2 print("Locked:",a.lock) # A взводит опущенный семафор 3 print("Locked:",a.lock) # Семафор взведён A 4 print("Locked:",b.lock) # Семафор взведён A 5 del(b.lock) # B пытается сбросить семафор 6 print("Locked:",b.lock) # Семафор взведён A 7 print("Locked:",a.lock) # Семафор взведён A 8 del(a.lock) # А сбрасывает семафор 9 print("Locked:",b.lock) # B взводит опущенный семафор 10 print("Locked:",a.lock) # Семафор взведён B
Locked: None Locked: <A> Locked: <A> Locked: <A> Locked: <A> Locked: None Locked: <B>