-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSlides.txt
More file actions
619 lines (456 loc) · 19.2 KB
/
Copy pathSlides.txt
File metadata and controls
619 lines (456 loc) · 19.2 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
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
---
Josh Cannon, aka "Dunder Seuss" has written no books so far, has been on a single podcast, and done
exactly one talk before he decided to write this book.
Josh is a Build Engineer by day,
a maintainer of the Pantsbuild Open Source Build System also by day.
And sometimes participates in other open source projects, Python community discussions,
and conference speaking... also by day.
He's a lover of getting to know enough Python to make expressive, readable, and intuitive code.
He wrote this book to teach others of the power and magic that you can bring to your objects to make them
expressive, readable, and intuitive.
---
---
Congratulations!
Today is your day.
You will soon learn you some magic,
the good and proper way.
You've chosen a wise way to spend your precious time
by learning from a crazy loon going on in rhyme
about a list of methods that are nicknamed "the dunders"
and how you'll use them to code great wonders
---
They start with two underscores, and end with two more
thus "dunder" which is short for "double underscore"
The runtime of Python calls them in many situations
allowing _your_ objects many customizations
---
You'll learn all about how to emulate a container
and iterators and numbers from a great explainer
Comparisons, callables, and descriptors too,
and how to make attributes appear out of the blue.
You'll learn some new names, and I know you'll learn many
at least one if not two, maybe even up to twenty
By the end you'll know magic, know magic galore
you'll be so full of magic, you'll yell "please sir no more"!
So with great skillful skill, and lots of caffeine
let's start off your learning... with a method you've seen
---
Let's pretend you're new to Python, just for a minute
you'll then learn the first magic: `__init__`
It is almost every object's _initializer_,
allowing you to be an attribute organizer
adding new attributes to your fresh new object.
The most common magic, I truly do suspect.
---
But,
where did `self` come from?
You'll soon start to wonder
Then you'll learn that its created by just another 'dunder
One that _constructs_ the blank object to give to you
And it's name, you'll soon find, is `__new__`.
---
It's a special static method you'll maybe define in your class
which returns _some_ object, then onward its passed,
to whom it turns out depends on the returned object's type
if its an instance of your class (including a possible subtype)
"it goes to `__init__`" you'll hear from the scholar
"but otherwise", he'll says, "it's back to the caller"
---
You'll see this in action, and it'll be more clear-y
by looking for it's use in the standard library
`pathlib.Path` uses this method to help it perform
constructing an object specific to _your_ platform.
The type, as you'll find, has defined `__new__`,
to return a subclass' instance specific to _you_.
---
Now, with great careful care, and great tactful tact,
you'll see using it is a great balancing act.
A magic with the power to create objects that are new,
is something to avoid misuse of, too.
---
And speaking of balance...
the opposite of `__new__` is not oft employed,
`__del__` is called right before your object is destroyed.
You'll use it to release resources you've acquired
but only if you want, its definition isn't _required_
But if you do write it, heed this warning as well,
you **MUST** call your `super()`'s `__del__`.
---
The next set of magics involve conjuring illusions,
of attributes, giving your callers delusions
that your object has more (or less) than it does
why? friendly interfaces is likely the because.
---
First on your journey through attribute emulation,
`__getattr__` is the proper incantation
you'll type it out with a flick and a twist
and you'll synthesize attributes that don't exist.
It's called when default attribute access fails,
The attribute simply wasn't in the details.
You'll get a chance to pretend it existed
However, on your object, the attribute isn't persisted.
Now, if you wish to pretend you don't recognize this `name`,
`raise AttributeError` emulates that all the same.
---
It has an alter-ego, named `__getattribute__`, you'll see
which is called for all access, _unconditionally_
It gets called for names both existing and not,
but beware, infinite recursion is easily got
if you happen to type out "self-dot".
So remember when you need to access your attributes inside of your thing,
you wont use `self.`, you'll give your `super()` a ring
---
To juxtapose "get", `__setattr__` is how,
attribute _setting_, your classes allow.
Again, by default the attribute isn't persisted,
you get to choose whether it becomes listed.
This is also called for all attributes without any condition,
(whether it exists or not) without your permission.
---
The last of the attr methods, you'll maybe leave off,
define `__delattr__`, and people might scoff.
As you'll probably guess its good for emulation
of the removal of a name from your object's formation.
And just like `__setattr__`'s unfortunate asymmetry,
it gets called for all names, unconditionally.
---
You'll then find out soon enough,
that when it comes to illusory stuff
attribute names is just where it starts
there's more you'll learn: another TWO parts
yes, like all good things these magics come in three
in more ways than one, you'll soon see
the first way is the trio of names "get", "set", and "del"
then how they're used for other illusions as well
The second in our trio-of-trios, I'll teach to you now,
these methods are so powerful. You'll see how,
an attribute gets to customize _itself_
instead of sitting _static_ on some other object's shelf.
---
"Descriptors" is the name given to this technique
of attributes themselves, using doublespeak.
First these things work as attributes of a class
(you'll see Django and SQLAlchemy use this en masse)
The "descriptor" is the attribute, and it gets a say
on how _it_ gets gotted, setted, and deleted, per se
---
`__get__` is the first of these spells you'll want to perfect
conjuring values for attributes based on the owner's object
(or sometimes the class, as callers sometimes will do,
so your `__get__` should support that too)
---
And usage of descriptors you'll find sooner, not later,
they're used for several built in decorators,
`staticmethod`, `classmethod`, and `property` all use
this technique to customize your attributes for you
---
Just like `__getattr__`, `__get__` has two brothers,
`__set__` and `__delete__` are the others.
They act just like `__get__` in proxying a call,
and can do anything they want, both big and small.
---
You've maybe have wondered, and even had a theory,
how SQL ORM's quickly fire off a query,
when you access certain attributes in your class you set
the "Column" "descriptor" is leveraging `__get__`
to run a SQL query, using the instance's database ID,
and return to you the value (and maybe cache it, you see).
You'll see `__set__` gets called for attribute assignment,
A SQL `UPDATE` is likely used for new value enshrinement
Lastly, `__delete__` when an attribute is told to go bye-bye
a SQL `DELETE` you'll likely see fly by.
---
There's one more method, that plays in this game,
and it's a method that goes by `__set_name__`
our "trio" really is four, oh well, what a shame.
At the end of your class definition, you see,
for all of the class attributes that be,
if they define a `__set_name__`,
the attribute's name, Python will disclaim.
---
And thus the _attribute_ shell game, now comes to a close
the illusions of _attributes_, we have now exposed
the last trio-of-trios, you'll learn from your trainer,
is emulating _items_ inside of a container
---
You'll see this time our trio's suffix is `item`,
to help quack like containers with things inside 'em
They are given the key (and in one case, the value)
implementing container semantics are then up to you.
---
`__getitem__` has different behaviors on the radar
depending on the type of container your are
Your sequence types (which quack like a tuple or a list)
will accept keys as integers and slice objects
Negative int support is something you can choose
to allow or not, is simply up to you.
If a value provided is outside of your bounds
an `IndexError` your code should resound
---
otherwise, if your container has a Mapping background,
you'll raise `KeyError` if the key isn't found
---
and in every case, if you you reject the key's type
`raise TypeError` you'll then want to gripe
---
You'll likely learn too,
those rules still hold true,
for the other methods two
---
And, as far as containers go, there are a few more dunders
you'll want to define, lest you commit several blunders
So although our trio of trios may have come to a close
you'll want to learn the other container methods I suppose.
---
A quick one that you'll want to define,
is `__len__` which helps Python divine
the length of your container, so when people cal `len`,
Python can return the number back to them.
---
The second one is `__iter__`, which should return an iterator
over the objects that all live inside your object container,
unless its a mapping then what your caller sees,
is simply all of the mapping's keys.
There's also this trivia, a bit of Python fun,
if your container isn't iterable, set `__iter__` to `None`!
---
Now, third on our extras is named `__contains__`,
to test if an object, your container maintains
Although technically, you don't have to define it, Python won't be bitter
---
Instead it'll test membership first using `__iter__`.
Going over every possible object that your object can contain,
and asking if any of those objects are equal or the same.
---
But if you also don't define a `__iter__` method,
`__getitem__` is called and repeatedly tested,
using incrementing indexes from 0 until it then gets
an `IndexError` exception or an equal/same object.
So it's best to define it, so you'll have control,
just how the object membership test will unroll.
---
And if for optional methods you'll have started to thirst,
another one available is `__reversed__`.
It returns an iterator for doing backwards iteration,
---
but you'll only define it, if you beat the default computation,
that Python uses combining `__getitem__` and `__len__`
indexing backwards to 0, and then...
---
There's one more optional method if you subclass `dict`
`__missing__` can be defined so that `__getitem__` can predict
what value to use, if the key in your mapping isnt yet there
It's how `collections.` `defaultdict` or `Counter`, with care
support operations on items conjured out of thin air.
Python just returns to your caller, the value itself,
so if you want to, dont forget to store it in yourself.
---
You'll find that you're done,
you've mastered container emulation
But your next set of magics form quite the combination
See, you briefly dipped your toe into the "index" operator
however you'll find the list of ops to support is oh so much greater
You'll think about `+` and `-` and start figuring
that the list of operators is biggering and biggering
---
Let's start with just one that you can define,
`__add__` lets you support the plus sign
when your object is on the left with whatevers on the right
you return the added value, except when you might
reject the operation, because you don't know what to do,
and instead return the `NotImplemented` singleton value
such is the case if you dont recognize the type
of whatever the thing is on the right.
---
Then...
`__sub__` for subtraction, `__mul__` for times,
`__truediv`- of `floordiv` depending on the kind,
of division you want, true or integer, respectively
using one slash or two, for division, collectively.
And speaking of operations that come in pairs,
modulo arithmetic is a double-dunder-affair,
with `__mod__` for modulo support and then,
`__divmod__` to support the `divmod` builtin.
The last of the pairs of magic method gifts,
is `__l`- and `__rshift`
Then next up is `__pow__` for "to-the-power-of" support
whose operator is two askerisks, side-by-side, for short
and then, although the list was already so long
three more dunder methods will came along
`__and` - `xor`- and `or__`, oh come now, don't gripe,
they're how you support ampersand, caret and pipe.
And then, when your object supports the at-symbol,
the method you'll want is named `__matmul__`
AND THEN, that was it, there won't be more later
FOURTEEN methods for numeric operators,
unless...
Unless you think that this isn't enough.
[[Here let's take a poll, is there other number stuff
we need to define? If yes raise your hand]]
I see. I suppose our list should expand.
---
You'll maybe be asking yourself "O' teacher, how come?"
You'll ask yourself where these new methods are from
and how come my methods sometimes return `NotImplemented`,
if, to my caller, a `TypeError` is presented?
---
And so our list of methods then expands,
to support the same things with swapped operands,
but only if the result is `NotImplemented`
AND if two different types are presented,
Python tries again but this time with an "R",
at the front of the name, (it's not _that_ bizarre)
and calls this other method on the thing on the right,
let's see an example to bring how this works to light
---
Let's say someone subtracts from a `list` your `Foo`,
well `list` doesn't know what the heck to do,
so it's `__sub__`, then returns `NotImplemented`,
then the runtime continues on as documented,
noticing that `list` and `Foo` are different classes
and calls `Foo`'s `__rsub__`, and it passes
the listuplet object on which we've acted
so `Foo` can say how it should be subtracted.
Now remember, you'll have to be dextrous and deft,
and try not to mix up a right "op" with a left,
when in an "r-method" you're the one on the right
getting this correct will make you seem bright
---
[[How about another poll? That last one was fun.
Who thinks our numeric operator list is done?]]
Me neither. As it turns out, and you'll soon enough see
all but one of these operators' support requires THREE.
THREE dunders, at most, for each of these things
what _joy_ to us, supporting operators brings
---
"So what sets _these_ apart?" You'll groan and you'll grunt.
These last set of thirteen has an "i" in the front.
They're meant to support doing the math "in-place",
mutating the object given in the left space,
for instance, `+=` uses `__iadd__`,
(completing the addition support triad).
---
However these methods you can actually omit,
they're there to help avoid copies a bit,
if one of these methods your type is lacking,
then `x = x + y` will be the fallbacking
---
If you kept watch, you might think I left one for later,
but no, `__divmod__` has no in-place operator.
---
Now, oh baby oh, how the operator list will still grow!
Regardless of lengthy class definitions you know.
You'll complain, and you'll curse, and you might even swear,
but you'll still need to learn the dunders to _compare_
---
The names of these methods really aren't at all surprising,
the brevity of each name is what's really quite appetizing,
using only 2 letters to represent each operator
for operations equality, lesser, and greater.
And exactly like the methods that I just reported
they return `NotImplemented` if the comparison isn't supported,
And if you want, `__ne__` you can simply omit,
`__eq__` if defined, will be tested a bit
and the return is inverted, unless `NotImplemented`
however this time there's no crazy switch-a-roo,
in this case a `TypeError` is raised unto you
And in the case the comparison is ok
a True or False as a return is the way.
Or anything truthy or falsey is ok,
it's turned into a boolean the Pythonic way.
---
Oh wait, forgive me, that's actually a new dunder
magics on magics, isn't Python a wonder.
If you want to make your object seem `Falsey` or `True`?
You'll have to define `__bool__` too.
Unless perhaps you define a `__len__`,
An empty container is Falsey, then.
---
Last ones on our list (and yes it is still growing,
brevity, at this point we are simply forgoing)
You'll only need four more, so no need to get wary,
to support the operators _unary_.
`__neg`- `pos`, `abs`, and `invert` you'll learn
for `-`, `+`, `abs(`, and `~` `x`, in turn
That's it, you'll think, our list is complete.
And you're right, I think, now isn't that neat
55-ish methods to support the operators
no list of magics in one section is greater
---
Actually, not true, as you'll find out
emulating a number gets to tout
the longest list of required magic capitulators
because in addition to most of those operators
there's even more you'll want to support, you'll see
numbers, I guess, just have lots of flexibility
---
From mathematics, the list adds on four,
__round, trunc, ceil and floor.
What these methods each do, you'll notice is visible,
truncating the value into an `Integral`
Then, from a number you'll have several excursions,
if your object supports any of several conversions.
---
`__complex, int, float, bytes, and str
are magics which Python will look and refer
to make the type conversion occur
---
And speaking of strings, they've got special magic too
`__repr__` and `format` will be waiting for you
`__repr__` returning the “official” string representation of your object
an executable string of your object is what callers expect
And for `__format__` you'll choose exactly how to trek
through formatting your object, based on the spec
---
The other magics all come in small lists,
and in this tome, some of them won't exist,
like the 11 methods for copying and/or pickling
yet, here are some more, for you, coming in at a trickling
---
If you want your object to quack like a function
`__call__` is the callable magic junction
---
Or maybe you'll quack, perhaps like an iterator,
`__next__` is the method for that imitator
returning the next value, or the special terminator
which goes by the name of `StopIteration`,
you raise it when there's no more values in your formation
---
And if you want something that supports use with `with`,
`__enter-` and `exit`, says the wordsmith
The former is issued right before the inner scope
`return self` is usually the trope
the latter is called when the inner scope is done
whether it finished or ran into an exception
This method has power, in multiple ways,
it can release acquired resources always,
but it also gets a chance to suppress an exception
`return True` to complete the interception
---
And then there are some, which feels kinda wacky,
defining them sometimes feels a bit tacky
`__init_subclass__` if defining it you have dared
every time a subclass of yours is declared
it receives that subclass, as quick as molasses,
and was added to wean off some use of metaclasses
And speaking of metaclasses there's `__prepare__`
on second thought, you'll not want to go there...
---
The last set of magics on this magicallest of treks
are for hooks into instance and subclass checks
That's right, the objects you write actually gets a say,
on the answer of `isinstance` and `issubclass`: yay or nay
However, these are looked up on the type of your class.
(Ugh metaclasses, How about we pass?)
---
And actually that's it, no more magic I'll disclose
your journey, you've journeyed now comes to a close
To know most of them, including the ones this talk doesn't entail,
read docs.python.org/3/reference/datamodel.html
(or just ask Google for the URL)
since, most of the magics, that page does detail
Now, since your mountain is waiting, and as you go on your way,
remember the things I rememembered to you today,
and write all your code, the good, Pythonic way.
---
The End.