You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+107Lines changed: 107 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -157,6 +157,7 @@ class Dice:
157
157
158
158
## Getters and Setters
159
159
160
+
### Getters
160
161
So far we have seen how the `__get__` method is called when we assign an instance of a descriptors to a class attribute.
161
162
But we can access that attribute either from the class itself, or the instance - as we saw in the last lecture, both accesses end up calling the `__get__` method.
162
163
So, when __get__ is called, we may want to know:
@@ -302,3 +303,109 @@ rocket1.countdown
302
303
```
303
304
304
305
As you can see, the current countdown value is shared by both `rocket1` and `rocket2` instances of `Rocket` - this is because the `Countdown` instance is a class attribute of `Rocket`. So we have to be careful how we deal with instance level state.
306
+
307
+
308
+
### Setters
309
+
The `__set__` method works in a similar way to `__get__` but it is used when we assign a value to the class attribute.
310
+
`__set__` signature is as follows: self, instance, value
311
+
- instance: the instance the __set__ method was called from
312
+
- value: the value we want to assign to the attribute
313
+
314
+
You'll notice there is no owner_class like we have in the `__get__` method
315
+
setters (and deleters) are always called from instances
>'__set__ called, instance=<__main__.Point2D object at 0x000001E7564084D0>, value=100'
335
+
```
336
+
337
+
## Caveat with Set and Delete (and Get)
338
+
Notice that we have only created a single instance of the `TimeUTC` descriptor:
339
+
```python
340
+
classLogger:
341
+
current_time = TimeUTC()
342
+
```
343
+
So what happens when we do this?
344
+
```python
345
+
l1 = Logger()
346
+
l2 = Logger()
347
+
```
348
+
Any instance of `Logger` will be referencing the same instance of `TimeUTC` in this case it does not matter because `__get__` just returns the current UTC time
349
+
But what happens when we have to "store" and "retrieve" data from the instances?
350
+
Suppose `IntegerValue` is a data descriptor -> implements `__get__` and `__set__` methods
351
+
```python
352
+
classPoint2D:
353
+
x = IntegerValue()
354
+
y = IntegerValue()
355
+
# two separate instances of IntegerValue assigned to the class attributes x and y
356
+
357
+
p1 = Point2D()
358
+
p2 = Point2D()
359
+
# two separate instances of Point2D
360
+
```
361
+
But what object does p1.x reference? -> the class attribute x
362
+
what about p2.x? -> the same class attribute x (the same instance of IntegerValue)
363
+
364
+
we have to be mindful of which instance we are "storing" the data for
365
+
this is one of the reasons both __get__ and __set__ need to know the **instance**
366
+
367
+
## Storing
368
+
So, where should we store the values `x` and `y` in the previous example?
369
+
370
+
### Approach 1
371
+
Many "tutorials" I see on the web naively store the value in the descriptor itself:
372
+
```python
373
+
classIntegerValue:
374
+
def__set__(self, instance, value):
375
+
self._value =int(value)
376
+
377
+
def__get__(self, instance, owner_class):
378
+
if instance isNone:
379
+
returnself
380
+
else:
381
+
returnself._value
382
+
383
+
classPoint2D:
384
+
x = IntegerValue()
385
+
y = IntegerValue()
386
+
```
387
+
At first blush, this seems to work just fine:
388
+
```python
389
+
p1 = Point2D()
390
+
p2 = Point2D()
391
+
392
+
p1.x =1.1
393
+
p1.y =2.2
394
+
395
+
print(p1.x, p1.y)
396
+
> (1, 2)
397
+
```
398
+
But, remember the point I was making about the instance of the descriptor (`IntegeraValue` in this case) being shared by all instances of the class (`Point2D` in this case)?
399
+
```python
400
+
p2 = Point2D()
401
+
print(p2.x, p2.y)
402
+
> (1, 2)
403
+
```
404
+
And of course if we set the value:
405
+
```python
406
+
p2.x =100.9
407
+
print(p2.x, p1.x)
408
+
> (100, 100)
409
+
```
410
+
So, obviously using the descriptor instance dictionary for storage at the instance level is probably not going to work in most cases!
411
+
And this is the reason both the `__get__` and `__set__` methods need to know which instance we are dealing with.
0 commit comments