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
@@ -405,7 +405,114 @@ And of course if we set the value:
405
405
```python
406
406
p2.x =100.9
407
407
print(p2.x, p1.x)
408
+
408
409
> (100, 100)
409
410
```
410
411
So, obviously using the descriptor instance dictionary for storage at the instance level is probably not going to work in most cases!
411
412
And this is the reason both the `__get__` and `__set__` methods need to know which instance we are dealing with.
413
+
414
+
### Approach 2
415
+
We are going to **Assuming** that the `instance` is a hashable object, and use a dictionary in the descriptor to store instance specific values.
416
+
```python
417
+
classIntegerValue:
418
+
def__init__(self):
419
+
self.values = {}
420
+
421
+
def__set__(self, instance, value):
422
+
self.values[instance] =int(value)
423
+
424
+
def__get__(self, instance, owner_class):
425
+
if instance isNone:
426
+
returnself
427
+
else:
428
+
returnself.values.get(instance)
429
+
```
430
+
431
+
```python
432
+
classPoint2D:
433
+
x = IntegerValue()
434
+
y = IntegerValue()
435
+
436
+
p1 = Point2D()
437
+
438
+
p1.x =10.1
439
+
p1.y =20.2
440
+
441
+
p1.x, p1.y
442
+
> (10, 20)
443
+
```
444
+
In fact, we can see the dictionary in the descriptor instances:
445
+
```python
446
+
Point2D.x.values
447
+
> {<__main__.Point2D at 0x212d93d9710>: 10}
448
+
Point2D.y.values
449
+
> {<__main__.Point2D at 0x212d93c2110>: 20}
450
+
451
+
```
452
+
where the key in both of these is our `p1` object
453
+
We can now create a second point, and go through the same steps:
454
+
```python
455
+
p2 = Point2D()
456
+
p2.x =100.1
457
+
p2.y =200.2
458
+
459
+
Point2D.x.values
460
+
> {<__main__.Point2D at 0x212d93b2290>: 10,
461
+
><__main__.Point2D at 0x212d931c8d0>: 100}
462
+
463
+
Point2D.y.values
464
+
> {<__main__.Point2D at 0x212d93b2290>: 20,
465
+
><__main__.Point2D at 0x212d931c8d0>: 200}
466
+
```
467
+
And everything works just fine ( Or does it?? ):
468
+
```bash
469
+
p1.x, p1.y, p2.x, p2.y
470
+
> (10, 20, 100, 200)
471
+
```
472
+
We actually have a potential memory leak - notice how the dictionary in the desccriptor instance is **also** storing a reference to the point object - as a **key** in the dictionary.
473
+
474
+
---
475
+
Let's write a simple utility function that allows us to get the reference count for an object given it's id (and it only makes sense if the id we use still has a valid non-destroyed object):
476
+
```python
477
+
import ctypes
478
+
479
+
defref_count(address):
480
+
return ctypes.c_long.from_address(address).value
481
+
482
+
p1 = Point2D()
483
+
id_p1 =id(p1)
484
+
485
+
ref_count(id_p1)
486
+
>1
487
+
```
488
+
Now let's set the `x` property of `p1`:
489
+
```python
490
+
p1.x =100.1
491
+
```
492
+
And let's check the ref count again:
493
+
```python
494
+
ref_count(id_p1)
495
+
>2
496
+
```
497
+
As you can see it's now `2`. if we delete our main reference to `p1` that is in our global namespace:
498
+
```python
499
+
'p1'inglobals()
500
+
>True
501
+
del p1
502
+
'p1'inglobals()
503
+
>False
504
+
ref_count(id_p1)
505
+
>1
506
+
```
507
+
And our reference count is still `1`, which means the object itself has not been destroyed!
508
+
In fact, we can see that object referenced in our data descriptor dictionary:
509
+
```python
510
+
Point2D.x.values.items()
511
+
> dict_items([(<__main__.Point2D object at 0x00000212D93B2290>, 10), (<__main__.Point2D object at 0x00000212D931C8D0>, 100), (<__main__.Point2D object at 0x00000212D9390150>, 100)])
512
+
```
513
+
As you can see, the last element's key is the same id as what `p1` was referencing.
514
+
So, although we deleted `p1`, the object was not destroyed - this can result in a memory leak.
515
+
There are a few ways we can handle this issue. The first one we are going to look at is something called **weak references**. So let's segway into that next.
0 commit comments