-
Notifications
You must be signed in to change notification settings - Fork 31
Expand file tree
/
Copy path04-oop.slim
More file actions
446 lines (333 loc) · 17.8 KB
/
04-oop.slim
File metadata and controls
446 lines (333 loc) · 17.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
= slide 'Ко да се…' do
example:
panda = 'shi shi'
def say_hi():
platypus = 'perry'
print('Hi, {}'.format(platypus))
print('Hi, {}'.format(panda))
say_hi()
print("Oh, you're still here, {}?".format(platypus))
p.action Hi, perry
p.action Hi, shi shi
p.action NameError: name 'platypus' is not defined
= slide 'Ко каза…' do
example:
>>> def closure(initial):
... def increment_by(x):
... return initial + x
... def decrement_by(x):
... return initial - x
... return increment_by, decrement_by
>>> inc, dec = closure(6)
>>> inc(3)
>>> dec(7)
p.action 9
p.action -1
= slide 'Ko?!' do
example:
def snitch(init_message):
def decorator(f):
print(init_message)
def snitched(*args, **kwargs):
print('Called with {}, {}'.format(args, kwargs))
result = f(*args, **kwargs)
print('Returned {}'.format(result))
return result
return snitched
return decorator
@snitch('Initializing baba')
def baba(a, b):
return a / b
baba(4, 2)
= slide 'OOP' do
p Обектно-ориентирано програмиране
= slide 'От идейна гледна точка' do
p Разбирайте „за програмирането генерално“
ol
li Абстракция
li Енкапсулация
li Модулярност
= slide 'Абстракция' do
p Най-общо казано(демек, според википедия):
blockquote cite='http://en.wikipedia.org/wiki/Abstraction' Abstraction in its main sense is a conceptual process by which general rules and concepts are derived from the usage and classification of specific examples, literal ("real" or "concrete") signifiers, first principles, or other methods. "An abstraction" is the product of this process—a concept that acts as a super-categorical noun for all subordinate concepts, and connects any related concepts as a group, field, or category.
= slide 'Абстракция' do
p А по-конкретно за областта, която ни интересува
blockquote cite='http://en.wikipedia.org/wiki/Abstraction_(computer_science)' […] a technique for managing complexity of computer systems. It works by establishing a level of complexity on which a person interacts with the system, suppressing the more complex details below the current level. The programmer works with an idealized interface (usually well defined) and can add additional levels of functionality that would otherwise be too complex to handle.
= slide 'Абстракция' do
img src='04/abstraction.png'
= slide 'Енкапсулация' do
blockquote cite='http://en.wikipedia.org/wiki/Encapsulation_(object-oriented_programming)' Encapsulation is the packing of data and functions into a single component.
p Има много механизми, с които можем да го реализираме, но обектно ориентираното програмиране е един от най-популярните
= slide 'Information hiding' do
blockquote cite='http://stackoverflow.com/a/24748' Abstraction and encapsulation are complementary concepts: abstraction focuses on the observable behavior of an object... encapsulation focuses upon the implementation that gives rise to this behavior... encapsulation is most often achieved through information hiding, which is the process of hiding all of the secrets of object that do not contribute to its essential characteristics.
= slide 'Модулярност' do
p Механизъм за организиране на сходна логика работеща върху свързани видове данни, обработваща сходни видове процеси от моделирания свят в добре обособени и ясно разделени парчета от кода ни
= slide 'По същество' do
p Разбирайте „конкретно за python“
ol
li Всичко е обект
li Обектите са отворени
li Класовете са отворени
p Последните две с някои малки уговорки, които обаче рядко ще ви интересуват
= slide 'Всичко е обект' do
p Съвсем буквално
example:
>>> type(42)
<class 'int'>
>>> type([])
<class 'list'>
>>> type([]) is list
True
>>> type(list)
<class 'type'>
>>> type(list) is type
True
= slide 'Тогава…' do
example:
>>> type(type)
p ???
p.action <class 'type'>
img.action src='04/mind_blown.gif'
= slide 'Класове' do
p Създаваме класове с ключовата дума <code>class</code>, след което всяка функция дефинирана в тялото на класа е метод, а всяка променлива е клас променлива
= slide 'Примерен клас Vector' do
example:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
spam = Vector(1.0, 1.0)
print(spam.x)
ol.action
li „Конструктура“ се казва <code>__init__</code>, той не връща стойност
li Първия аргумент на методите винаги е инстанцията, върху която се извикват, той може да се казва всякак, но винаги се казва <code>self</code> , иначе губите огромни количества точки/колегите ви ви мразят/никой не иска да си играе с вас в пясъчника
li Атрибутите („член-променливите“/„член-данните“) не се нуждаят от декларации (обектите са отворени)
li Инстанцираме клас, като го „извикаме“ със съответните аргументи, които очаква <code>__init__</code> метода му и като резултат получаваме новоконструирания обект
= slide 'Забележки' do
p „Конструктор“ е думата, с която сте свикнали, но в случая далеч по-подходяща е „инициализатор“, както си личи от името
p Въпреки, че при инстанциране на обект подаваме аргументите, които очаква инциализатора, той не е първия метод, който се извиква в този момент, но за сега не влагайте много мисъл в това, просто го имайте предвид
= slide 'Примерен клас Vector (2)' do
example:
import math
class Vector:
def __init__(self, x, y): ...
def length(self):
return math.sqrt(self.x**2 + self.y**2)
spam = Vector(1.0, 2.0)
print(spam.length())
ol.action
li В методите атрибутите могат да се достъпват само през <code>self</code> , няма никакви магически имплицитни scope-ове
li Методите се извикват през инстанцирания обект <code>обект.име_на_метод()</code>
= slide 'Примерен клас Vector (3)' do
example:
class Vector:
def __init__(self, x, y, z): ...
def _coords(self):
return (self.x, self.y, self.z)
def length(self):
return sum(_ ** 2 for _ in self._coords()) ** 0.5
ol.action
li _coords е protected метод
li Отново, методите се извикват върху self
li _ е валидно име за променлива
= slide 'Private/protected' do
p Казахме, че класовете са отворени. Това ще рече, че private и protected концепциите не са това, за което сте свикнали да мислите в езици като C++/Java/C#
p Ограниченията за използване на защитени и частни методи в класовете в python е отговорност на програмиста, което по никакъв начин не прави живота ви по-труден
p Методи/атрибути започващи с _ са защитени, т.е. би следвало да се ползват само от методи на класа и наследяващи го класове
p Методи/атрибути започващи с __ са частни, т.е. би следвало да се ползват само от методи на класа
p Достатъчно очевидно е, а някои много редки случаи може да се наложи тези ограничения да не се спазят
= slide 'unbound methods' do
example:
v1 = Vector(1.0, 2.0, 3.0)
v2 = Vector(4.0, 5.0, 6.0)
v3 = Vector(7.0, 8.0, 9.0)
print(Vector.length(v1))
print(Vector.length(v2))
p Което може да бъде полезно за следното
example:
print(list(map(Vector.length, [v1, v2, v3])))
= slide 'Състояние' do
p Mutable vs. Immutable
ul
li mutable са обекти, които променят вътрешното си състояние във времето
li immutable са обекти, които никога не променят вътрешното си състояние
p Най-общо повечето обекти в python са mutable, доколкото езика не ни забранява да ги променяме
p.action Какво в python знаем, че е immutable?
= slide 'mutating method' do
example:
class Vector:
def __init__(self, x, y, z): ...
def length(self): ...
def normalize(self):
length = self.length()
self.x /= length
self.y /= length
self.z /= length
= slide 'non-mutating method' do
example:
class Vector:
def __init__(self, x, y, z): ...
def length(self): ...
def normalized(self):
length = self.length()
return Vector(self.x / length,
self.y / length, self.z / length)
= slide 'normalize vs normalized' do
example:
class Vector:
...
def normalize(self):
length = self.length()
self.x /= length
self.y /= length
self.z /= length
def normalized(self):
return Vector(self.x / self.length(), self.y / self.length(),
self.z / self.length())
= slide 'Сравняване на обекти' do
ul
li Можете да проверите дали два обекта са равни по стойност с ==
li Можете да проверите дали две имена сочат към един и същи обект с is
li Можете да предефинирате равенството за обекти от даден клас с метода __eq__
li По подразбиране, __eq__ е имплементирана с is
example:
class Vector:
def __init__(self, x, y, z):
self._coords = (x, y, z)
def __eq__(self, other):
return self._coords == other._coords
= slide 'dunder methods' do
p Известни още като "magic methods", dunder(double under) методите в python най-често предефинират някакъв аспект от поведението на обектите ни
= slide 'Аритметични оператори' do
ul
li <code>__add__(self, other)</code> - <code>self + other</code>
li <code>__sub__(self, other)</code> - <code>self - other</code>
li <code>__mul__(self, other)</code> - <code>self * other</code>
li <code>__truediv__(self, other)</code> - <code>self / other</code>
li <code>__floordiv__(self, other)</code> - <code>self // other</code>
li <code>__mod__(self, other)</code> - <code>self % other</code>
li <code>__lshift__(self, other)</code> - <code>self << other</code>
li <code>__rshift__(self, other)</code> - <code>self >> other</code>
li <code>__and__(self, other)</code> - <code>self & other</code>
li <code>__xor__(self, other)</code> - <code>self ^ other</code>
li <code>__or__(self, other)</code> - <code>self | other</code>
= slide 'Преобразуване до стандартни типове' do
ul
li <code>__int__(self)</code> - <code>int(обект)</code>
li <code>__float__(self)</code> - <code>float(обект)</code>
li <code>__complex__(self)</code> - <code>complex(обект)</code>
li <code>__bool__(self)</code> - <code>bool(обект)</code>
= slide 'Обекти, които могат да бъдат извиквани като функции' do
p Можете да дефинирате поведение на обектите си, когато биват извиквани като функции
example:
class Stamp:
def __init__(self, name):
self.name = name
def __call__(self, something):
print("{0} was stamped by {1}".format(something, self.name))
>>> stamp = Stamp("The government")
>>> stamp("That thing there")
That thing there was stamped by The government
= slide 'getattr/setattr' do
p Можем да достъпваме/променяме атрибути на обект динамично
example:
>>> v1 = Vector(1, 1, 1)
>>> getattr(v1, 'y')
1
>>> setattr(v1, 'z', 5)
>>> getattr(v1, 'z')
5
= slide 'Статични методи' do
p Можете да дефинирате методи, така че те да са статични -- т.е. да нямат нужда от инстанция
example:
class GoatSimulator:
goats = []
@staticmethod
def register(name):
GoatSimulator.goats.append(name)
print(len(GoatSimulator.goats), " goats are registered now")
>>> GoatSimulator.register("Pip the Happy Goat")
1 goats are registered now
>>> GoatSimulator.register("George the Gutsy Goat")
2 goats are registered now
= slide 'Класови методи' do
p Можете да използвате и @classmethod, за да получите класа от който е извикан метода като първи аргумент
example:
class Countable:
_count = 0
def __init__(self, data):
self.data = data
type(self).increase_count()
@classmethod
def increase_count(cls):
cls._count += 1
@classmethod
def decrease_count(cls):
cls._count -= 1
= slide '"Деструктор"' do
p Когато обект от даден тип бъде събран от garbage collector-а, се извиква неговия <code>__del__</code> метод.
p Той не прави това, което прави деструктора в C++, а неща от по-високо ниво, например затваря мрежови връзки или файлове, изпраща съобщения до някъде, че нещо е приключило и прочее.
example:
class Countable:
_count = 0
def __init__(self, data):
self.data = data
type(self).increase_count()
@classmethod
def increase_count(cls):
cls._count += 1
@classmethod
def decrease_count(cls):
cls._count -= 1
def __del__(self):
type(self).decrease_count()
= slide 'Property методи' do
p Декораторът <code>@property</code> може се използва за да накарате някой метод да се преструва на property
example:
class Goat:
def __init__(self, name, trait):
self.name = name
self.trait = trait
@property
def description(self):
return "{} the {} goat".format(self.name, self.trait)
>>> g = Goat("George", "Gutsy")
>>> g.description
'George the Gutsy goat'
= slide 'Property методи (2)' do
ul
li.action Но можем ли да променим стойността на метод, който се преструва на атрибут?
li.action На помощ идват setter-ите
= slide 'Property методи (3)' do
example:
class Color:
def __init__(self, rgba):
self._rgba = tuple(rgba)
@property
def rgba(self):
return self._rgba
@rgba.setter
def rgba(self, value):
self._rgba = tuple(value)
>>> red = Color([255,0,0])
>>> red.rgba
(255, 0, 0)
>>> red.rgba = [127,0,0]
>>> red.rgba
(127, 0, 0)
= slide 'Vector (6)' do
example:
def addition(a, b):
return Vector(a.x + b.x, a.y + b.y, a.z + b.z)
class Vector:
def __init__(self, x, y, z): ...
__add__ = addition
print(Vector(1.0, 2.0, 3.0) + Vector(4.0, 5.0, 6.0))
ol
li.action Функциите са първокласни обекти
li.action Методите са атрибути на класа
li.action Класовете са динамични
li.action Ето защо self е явен
= slide '' do
p ООП-то в python е голямо, сложно, страшно елегантно и изключително интересно
img src='04/rabbit_hole.jpg'