-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathdescriptor.html
More file actions
1678 lines (1535 loc) · 171 KB
/
Copy pathdescriptor.html
File metadata and controls
1678 lines (1535 loc) · 171 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
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="fa" data-content_root="../">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="viewport" content="width=device-width, initial-scale=1" />
<meta property="og:title" content="Descriptor Guide" />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://docs.python.org/3/howto/descriptor.html" />
<meta property="og:site_name" content="Python documentation" />
<meta property="og:description" content="Author, Raymond Hettinger,, Contact,<python at rcn dot com>,. محتوا: Descriptor Guide- Primer- Simple example: A descriptor that returns a constant, Dynamic lookups, Managed attributes, Custo..." />
<meta property="og:image" content="_static/og-image.png" />
<meta property="og:image:alt" content="Python documentation" />
<meta name="description" content="Author, Raymond Hettinger,, Contact,<python at rcn dot com>,. محتوا: Descriptor Guide- Primer- Simple example: A descriptor that returns a constant, Dynamic lookups, Managed attributes, Custo..." />
<meta name="theme-color" content="#3776ab">
<meta property="og:image:width" content="200">
<meta property="og:image:height" content="200">
<title>Descriptor Guide — مستندات Python3.14.6</title><meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="../_static/pygments.css?v=b86133f3" />
<link rel="stylesheet" type="text/css" href="../_static/classic.css?v=234b1a7c" />
<link rel="stylesheet" type="text/css" href="../_static/pydoctheme.css?v=4365c8fe" />
<link id="pygments_dark_css" media="(prefers-color-scheme: dark)" rel="stylesheet" type="text/css" href="../_static/pygments_dark.css?v=0fc419ee" />
<script src="../_static/documentation_options.js?v=e254dbbb"></script>
<script src="../_static/doctools.js?v=9bcbadda"></script>
<script src="../_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="../_static/translations.js?v=5df48d09"></script>
<script src="../_static/sidebar.js"></script>
<link rel="search" type="application/opensearchdescription+xml"
title="جستجو در مستندات Python3.14.6"
href="../_static/opensearch.xml"/>
<link rel="author" title="درباره این مستندات" href="../about.html" />
<link rel="index" title="فهرست" href="../genindex.html" />
<link rel="search" title="جستجو" href="../search.html" />
<link rel="copyright" title="حق چاپ" href="../copyright.html" />
<link rel="next" title="Debugging C API extensions and CPython Internals with GDB" href="gdb_helpers.html" />
<link rel="prev" title="Curses Programming with Python" href="curses.html" />
<link rel="canonical" href="https://docs.python.org/3/howto/descriptor.html">
<style>
@media only screen {
table.full-width-table {
width: 100%;
}
}
</style>
<link rel="stylesheet" href="../_static/pydoctheme_dark.css" media="(prefers-color-scheme: dark)" id="pydoctheme_dark_css">
<link rel="shortcut icon" type="image/png" href="../_static/py.svg">
<script type="text/javascript" src="../_static/copybutton.js"></script>
<script type="text/javascript" src="../_static/menu.js"></script>
<script type="text/javascript" src="../_static/search-focus.js"></script>
<script type="text/javascript" src="../_static/themetoggle.js"></script>
<script type="text/javascript" src="../_static/rtd_switcher.js"></script>
<meta name="readthedocs-addons-api-version" content="1">
</head>
<body>
<div class="mobile-nav">
<input type="checkbox" id="menuToggler" class="toggler__input" aria-controls="navigation"
aria-pressed="false" aria-expanded="false" role="button" aria-label="Menu">
<nav class="nav-content" role="navigation">
<label for="menuToggler" class="toggler__label">
<span></span>
</label>
<span class="nav-items-wrapper">
<a href="https://www.python.org/" class="nav-logo">
<img src="../_static/py.svg" alt="Python logo">
</a>
<span class="version_switcher_placeholder"></span>
<form role="search" class="search" action="../search.html" method="get">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" class="search-icon">
<path fill-rule="nonzero" fill="currentColor" d="M15.5 14h-.79l-.28-.27a6.5 6.5 0 001.48-5.34c-.47-2.78-2.79-5-5.59-5.34a6.505 6.505 0 00-7.27 7.27c.34 2.8 2.56 5.12 5.34 5.59a6.5 6.5 0 005.34-1.48l.27.28v.79l4.25 4.25c.41.41 1.08.41 1.49 0 .41-.41.41-1.08 0-1.49L15.5 14zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"></path>
</svg>
<input placeholder="جستجو سریع" aria-label="جستجو سریع" type="search" name="q">
<input type="submit" value="برو">
</form>
</span>
</nav>
<div class="menu-wrapper">
<nav class="menu" role="navigation" aria-label="main navigation">
<div class="language_switcher_placeholder"></div>
<label class="theme-selector-label">
Theme
<select class="theme-selector" oninput="activateTheme(this.value)">
<option value="auto" selected>Auto</option>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
</label>
<div>
<h3><a href="../contents.html">فهرست عناوین</a></h3>
<ul>
<li><a class="reference internal" href="#">Descriptor Guide</a><ul>
<li><a class="reference internal" href="#primer">Primer</a><ul>
<li><a class="reference internal" href="#simple-example-a-descriptor-that-returns-a-constant">Simple example: A descriptor that returns a constant</a></li>
<li><a class="reference internal" href="#dynamic-lookups">Dynamic lookups</a></li>
<li><a class="reference internal" href="#managed-attributes">Managed attributes</a></li>
<li><a class="reference internal" href="#customized-names">Customized names</a></li>
<li><a class="reference internal" href="#closing-thoughts">Closing thoughts</a></li>
</ul>
</li>
<li><a class="reference internal" href="#complete-practical-example">Complete Practical Example</a><ul>
<li><a class="reference internal" href="#validator-class">Validator class</a></li>
<li><a class="reference internal" href="#custom-validators">Custom validators</a></li>
<li><a class="reference internal" href="#practical-application">Practical application</a></li>
</ul>
</li>
<li><a class="reference internal" href="#technical-tutorial">Technical Tutorial</a><ul>
<li><a class="reference internal" href="#abstract">Abstract</a></li>
<li><a class="reference internal" href="#definition-and-introduction">Definition and introduction</a></li>
<li><a class="reference internal" href="#descriptor-protocol">Descriptor protocol</a></li>
<li><a class="reference internal" href="#overview-of-descriptor-invocation">Overview of descriptor invocation</a></li>
<li><a class="reference internal" href="#invocation-from-an-instance">Invocation from an instance</a></li>
<li><a class="reference internal" href="#invocation-from-a-class">Invocation from a class</a></li>
<li><a class="reference internal" href="#invocation-from-super">Invocation from super</a></li>
<li><a class="reference internal" href="#summary-of-invocation-logic">Summary of invocation logic</a></li>
<li><a class="reference internal" href="#automatic-name-notification">Automatic name notification</a></li>
<li><a class="reference internal" href="#orm-example">ORM example</a></li>
</ul>
</li>
<li><a class="reference internal" href="#pure-python-equivalents">Pure Python Equivalents</a><ul>
<li><a class="reference internal" href="#properties">Properties</a></li>
<li><a class="reference internal" href="#functions-and-methods">Functions and methods</a></li>
<li><a class="reference internal" href="#kinds-of-methods">Kinds of methods</a></li>
<li><a class="reference internal" href="#static-methods">Static methods</a></li>
<li><a class="reference internal" href="#class-methods">Class methods</a></li>
<li><a class="reference internal" href="#member-objects-and-slots">Member objects and __slots__</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
<div>
<h4>موضوع قبلی</h4>
<p class="topless"><a href="curses.html"
title="فصل قبلی">Curses Programming with Python</a></p>
</div>
<div>
<h4>موضوع بعدی</h4>
<p class="topless"><a href="gdb_helpers.html"
title="فصل بعدی">Debugging C API extensions and CPython Internals with GDB</a></p>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const title = document.querySelector('meta[property="og:title"]').content;
const elements = document.querySelectorAll('.improvepage');
const pageurl = window.location.href.split('?')[0];
elements.forEach(element => {
const url = new URL(element.href.split('?')[0].replace("-nojs", ""));
url.searchParams.set('pagetitle', title);
url.searchParams.set('pageurl', pageurl);
url.searchParams.set('pagesource', "howto/descriptor.rst");
element.href = url.toString();
});
});
</script>
<div role="note" aria-label="source link">
<h3>این صفحه</h3>
<ul class="this-page-menu">
<li><a href="../bugs.html">گزارش یک اشکال</a></li>
<li><a class="improvepage" href="../improve-page-nojs.html">بهبود این صفحه</a></li>
<li>
<a href="https://github.com/python/cpython/blob/main/Doc/howto/descriptor.rst?plain=1"
rel="nofollow">نمایش منبع
</a>
</li>
<li>
<a href="https://github.com/python/python-docs-fa/blob/3.14/howto/descriptor.po?plain=1"
rel="nofollow">نمایش منبع ترجمه</a>
</li>
</ul>
</div>
</nav>
</div>
</div>
<div class="related" role="navigation" aria-label="Related">
<h3>ناوبری</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../genindex.html" title="فهرست کلی"
accesskey="I">فهرست</a></li>
<li class="right" >
<a href="../py-modindex.html" title="نمایه ی ماژول های پایتون"
>ماژول ها</a> |</li>
<li class="right" >
<a href="gdb_helpers.html" title="Debugging C API extensions and CPython Internals with GDB"
accesskey="N">بعدی</a> |</li>
<li class="right" >
<a href="curses.html" title="Curses Programming with Python"
accesskey="P">قبلی</a> |</li>
<li><img src="../_static/py.svg" alt="Python logo" style="vertical-align: middle; margin-top: -1px"></li>
<li><a href="https://www.python.org/">Python</a> »</li>
<li class="switchers">
<div class="language_switcher_placeholder"></div>
<div class="version_switcher_placeholder"></div>
</li>
<li>
</li>
<li id="cpython-language-and-version">
<a href="../index.html">3.14.6 Documentation</a> »
</li>
<li class="nav-item nav-item-1"><a href="index.html" accesskey="U">Python HOWTOs</a> »</li>
<li class="nav-item nav-item-this"><a href="">Descriptor Guide</a></li>
<li class="right">
<div class="inline-search" role="search">
<form class="inline-search" action="../search.html" method="get">
<input placeholder="جستجو سریع" aria-label="جستجو سریع" type="search" name="q" id="search-box">
<input type="submit" value="برو">
</form>
</div>
|
</li>
<li class="right">
<label class="theme-selector-label">
Theme
<select class="theme-selector" oninput="activateTheme(this.value)">
<option value="auto" selected>Auto</option>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
</label> |</li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="descriptor-guide">
<span id="descriptorhowto"></span><h1><a class="toc-backref" href="#id2" role="doc-backlink">Descriptor Guide</a><a class="headerlink" href="#descriptor-guide" title="Link to this heading">¶</a></h1>
<dl class="field-list simple">
<dt class="field-odd">Author<span class="colon">:</span></dt>
<dd class="field-odd"><p>Raymond Hettinger</p>
</dd>
<dt class="field-even">Contact<span class="colon">:</span></dt>
<dd class="field-even"><p><python at rcn dot com></p>
</dd>
</dl>
<nav class="contents" id="id1">
<p class="topic-title">محتوا</p>
<ul class="simple">
<li><p><a class="reference internal" href="#descriptor-guide" id="id2">Descriptor Guide</a></p>
<ul>
<li><p><a class="reference internal" href="#primer" id="id3">Primer</a></p>
<ul>
<li><p><a class="reference internal" href="#simple-example-a-descriptor-that-returns-a-constant" id="id4">Simple example: A descriptor that returns a constant</a></p></li>
<li><p><a class="reference internal" href="#dynamic-lookups" id="id5">Dynamic lookups</a></p></li>
<li><p><a class="reference internal" href="#managed-attributes" id="id6">Managed attributes</a></p></li>
<li><p><a class="reference internal" href="#customized-names" id="id7">Customized names</a></p></li>
<li><p><a class="reference internal" href="#closing-thoughts" id="id8">Closing thoughts</a></p></li>
</ul>
</li>
<li><p><a class="reference internal" href="#complete-practical-example" id="id9">Complete Practical Example</a></p>
<ul>
<li><p><a class="reference internal" href="#validator-class" id="id10">Validator class</a></p></li>
<li><p><a class="reference internal" href="#custom-validators" id="id11">Custom validators</a></p></li>
<li><p><a class="reference internal" href="#practical-application" id="id12">Practical application</a></p></li>
</ul>
</li>
<li><p><a class="reference internal" href="#technical-tutorial" id="id13">Technical Tutorial</a></p>
<ul>
<li><p><a class="reference internal" href="#abstract" id="id14">Abstract</a></p></li>
<li><p><a class="reference internal" href="#definition-and-introduction" id="id15">Definition and introduction</a></p></li>
<li><p><a class="reference internal" href="#descriptor-protocol" id="id16">Descriptor protocol</a></p></li>
<li><p><a class="reference internal" href="#overview-of-descriptor-invocation" id="id17">Overview of descriptor invocation</a></p></li>
<li><p><a class="reference internal" href="#invocation-from-an-instance" id="id18">Invocation from an instance</a></p></li>
<li><p><a class="reference internal" href="#invocation-from-a-class" id="id19">Invocation from a class</a></p></li>
<li><p><a class="reference internal" href="#invocation-from-super" id="id20">Invocation from super</a></p></li>
<li><p><a class="reference internal" href="#summary-of-invocation-logic" id="id21">Summary of invocation logic</a></p></li>
<li><p><a class="reference internal" href="#automatic-name-notification" id="id22">Automatic name notification</a></p></li>
<li><p><a class="reference internal" href="#orm-example" id="id23">ORM example</a></p></li>
</ul>
</li>
<li><p><a class="reference internal" href="#pure-python-equivalents" id="id24">Pure Python Equivalents</a></p>
<ul>
<li><p><a class="reference internal" href="#properties" id="id25">Properties</a></p></li>
<li><p><a class="reference internal" href="#functions-and-methods" id="id26">Functions and methods</a></p></li>
<li><p><a class="reference internal" href="#kinds-of-methods" id="id27">Kinds of methods</a></p></li>
<li><p><a class="reference internal" href="#static-methods" id="id28">Static methods</a></p></li>
<li><p><a class="reference internal" href="#class-methods" id="id29">Class methods</a></p></li>
<li><p><a class="reference internal" href="#member-objects-and-slots" id="id30">Member objects and __slots__</a></p></li>
</ul>
</li>
</ul>
</li>
</ul>
</nav>
<p><a class="reference internal" href="../glossary.html#term-descriptor"><span class="xref std std-term">Descriptors</span></a> let objects customize attribute lookup,
storage, and deletion.</p>
<p>This guide has four major sections:</p>
<ol class="arabic simple">
<li><p>The "primer" gives a basic overview, moving gently from simple examples,
adding one feature at a time. Start here if you're new to descriptors.</p></li>
<li><p>The second section shows a complete, practical descriptor example. If you
already know the basics, start there.</p></li>
<li><p>The third section provides a more technical tutorial that goes into the
detailed mechanics of how descriptors work. Most people don't need this
level of detail.</p></li>
<li><p>The last section has pure Python equivalents for built-in descriptors that
are written in C. Read this if you're curious about how functions turn
into bound methods or about the implementation of common tools like
<a class="reference internal" href="../library/functions.html#classmethod" title="classmethod"><code class="xref py py-func docutils literal notranslate"><span class="pre">classmethod()</span></code></a>, <a class="reference internal" href="../library/functions.html#staticmethod" title="staticmethod"><code class="xref py py-func docutils literal notranslate"><span class="pre">staticmethod()</span></code></a>, <a class="reference internal" href="../library/functions.html#property" title="property"><code class="xref py py-func docutils literal notranslate"><span class="pre">property()</span></code></a>, and
<a class="reference internal" href="../glossary.html#term-__slots__"><span class="xref std std-term">__slots__</span></a>.</p></li>
</ol>
<section id="primer">
<h2><a class="toc-backref" href="#id3" role="doc-backlink">Primer</a><a class="headerlink" href="#primer" title="Link to this heading">¶</a></h2>
<p>In this primer, we start with the most basic possible example and then we'll
add new capabilities one by one.</p>
<section id="simple-example-a-descriptor-that-returns-a-constant">
<h3><a class="toc-backref" href="#id4" role="doc-backlink">Simple example: A descriptor that returns a constant</a><a class="headerlink" href="#simple-example-a-descriptor-that-returns-a-constant" title="Link to this heading">¶</a></h3>
<p>The <code class="xref py py-class docutils literal notranslate"><span class="pre">Ten</span></code> class is a descriptor whose <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a> method always
returns the constant <code class="docutils literal notranslate"><span class="pre">10</span></code>:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Ten</span><span class="p">:</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__get__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">objtype</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="k">return</span> <span class="mi">10</span>
</pre></div>
</div>
<p>To use the descriptor, it must be stored as a class variable in another class:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">A</span><span class="p">:</span>
<span class="n">x</span> <span class="o">=</span> <span class="mi">5</span> <span class="c1"># Regular class attribute</span>
<span class="n">y</span> <span class="o">=</span> <span class="n">Ten</span><span class="p">()</span> <span class="c1"># Descriptor instance</span>
</pre></div>
</div>
<p>An interactive session shows the difference between normal attribute lookup
and descriptor lookup:</p>
<div class="highlight-pycon notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">a</span> <span class="o">=</span> <span class="n">A</span><span class="p">()</span> <span class="c1"># Make an instance of class A</span>
<span class="gp">>>> </span><span class="n">a</span><span class="o">.</span><span class="n">x</span> <span class="c1"># Normal attribute lookup</span>
<span class="go">5</span>
<span class="gp">>>> </span><span class="n">a</span><span class="o">.</span><span class="n">y</span> <span class="c1"># Descriptor lookup</span>
<span class="go">10</span>
</pre></div>
</div>
<p>In the <code class="docutils literal notranslate"><span class="pre">a.x</span></code> attribute lookup, the dot operator finds <code class="docutils literal notranslate"><span class="pre">'x':</span> <span class="pre">5</span></code>
in the class dictionary. In the <code class="docutils literal notranslate"><span class="pre">a.y</span></code> lookup, the dot operator
finds a descriptor instance, recognized by its <code class="docutils literal notranslate"><span class="pre">__get__</span></code> method.
Calling that method returns <code class="docutils literal notranslate"><span class="pre">10</span></code>.</p>
<p>Note that the value <code class="docutils literal notranslate"><span class="pre">10</span></code> is not stored in either the class dictionary or the
instance dictionary. Instead, the value <code class="docutils literal notranslate"><span class="pre">10</span></code> is computed on demand.</p>
<p>This example shows how a simple descriptor works, but it isn't very useful.
For retrieving constants, normal attribute lookup would be better.</p>
<p>In the next section, we'll create something more useful, a dynamic lookup.</p>
</section>
<section id="dynamic-lookups">
<h3><a class="toc-backref" href="#id5" role="doc-backlink">Dynamic lookups</a><a class="headerlink" href="#dynamic-lookups" title="Link to this heading">¶</a></h3>
<p>Interesting descriptors typically run computations instead of returning
constants:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span><span class="w"> </span><span class="nn">os</span>
<span class="k">class</span><span class="w"> </span><span class="nc">DirectorySize</span><span class="p">:</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__get__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">objtype</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">listdir</span><span class="p">(</span><span class="n">obj</span><span class="o">.</span><span class="n">dirname</span><span class="p">))</span>
<span class="k">class</span><span class="w"> </span><span class="nc">Directory</span><span class="p">:</span>
<span class="n">size</span> <span class="o">=</span> <span class="n">DirectorySize</span><span class="p">()</span> <span class="c1"># Descriptor instance</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">dirname</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">dirname</span> <span class="o">=</span> <span class="n">dirname</span> <span class="c1"># Regular instance attribute</span>
</pre></div>
</div>
<p>An interactive session shows that the lookup is dynamic — it computes
different, updated answers each time:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">s</span> <span class="o">=</span> <span class="n">Directory</span><span class="p">(</span><span class="s1">'songs'</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">g</span> <span class="o">=</span> <span class="n">Directory</span><span class="p">(</span><span class="s1">'games'</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">s</span><span class="o">.</span><span class="n">size</span> <span class="c1"># The songs directory has twenty files</span>
<span class="go">20</span>
<span class="gp">>>> </span><span class="n">g</span><span class="o">.</span><span class="n">size</span> <span class="c1"># The games directory has three files</span>
<span class="go">3</span>
<span class="gp">>>> </span><span class="n">os</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="s1">'games/chess'</span><span class="p">)</span> <span class="c1"># Delete a game</span>
<span class="gp">>>> </span><span class="n">g</span><span class="o">.</span><span class="n">size</span> <span class="c1"># File count is automatically updated</span>
<span class="go">2</span>
</pre></div>
</div>
<p>Besides showing how descriptors can run computations, this example also
reveals the purpose of the parameters to <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a>. The <em>self</em>
parameter is <em>size</em>, an instance of <em>DirectorySize</em>. The <em>obj</em> parameter is
either <em>g</em> or <em>s</em>, an instance of <em>Directory</em>. It is the <em>obj</em> parameter that
lets the <code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code> method learn the target directory. The <em>objtype</em>
parameter is the class <em>Directory</em>.</p>
</section>
<section id="managed-attributes">
<h3><a class="toc-backref" href="#id6" role="doc-backlink">Managed attributes</a><a class="headerlink" href="#managed-attributes" title="Link to this heading">¶</a></h3>
<p>A popular use for descriptors is managing access to instance data. The
descriptor is assigned to a public attribute in the class dictionary while the
actual data is stored as a private attribute in the instance dictionary. The
descriptor's <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a> and <a class="reference internal" href="../reference/datamodel.html#object.__set__" title="object.__set__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set__()</span></code></a> methods are triggered when
the public attribute is accessed.</p>
<p>In the following example, <em>age</em> is the public attribute and <em>_age</em> is the
private attribute. When the public attribute is accessed, the descriptor logs
the lookup or update:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span><span class="w"> </span><span class="nn">logging</span>
<span class="n">logging</span><span class="o">.</span><span class="n">basicConfig</span><span class="p">(</span><span class="n">level</span><span class="o">=</span><span class="n">logging</span><span class="o">.</span><span class="n">INFO</span><span class="p">)</span>
<span class="k">class</span><span class="w"> </span><span class="nc">LoggedAgeAccess</span><span class="p">:</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__get__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">objtype</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="n">value</span> <span class="o">=</span> <span class="n">obj</span><span class="o">.</span><span class="n">_age</span>
<span class="n">logging</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">'Accessing </span><span class="si">%r</span><span class="s1"> giving </span><span class="si">%r</span><span class="s1">'</span><span class="p">,</span> <span class="s1">'age'</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
<span class="k">return</span> <span class="n">value</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__set__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="n">logging</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">'Updating </span><span class="si">%r</span><span class="s1"> to </span><span class="si">%r</span><span class="s1">'</span><span class="p">,</span> <span class="s1">'age'</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
<span class="n">obj</span><span class="o">.</span><span class="n">_age</span> <span class="o">=</span> <span class="n">value</span>
<span class="k">class</span><span class="w"> </span><span class="nc">Person</span><span class="p">:</span>
<span class="n">age</span> <span class="o">=</span> <span class="n">LoggedAgeAccess</span><span class="p">()</span> <span class="c1"># Descriptor instance</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">age</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span> <span class="c1"># Regular instance attribute</span>
<span class="bp">self</span><span class="o">.</span><span class="n">age</span> <span class="o">=</span> <span class="n">age</span> <span class="c1"># Calls __set__()</span>
<span class="k">def</span><span class="w"> </span><span class="nf">birthday</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">age</span> <span class="o">+=</span> <span class="mi">1</span> <span class="c1"># Calls both __get__() and __set__()</span>
</pre></div>
</div>
<p>An interactive session shows that all access to the managed attribute <em>age</em> is
logged, but that the regular attribute <em>name</em> is not logged:</p>
<div class="highlight-pycon notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">mary</span> <span class="o">=</span> <span class="n">Person</span><span class="p">(</span><span class="s1">'Mary M'</span><span class="p">,</span> <span class="mi">30</span><span class="p">)</span> <span class="c1"># The initial age update is logged</span>
<span class="go">INFO:root:Updating 'age' to 30</span>
<span class="gp">>>> </span><span class="n">dave</span> <span class="o">=</span> <span class="n">Person</span><span class="p">(</span><span class="s1">'David D'</span><span class="p">,</span> <span class="mi">40</span><span class="p">)</span>
<span class="go">INFO:root:Updating 'age' to 40</span>
<span class="gp">>>> </span><span class="nb">vars</span><span class="p">(</span><span class="n">mary</span><span class="p">)</span> <span class="c1"># The actual data is in a private attribute</span>
<span class="go">{'name': 'Mary M', '_age': 30}</span>
<span class="gp">>>> </span><span class="nb">vars</span><span class="p">(</span><span class="n">dave</span><span class="p">)</span>
<span class="go">{'name': 'David D', '_age': 40}</span>
<span class="gp">>>> </span><span class="n">mary</span><span class="o">.</span><span class="n">age</span> <span class="c1"># Access the data and log the lookup</span>
<span class="go">INFO:root:Accessing 'age' giving 30</span>
<span class="go">30</span>
<span class="gp">>>> </span><span class="n">mary</span><span class="o">.</span><span class="n">birthday</span><span class="p">()</span> <span class="c1"># Updates are logged as well</span>
<span class="go">INFO:root:Accessing 'age' giving 30</span>
<span class="go">INFO:root:Updating 'age' to 31</span>
<span class="gp">>>> </span><span class="n">dave</span><span class="o">.</span><span class="n">name</span> <span class="c1"># Regular attribute lookup isn't logged</span>
<span class="go">'David D'</span>
<span class="gp">>>> </span><span class="n">dave</span><span class="o">.</span><span class="n">age</span> <span class="c1"># Only the managed attribute is logged</span>
<span class="go">INFO:root:Accessing 'age' giving 40</span>
<span class="go">40</span>
</pre></div>
</div>
<p>One major issue with this example is that the private name <em>_age</em> is hardwired in
the <em>LoggedAgeAccess</em> class. That means that each instance can only have one
logged attribute and that its name is unchangeable. In the next example,
we'll fix that problem.</p>
</section>
<section id="customized-names">
<h3><a class="toc-backref" href="#id7" role="doc-backlink">Customized names</a><a class="headerlink" href="#customized-names" title="Link to this heading">¶</a></h3>
<p>When a class uses descriptors, it can inform each descriptor about which
variable name was used.</p>
<p>In this example, the <code class="xref py py-class docutils literal notranslate"><span class="pre">Person</span></code> class has two descriptor instances,
<em>name</em> and <em>age</em>. When the <code class="xref py py-class docutils literal notranslate"><span class="pre">Person</span></code> class is defined, it makes a
callback to <a class="reference internal" href="../reference/datamodel.html#object.__set_name__" title="object.__set_name__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set_name__()</span></code></a> in <em>LoggedAccess</em> so that the field names can
be recorded, giving each descriptor its own <em>public_name</em> and <em>private_name</em>:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span><span class="w"> </span><span class="nn">logging</span>
<span class="n">logging</span><span class="o">.</span><span class="n">basicConfig</span><span class="p">(</span><span class="n">level</span><span class="o">=</span><span class="n">logging</span><span class="o">.</span><span class="n">INFO</span><span class="p">)</span>
<span class="k">class</span><span class="w"> </span><span class="nc">LoggedAccess</span><span class="p">:</span>
<span class="k">def</span><span class="w"> </span><span class="nf">__set_name__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">owner</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">public_name</span> <span class="o">=</span> <span class="n">name</span>
<span class="bp">self</span><span class="o">.</span><span class="n">private_name</span> <span class="o">=</span> <span class="s1">'_'</span> <span class="o">+</span> <span class="n">name</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__get__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">objtype</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="n">value</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">private_name</span><span class="p">)</span>
<span class="n">logging</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">'Accessing </span><span class="si">%r</span><span class="s1"> giving </span><span class="si">%r</span><span class="s1">'</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">public_name</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
<span class="k">return</span> <span class="n">value</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__set__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="n">logging</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">'Updating </span><span class="si">%r</span><span class="s1"> to </span><span class="si">%r</span><span class="s1">'</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">public_name</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
<span class="nb">setattr</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">private_name</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
<span class="k">class</span><span class="w"> </span><span class="nc">Person</span><span class="p">:</span>
<span class="n">name</span> <span class="o">=</span> <span class="n">LoggedAccess</span><span class="p">()</span> <span class="c1"># First descriptor instance</span>
<span class="n">age</span> <span class="o">=</span> <span class="n">LoggedAccess</span><span class="p">()</span> <span class="c1"># Second descriptor instance</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">age</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span> <span class="c1"># Calls the first descriptor</span>
<span class="bp">self</span><span class="o">.</span><span class="n">age</span> <span class="o">=</span> <span class="n">age</span> <span class="c1"># Calls the second descriptor</span>
<span class="k">def</span><span class="w"> </span><span class="nf">birthday</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">age</span> <span class="o">+=</span> <span class="mi">1</span>
</pre></div>
</div>
<p>An interactive session shows that the <code class="xref py py-class docutils literal notranslate"><span class="pre">Person</span></code> class has called
<a class="reference internal" href="../reference/datamodel.html#object.__set_name__" title="object.__set_name__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set_name__()</span></code></a> so that the field names would be recorded. Here
we call <a class="reference internal" href="../library/functions.html#vars" title="vars"><code class="xref py py-func docutils literal notranslate"><span class="pre">vars()</span></code></a> to look up the descriptor without triggering it:</p>
<div class="highlight-pycon notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="nb">vars</span><span class="p">(</span><span class="nb">vars</span><span class="p">(</span><span class="n">Person</span><span class="p">)[</span><span class="s1">'name'</span><span class="p">])</span>
<span class="go">{'public_name': 'name', 'private_name': '_name'}</span>
<span class="gp">>>> </span><span class="nb">vars</span><span class="p">(</span><span class="nb">vars</span><span class="p">(</span><span class="n">Person</span><span class="p">)[</span><span class="s1">'age'</span><span class="p">])</span>
<span class="go">{'public_name': 'age', 'private_name': '_age'}</span>
</pre></div>
</div>
<p>The new class now logs access to both <em>name</em> and <em>age</em>:</p>
<div class="highlight-pycon notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">pete</span> <span class="o">=</span> <span class="n">Person</span><span class="p">(</span><span class="s1">'Peter P'</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span>
<span class="go">INFO:root:Updating 'name' to 'Peter P'</span>
<span class="go">INFO:root:Updating 'age' to 10</span>
<span class="gp">>>> </span><span class="n">kate</span> <span class="o">=</span> <span class="n">Person</span><span class="p">(</span><span class="s1">'Catherine C'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
<span class="go">INFO:root:Updating 'name' to 'Catherine C'</span>
<span class="go">INFO:root:Updating 'age' to 20</span>
</pre></div>
</div>
<p>The two <em>Person</em> instances contain only the private names:</p>
<div class="highlight-pycon notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="nb">vars</span><span class="p">(</span><span class="n">pete</span><span class="p">)</span>
<span class="go">{'_name': 'Peter P', '_age': 10}</span>
<span class="gp">>>> </span><span class="nb">vars</span><span class="p">(</span><span class="n">kate</span><span class="p">)</span>
<span class="go">{'_name': 'Catherine C', '_age': 20}</span>
</pre></div>
</div>
</section>
<section id="closing-thoughts">
<h3><a class="toc-backref" href="#id8" role="doc-backlink">Closing thoughts</a><a class="headerlink" href="#closing-thoughts" title="Link to this heading">¶</a></h3>
<p>A <a class="reference internal" href="../glossary.html#term-descriptor"><span class="xref std std-term">descriptor</span></a> is what we call any object that defines <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a>,
<a class="reference internal" href="../reference/datamodel.html#object.__set__" title="object.__set__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set__()</span></code></a>, or <a class="reference internal" href="../reference/datamodel.html#object.__delete__" title="object.__delete__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__delete__()</span></code></a>.</p>
<p>Optionally, descriptors can have a <a class="reference internal" href="../reference/datamodel.html#object.__set_name__" title="object.__set_name__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set_name__()</span></code></a> method. This is only
used in cases where a descriptor needs to know either the class where it was
created or the name of class variable it was assigned to. (This method, if
present, is called even if the class is not a descriptor.)</p>
<p>Descriptors get invoked by the dot operator during attribute lookup. If a
descriptor is accessed indirectly with <code class="docutils literal notranslate"><span class="pre">vars(some_class)[descriptor_name]</span></code>,
the descriptor instance is returned without invoking it.</p>
<p>Descriptors only work when used as class variables. When put in instances,
they have no effect.</p>
<p>The main motivation for descriptors is to provide a hook allowing objects
stored in class variables to control what happens during attribute lookup.</p>
<p>Traditionally, the calling class controls what happens during lookup.
Descriptors invert that relationship and allow the data being looked-up to
have a say in the matter.</p>
<p>Descriptors are used throughout the language. It is how functions turn into
bound methods. Common tools like <a class="reference internal" href="../library/functions.html#classmethod" title="classmethod"><code class="xref py py-func docutils literal notranslate"><span class="pre">classmethod()</span></code></a>, <a class="reference internal" href="../library/functions.html#staticmethod" title="staticmethod"><code class="xref py py-func docutils literal notranslate"><span class="pre">staticmethod()</span></code></a>,
<a class="reference internal" href="../library/functions.html#property" title="property"><code class="xref py py-func docutils literal notranslate"><span class="pre">property()</span></code></a>, and <a class="reference internal" href="../library/functools.html#functools.cached_property" title="functools.cached_property"><code class="xref py py-func docutils literal notranslate"><span class="pre">functools.cached_property()</span></code></a> are all implemented as
descriptors.</p>
</section>
</section>
<section id="complete-practical-example">
<h2><a class="toc-backref" href="#id9" role="doc-backlink">Complete Practical Example</a><a class="headerlink" href="#complete-practical-example" title="Link to this heading">¶</a></h2>
<p>In this example, we create a practical and powerful tool for locating
notoriously hard to find data corruption bugs.</p>
<section id="validator-class">
<h3><a class="toc-backref" href="#id10" role="doc-backlink">Validator class</a><a class="headerlink" href="#validator-class" title="Link to this heading">¶</a></h3>
<p>A validator is a descriptor for managed attribute access. Prior to storing
any data, it verifies that the new value meets various type and range
restrictions. If those restrictions aren't met, it raises an exception to
prevent data corruption at its source.</p>
<p>This <code class="xref py py-class docutils literal notranslate"><span class="pre">Validator</span></code> class is both an <a class="reference internal" href="../glossary.html#term-abstract-base-class"><span class="xref std std-term">abstract base class</span></a> and a
managed attribute descriptor:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">abc</span><span class="w"> </span><span class="kn">import</span> <span class="n">ABC</span><span class="p">,</span> <span class="n">abstractmethod</span>
<span class="k">class</span><span class="w"> </span><span class="nc">Validator</span><span class="p">(</span><span class="n">ABC</span><span class="p">):</span>
<span class="k">def</span><span class="w"> </span><span class="nf">__set_name__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">owner</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">private_name</span> <span class="o">=</span> <span class="s1">'_'</span> <span class="o">+</span> <span class="n">name</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__get__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">objtype</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">private_name</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__set__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">validate</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
<span class="nb">setattr</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">private_name</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
<span class="nd">@abstractmethod</span>
<span class="k">def</span><span class="w"> </span><span class="nf">validate</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="k">pass</span>
</pre></div>
</div>
<p>Custom validators need to inherit from <code class="xref py py-class docutils literal notranslate"><span class="pre">Validator</span></code> and must supply a
<code class="xref py py-meth docutils literal notranslate"><span class="pre">validate()</span></code> method to test various restrictions as needed.</p>
</section>
<section id="custom-validators">
<h3><a class="toc-backref" href="#id11" role="doc-backlink">Custom validators</a><a class="headerlink" href="#custom-validators" title="Link to this heading">¶</a></h3>
<p>Here are three practical data validation utilities:</p>
<ol class="arabic simple">
<li><p><code class="xref py py-class docutils literal notranslate"><span class="pre">OneOf</span></code> verifies that a value is one of a restricted set of options.</p></li>
<li><p><code class="xref py py-class docutils literal notranslate"><span class="pre">Number</span></code> verifies that a value is either an <a class="reference internal" href="../library/functions.html#int" title="int"><code class="xref py py-class docutils literal notranslate"><span class="pre">int</span></code></a> or
<a class="reference internal" href="../library/functions.html#float" title="float"><code class="xref py py-class docutils literal notranslate"><span class="pre">float</span></code></a>. Optionally, it verifies that a value is between a given
minimum or maximum.</p></li>
<li><p><code class="xref py py-class docutils literal notranslate"><span class="pre">String</span></code> verifies that a value is a <a class="reference internal" href="../library/stdtypes.html#str" title="str"><code class="xref py py-class docutils literal notranslate"><span class="pre">str</span></code></a>. Optionally, it
validates a given minimum or maximum length. It can validate a
user-defined <a class="reference external" href="https://en.wikipedia.org/wiki/Predicate_(mathematical_logic)">predicate</a> as well.</p></li>
</ol>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">OneOf</span><span class="p">(</span><span class="n">Validator</span><span class="p">):</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">options</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="o">=</span> <span class="nb">set</span><span class="p">(</span><span class="n">options</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">validate</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="k">if</span> <span class="n">value</span> <span class="ow">not</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
<span class="sa">f</span><span class="s1">'Expected </span><span class="si">{</span><span class="n">value</span><span class="si">!r}</span><span class="s1"> to be one of </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="si">!r}</span><span class="s1">'</span>
<span class="p">)</span>
<span class="k">class</span><span class="w"> </span><span class="nc">Number</span><span class="p">(</span><span class="n">Validator</span><span class="p">):</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">minvalue</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">maxvalue</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">minvalue</span> <span class="o">=</span> <span class="n">minvalue</span>
<span class="bp">self</span><span class="o">.</span><span class="n">maxvalue</span> <span class="o">=</span> <span class="n">maxvalue</span>
<span class="k">def</span><span class="w"> </span><span class="nf">validate</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="p">(</span><span class="nb">int</span><span class="p">,</span> <span class="nb">float</span><span class="p">)):</span>
<span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="sa">f</span><span class="s1">'Expected </span><span class="si">{</span><span class="n">value</span><span class="si">!r}</span><span class="s1"> to be an int or float'</span><span class="p">)</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">minvalue</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="n">value</span> <span class="o"><</span> <span class="bp">self</span><span class="o">.</span><span class="n">minvalue</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
<span class="sa">f</span><span class="s1">'Expected </span><span class="si">{</span><span class="n">value</span><span class="si">!r}</span><span class="s1"> to be at least </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">minvalue</span><span class="si">!r}</span><span class="s1">'</span>
<span class="p">)</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">maxvalue</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="n">value</span> <span class="o">></span> <span class="bp">self</span><span class="o">.</span><span class="n">maxvalue</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
<span class="sa">f</span><span class="s1">'Expected </span><span class="si">{</span><span class="n">value</span><span class="si">!r}</span><span class="s1"> to be no more than </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">maxvalue</span><span class="si">!r}</span><span class="s1">'</span>
<span class="p">)</span>
<span class="k">class</span><span class="w"> </span><span class="nc">String</span><span class="p">(</span><span class="n">Validator</span><span class="p">):</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">minsize</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">maxsize</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">predicate</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">minsize</span> <span class="o">=</span> <span class="n">minsize</span>
<span class="bp">self</span><span class="o">.</span><span class="n">maxsize</span> <span class="o">=</span> <span class="n">maxsize</span>
<span class="bp">self</span><span class="o">.</span><span class="n">predicate</span> <span class="o">=</span> <span class="n">predicate</span>
<span class="k">def</span><span class="w"> </span><span class="nf">validate</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="nb">str</span><span class="p">):</span>
<span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="sa">f</span><span class="s1">'Expected </span><span class="si">{</span><span class="n">value</span><span class="si">!r}</span><span class="s1"> to be a str'</span><span class="p">)</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">minsize</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="nb">len</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="o"><</span> <span class="bp">self</span><span class="o">.</span><span class="n">minsize</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
<span class="sa">f</span><span class="s1">'Expected </span><span class="si">{</span><span class="n">value</span><span class="si">!r}</span><span class="s1"> to be no smaller than </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">minsize</span><span class="si">!r}</span><span class="s1">'</span>
<span class="p">)</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">maxsize</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="nb">len</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="o">></span> <span class="bp">self</span><span class="o">.</span><span class="n">maxsize</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
<span class="sa">f</span><span class="s1">'Expected </span><span class="si">{</span><span class="n">value</span><span class="si">!r}</span><span class="s1"> to be no bigger than </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">maxsize</span><span class="si">!r}</span><span class="s1">'</span>
<span class="p">)</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">predicate</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">predicate</span><span class="p">(</span><span class="n">value</span><span class="p">):</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
<span class="sa">f</span><span class="s1">'Expected </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">predicate</span><span class="si">}</span><span class="s1"> to be true for </span><span class="si">{</span><span class="n">value</span><span class="si">!r}</span><span class="s1">'</span>
<span class="p">)</span>
</pre></div>
</div>
</section>
<section id="practical-application">
<h3><a class="toc-backref" href="#id12" role="doc-backlink">Practical application</a><a class="headerlink" href="#practical-application" title="Link to this heading">¶</a></h3>
<p>Here's how the data validators can be used in a real class:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Component</span><span class="p">:</span>
<span class="n">name</span> <span class="o">=</span> <span class="n">String</span><span class="p">(</span><span class="n">minsize</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">maxsize</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span> <span class="n">predicate</span><span class="o">=</span><span class="nb">str</span><span class="o">.</span><span class="n">isupper</span><span class="p">)</span>
<span class="n">kind</span> <span class="o">=</span> <span class="n">OneOf</span><span class="p">(</span><span class="s1">'wood'</span><span class="p">,</span> <span class="s1">'metal'</span><span class="p">,</span> <span class="s1">'plastic'</span><span class="p">)</span>
<span class="n">quantity</span> <span class="o">=</span> <span class="n">Number</span><span class="p">(</span><span class="n">minvalue</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">kind</span><span class="p">,</span> <span class="n">quantity</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span>
<span class="bp">self</span><span class="o">.</span><span class="n">kind</span> <span class="o">=</span> <span class="n">kind</span>
<span class="bp">self</span><span class="o">.</span><span class="n">quantity</span> <span class="o">=</span> <span class="n">quantity</span>
</pre></div>
</div>
<p>The descriptors prevent invalid instances from being created:</p>
<div class="highlight-pycon notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">Component</span><span class="p">(</span><span class="s1">'Widget'</span><span class="p">,</span> <span class="s1">'metal'</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span> <span class="c1"># Blocked: 'Widget' is not all uppercase</span>
<span class="gt">Traceback (most recent call last):</span>
<span class="w"> </span><span class="o">...</span>
<span class="gr">ValueError</span>: <span class="n">Expected <method 'isupper' of 'str' objects> to be true for 'Widget'</span>
<span class="gp">>>> </span><span class="n">Component</span><span class="p">(</span><span class="s1">'WIDGET'</span><span class="p">,</span> <span class="s1">'metle'</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span> <span class="c1"># Blocked: 'metle' is misspelled</span>
<span class="gt">Traceback (most recent call last):</span>
<span class="w"> </span><span class="o">...</span>
<span class="gr">ValueError</span>: <span class="n">Expected 'metle' to be one of {'metal', 'plastic', 'wood'}</span>
<span class="gp">>>> </span><span class="n">Component</span><span class="p">(</span><span class="s1">'WIDGET'</span><span class="p">,</span> <span class="s1">'metal'</span><span class="p">,</span> <span class="o">-</span><span class="mi">5</span><span class="p">)</span> <span class="c1"># Blocked: -5 is negative</span>
<span class="gt">Traceback (most recent call last):</span>
<span class="w"> </span><span class="o">...</span>
<span class="gr">ValueError</span>: <span class="n">Expected -5 to be at least 0</span>
<span class="gp">>>> </span><span class="n">Component</span><span class="p">(</span><span class="s1">'WIDGET'</span><span class="p">,</span> <span class="s1">'metal'</span><span class="p">,</span> <span class="s1">'V'</span><span class="p">)</span> <span class="c1"># Blocked: 'V' isn't a number</span>
<span class="gt">Traceback (most recent call last):</span>
<span class="w"> </span><span class="o">...</span>
<span class="gr">TypeError</span>: <span class="n">Expected 'V' to be an int or float</span>
<span class="gp">>>> </span><span class="n">c</span> <span class="o">=</span> <span class="n">Component</span><span class="p">(</span><span class="s1">'WIDGET'</span><span class="p">,</span> <span class="s1">'metal'</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span> <span class="c1"># Allowed: The inputs are valid</span>
</pre></div>
</div>
</section>
</section>
<section id="technical-tutorial">
<h2><a class="toc-backref" href="#id13" role="doc-backlink">Technical Tutorial</a><a class="headerlink" href="#technical-tutorial" title="Link to this heading">¶</a></h2>
<p>What follows is a more technical tutorial for the mechanics and details of how
descriptors work.</p>
<section id="abstract">
<h3><a class="toc-backref" href="#id14" role="doc-backlink">Abstract</a><a class="headerlink" href="#abstract" title="Link to this heading">¶</a></h3>
<p>Defines descriptors, summarizes the protocol, and shows how descriptors are
called. Provides an example showing how object relational mappings work.</p>
<p>Learning about descriptors not only provides access to a larger toolset, it
creates a deeper understanding of how Python works.</p>
</section>
<section id="definition-and-introduction">
<h3><a class="toc-backref" href="#id15" role="doc-backlink">Definition and introduction</a><a class="headerlink" href="#definition-and-introduction" title="Link to this heading">¶</a></h3>
<p>In general, a descriptor is an attribute value that has one of the methods in
the descriptor protocol. Those methods are <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a>, <a class="reference internal" href="../reference/datamodel.html#object.__set__" title="object.__set__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set__()</span></code></a>,
and <a class="reference internal" href="../reference/datamodel.html#object.__delete__" title="object.__delete__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__delete__()</span></code></a>. If any of those methods are defined for an
attribute, it is said to be a <a class="reference internal" href="../glossary.html#term-descriptor"><span class="xref std std-term">descriptor</span></a>.</p>
<p>The default behavior for attribute access is to get, set, or delete the
attribute from an object's dictionary. For instance, <code class="docutils literal notranslate"><span class="pre">a.x</span></code> has a lookup chain
starting with <code class="docutils literal notranslate"><span class="pre">a.__dict__['x']</span></code>, then <code class="docutils literal notranslate"><span class="pre">type(a).__dict__['x']</span></code>, and
continuing through the method resolution order of <code class="docutils literal notranslate"><span class="pre">type(a)</span></code>. If the
looked-up value is an object defining one of the descriptor methods, then Python
may override the default behavior and invoke the descriptor method instead.
Where this occurs in the precedence chain depends on which descriptor methods
were defined.</p>
<p>Descriptors are a powerful, general purpose protocol. They are the mechanism
behind properties, methods, static methods, class methods, and
<a class="reference internal" href="../library/functions.html#super" title="super"><code class="xref py py-func docutils literal notranslate"><span class="pre">super()</span></code></a>. They are used throughout Python itself. Descriptors
simplify the underlying C code and offer a flexible set of new tools for
everyday Python programs.</p>
</section>
<section id="descriptor-protocol">
<h3><a class="toc-backref" href="#id16" role="doc-backlink">Descriptor protocol</a><a class="headerlink" href="#descriptor-protocol" title="Link to this heading">¶</a></h3>
<p><code class="docutils literal notranslate"><span class="pre">descr.__get__(self,</span> <span class="pre">obj,</span> <span class="pre">type=None)</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">descr.__set__(self,</span> <span class="pre">obj,</span> <span class="pre">value)</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">descr.__delete__(self,</span> <span class="pre">obj)</span></code></p>
<p>That is all there is to it. Define any of these methods and an object is
considered a descriptor and can override default behavior upon being looked up
as an attribute.</p>
<p>If an object defines <a class="reference internal" href="../reference/datamodel.html#object.__set__" title="object.__set__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set__()</span></code></a> or <a class="reference internal" href="../reference/datamodel.html#object.__delete__" title="object.__delete__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__delete__()</span></code></a>, it is considered
a data descriptor. Descriptors that only define <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a> are called
non-data descriptors (they are often used for methods but other uses are
possible).</p>
<p>Data and non-data descriptors differ in how overrides are calculated with
respect to entries in an instance's dictionary. If an instance's dictionary
has an entry with the same name as a data descriptor, the data descriptor
takes precedence. If an instance's dictionary has an entry with the same
name as a non-data descriptor, the dictionary entry takes precedence.</p>
<p>To make a read-only data descriptor, define both <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a> and
<a class="reference internal" href="../reference/datamodel.html#object.__set__" title="object.__set__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set__()</span></code></a> with the <code class="xref py py-meth docutils literal notranslate"><span class="pre">__set__()</span></code> raising an <a class="reference internal" href="../library/exceptions.html#AttributeError" title="AttributeError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">AttributeError</span></code></a> when
called. Defining the <code class="xref py py-meth docutils literal notranslate"><span class="pre">__set__()</span></code> method with an exception raising
placeholder is enough to make it a data descriptor.</p>
</section>
<section id="overview-of-descriptor-invocation">
<h3><a class="toc-backref" href="#id17" role="doc-backlink">Overview of descriptor invocation</a><a class="headerlink" href="#overview-of-descriptor-invocation" title="Link to this heading">¶</a></h3>
<p>A descriptor can be called directly with <code class="docutils literal notranslate"><span class="pre">desc.__get__(obj)</span></code> or
<code class="docutils literal notranslate"><span class="pre">desc.__get__(None,</span> <span class="pre">cls)</span></code>.</p>
<p>But it is more common for a descriptor to be invoked automatically from
attribute access.</p>
<p>The expression <code class="docutils literal notranslate"><span class="pre">obj.x</span></code> looks up the attribute <code class="docutils literal notranslate"><span class="pre">x</span></code> in the chain of
namespaces for <code class="docutils literal notranslate"><span class="pre">obj</span></code>. If the search finds a descriptor outside of the
instance <a class="reference internal" href="../reference/datamodel.html#object.__dict__" title="object.__dict__"><code class="xref py py-attr docutils literal notranslate"><span class="pre">__dict__</span></code></a>, its <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a> method is
invoked according to the precedence rules listed below.</p>
<p>The details of invocation depend on whether <code class="docutils literal notranslate"><span class="pre">obj</span></code> is an object, class, or
instance of super.</p>
</section>
<section id="invocation-from-an-instance">
<h3><a class="toc-backref" href="#id18" role="doc-backlink">Invocation from an instance</a><a class="headerlink" href="#invocation-from-an-instance" title="Link to this heading">¶</a></h3>
<p>Instance lookup scans through a chain of namespaces giving data descriptors
the highest priority, followed by instance variables, then non-data
descriptors, then class variables, and lastly <a class="reference internal" href="../reference/datamodel.html#object.__getattr__" title="object.__getattr__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattr__()</span></code></a> if it is
provided.</p>
<p>If a descriptor is found for <code class="docutils literal notranslate"><span class="pre">a.x</span></code>, then it is invoked with:
<code class="docutils literal notranslate"><span class="pre">desc.__get__(a,</span> <span class="pre">type(a))</span></code>.</p>
<p>The logic for a dotted lookup is in <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">object.__getattribute__()</span></code></a>. Here is
a pure Python equivalent:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">find_name_in_mro</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">default</span><span class="p">):</span>
<span class="s2">"Emulate _PyType_Lookup() in Objects/typeobject.c"</span>
<span class="k">for</span> <span class="n">base</span> <span class="ow">in</span> <span class="bp">cls</span><span class="o">.</span><span class="vm">__mro__</span><span class="p">:</span>
<span class="k">if</span> <span class="n">name</span> <span class="ow">in</span> <span class="nb">vars</span><span class="p">(</span><span class="n">base</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">vars</span><span class="p">(</span><span class="n">base</span><span class="p">)[</span><span class="n">name</span><span class="p">]</span>
<span class="k">return</span> <span class="n">default</span>
<span class="k">def</span><span class="w"> </span><span class="nf">object_getattribute</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
<span class="s2">"Emulate PyObject_GenericGetAttr() in Objects/object.c"</span>
<span class="n">null</span> <span class="o">=</span> <span class="nb">object</span><span class="p">()</span>
<span class="n">objtype</span> <span class="o">=</span> <span class="nb">type</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
<span class="n">cls_var</span> <span class="o">=</span> <span class="n">find_name_in_mro</span><span class="p">(</span><span class="n">objtype</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">null</span><span class="p">)</span>
<span class="n">descr_get</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="nb">type</span><span class="p">(</span><span class="n">cls_var</span><span class="p">),</span> <span class="s1">'__get__'</span><span class="p">,</span> <span class="n">null</span><span class="p">)</span>
<span class="k">if</span> <span class="n">descr_get</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">null</span><span class="p">:</span>
<span class="k">if</span> <span class="p">(</span><span class="nb">hasattr</span><span class="p">(</span><span class="nb">type</span><span class="p">(</span><span class="n">cls_var</span><span class="p">),</span> <span class="s1">'__set__'</span><span class="p">)</span>
<span class="ow">or</span> <span class="nb">hasattr</span><span class="p">(</span><span class="nb">type</span><span class="p">(</span><span class="n">cls_var</span><span class="p">),</span> <span class="s1">'__delete__'</span><span class="p">)):</span>
<span class="k">return</span> <span class="n">descr_get</span><span class="p">(</span><span class="n">cls_var</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">objtype</span><span class="p">)</span> <span class="c1"># data descriptor</span>
<span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="s1">'__dict__'</span><span class="p">)</span> <span class="ow">and</span> <span class="n">name</span> <span class="ow">in</span> <span class="nb">vars</span><span class="p">(</span><span class="n">obj</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">vars</span><span class="p">(</span><span class="n">obj</span><span class="p">)[</span><span class="n">name</span><span class="p">]</span> <span class="c1"># instance variable</span>
<span class="k">if</span> <span class="n">descr_get</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">null</span><span class="p">:</span>
<span class="k">return</span> <span class="n">descr_get</span><span class="p">(</span><span class="n">cls_var</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">objtype</span><span class="p">)</span> <span class="c1"># non-data descriptor</span>
<span class="k">if</span> <span class="n">cls_var</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">null</span><span class="p">:</span>
<span class="k">return</span> <span class="n">cls_var</span> <span class="c1"># class variable</span>
<span class="k">raise</span> <span class="ne">AttributeError</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
</pre></div>
</div>
<p>Note, there is no <a class="reference internal" href="../reference/datamodel.html#object.__getattr__" title="object.__getattr__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattr__()</span></code></a> hook in the <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattribute__()</span></code></a>
code. That is why calling <code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattribute__()</span></code> directly or with
<code class="docutils literal notranslate"><span class="pre">super().__getattribute__</span></code> will bypass <code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattr__()</span></code> entirely.</p>
<p>Instead, it is the dot operator and the <a class="reference internal" href="../library/functions.html#getattr" title="getattr"><code class="xref py py-func docutils literal notranslate"><span class="pre">getattr()</span></code></a> function that are
responsible for invoking <a class="reference internal" href="../reference/datamodel.html#object.__getattr__" title="object.__getattr__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattr__()</span></code></a> whenever <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattribute__()</span></code></a>
raises an <a class="reference internal" href="../library/exceptions.html#AttributeError" title="AttributeError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">AttributeError</span></code></a>. Their logic is encapsulated in a helper
function:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">getattr_hook</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
<span class="s2">"Emulate slot_tp_getattr_hook() in Objects/typeobject.c"</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="n">obj</span><span class="o">.</span><span class="fm">__getattribute__</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="nb">type</span><span class="p">(</span><span class="n">obj</span><span class="p">),</span> <span class="s1">'__getattr__'</span><span class="p">):</span>
<span class="k">raise</span>
<span class="k">return</span> <span class="nb">type</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span><span class="o">.</span><span class="fm">__getattr__</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span> <span class="c1"># __getattr__</span>
</pre></div>
</div>
</section>
<section id="invocation-from-a-class">
<h3><a class="toc-backref" href="#id19" role="doc-backlink">Invocation from a class</a><a class="headerlink" href="#invocation-from-a-class" title="Link to this heading">¶</a></h3>
<p>The logic for a dotted lookup such as <code class="docutils literal notranslate"><span class="pre">A.x</span></code> is in
<code class="xref py py-meth docutils literal notranslate"><span class="pre">type.__getattribute__()</span></code>. The steps are similar to those for
<code class="xref py py-meth docutils literal notranslate"><span class="pre">object.__getattribute__()</span></code> but the instance dictionary lookup is replaced
by a search through the class's <a class="reference internal" href="../glossary.html#term-method-resolution-order"><span class="xref std std-term">method resolution order</span></a>.</p>
<p>If a descriptor is found, it is invoked with <code class="docutils literal notranslate"><span class="pre">desc.__get__(None,</span> <span class="pre">A)</span></code>.</p>
<p>The full C implementation can be found in <code class="xref c c-func docutils literal notranslate"><span class="pre">type_getattro()</span></code> and
<code class="xref c c-func docutils literal notranslate"><span class="pre">_PyType_Lookup()</span></code> in <a class="extlink-source reference external" href="https://github.com/python/cpython/tree/3.14/Objects/typeobject.c">Objects/typeobject.c</a>.</p>
</section>
<section id="invocation-from-super">
<h3><a class="toc-backref" href="#id20" role="doc-backlink">Invocation from super</a><a class="headerlink" href="#invocation-from-super" title="Link to this heading">¶</a></h3>
<p>The logic for super's dotted lookup is in the <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattribute__()</span></code></a> method for
object returned by <a class="reference internal" href="../library/functions.html#super" title="super"><code class="xref py py-func docutils literal notranslate"><span class="pre">super()</span></code></a>.</p>
<p>A dotted lookup such as <code class="docutils literal notranslate"><span class="pre">super(A,</span> <span class="pre">obj).m</span></code> searches <code class="docutils literal notranslate"><span class="pre">obj.__class__.__mro__</span></code>
for the base class <code class="docutils literal notranslate"><span class="pre">B</span></code> immediately following <code class="docutils literal notranslate"><span class="pre">A</span></code> and then returns
<code class="docutils literal notranslate"><span class="pre">B.__dict__['m'].__get__(obj,</span> <span class="pre">A)</span></code>. If not a descriptor, <code class="docutils literal notranslate"><span class="pre">m</span></code> is returned
unchanged.</p>
<p>The full C implementation can be found in <code class="xref c c-func docutils literal notranslate"><span class="pre">super_getattro()</span></code> in
<a class="extlink-source reference external" href="https://github.com/python/cpython/tree/3.14/Objects/typeobject.c">Objects/typeobject.c</a>. A pure Python equivalent can be found in
<a class="reference external" href="https://www.python.org/download/releases/2.2.3/descrintro/#cooperation">Guido's Tutorial</a>.</p>
</section>
<section id="summary-of-invocation-logic">
<h3><a class="toc-backref" href="#id21" role="doc-backlink">Summary of invocation logic</a><a class="headerlink" href="#summary-of-invocation-logic" title="Link to this heading">¶</a></h3>
<p>The mechanism for descriptors is embedded in the <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattribute__()</span></code></a>
methods for <a class="reference internal" href="../library/functions.html#object" title="object"><code class="xref py py-class docutils literal notranslate"><span class="pre">object</span></code></a>, <a class="reference internal" href="../library/functions.html#type" title="type"><code class="xref py py-class docutils literal notranslate"><span class="pre">type</span></code></a>, and <a class="reference internal" href="../library/functions.html#super" title="super"><code class="xref py py-func docutils literal notranslate"><span class="pre">super()</span></code></a>.</p>
<p>The important points to remember are:</p>
<ul class="simple">
<li><p>Descriptors are invoked by the <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattribute__()</span></code></a> method.</p></li>
<li><p>Classes inherit this machinery from <a class="reference internal" href="../library/functions.html#object" title="object"><code class="xref py py-class docutils literal notranslate"><span class="pre">object</span></code></a>, <a class="reference internal" href="../library/functions.html#type" title="type"><code class="xref py py-class docutils literal notranslate"><span class="pre">type</span></code></a>, or
<a class="reference internal" href="../library/functions.html#super" title="super"><code class="xref py py-func docutils literal notranslate"><span class="pre">super()</span></code></a>.</p></li>
<li><p>Overriding <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattribute__()</span></code></a> prevents automatic descriptor calls
because all the descriptor logic is in that method.</p></li>
<li><p><code class="xref py py-meth docutils literal notranslate"><span class="pre">object.__getattribute__()</span></code> and <code class="xref py py-meth docutils literal notranslate"><span class="pre">type.__getattribute__()</span></code> make
different calls to <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a>. The first includes the instance and may
include the class. The second puts in <code class="docutils literal notranslate"><span class="pre">None</span></code> for the instance and always
includes the class.</p></li>
<li><p>Data descriptors always override instance dictionaries.</p></li>
<li><p>Non-data descriptors may be overridden by instance dictionaries.</p></li>
</ul>
</section>
<section id="automatic-name-notification">
<h3><a class="toc-backref" href="#id22" role="doc-backlink">Automatic name notification</a><a class="headerlink" href="#automatic-name-notification" title="Link to this heading">¶</a></h3>
<p>Sometimes it is desirable for a descriptor to know what class variable name it
was assigned to. When a new class is created, the <a class="reference internal" href="../library/functions.html#type" title="type"><code class="xref py py-class docutils literal notranslate"><span class="pre">type</span></code></a> metaclass
scans the dictionary of the new class. If any of the entries are descriptors
and if they define <a class="reference internal" href="../reference/datamodel.html#object.__set_name__" title="object.__set_name__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set_name__()</span></code></a>, that method is called with two
arguments. The <em>owner</em> is the class where the descriptor is used, and the
<em>name</em> is the class variable the descriptor was assigned to.</p>
<p>The implementation details are in <code class="xref c c-func docutils literal notranslate"><span class="pre">type_new()</span></code> and
<code class="xref c c-func docutils literal notranslate"><span class="pre">set_names()</span></code> in <a class="extlink-source reference external" href="https://github.com/python/cpython/tree/3.14/Objects/typeobject.c">Objects/typeobject.c</a>.</p>
<p>Since the update logic is in <code class="xref py py-meth docutils literal notranslate"><span class="pre">type.__new__()</span></code>, notifications only take
place at the time of class creation. If descriptors are added to the class
afterwards, <a class="reference internal" href="../reference/datamodel.html#object.__set_name__" title="object.__set_name__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set_name__()</span></code></a> will need to be called manually.</p>
</section>
<section id="orm-example">
<h3><a class="toc-backref" href="#id23" role="doc-backlink">ORM example</a><a class="headerlink" href="#orm-example" title="Link to this heading">¶</a></h3>
<p>The following code is a simplified skeleton showing how data descriptors could
be used to implement an <a class="reference external" href="https://en.wikipedia.org/wiki/Object%E2%80%93relational_mapping">object relational mapping</a>.</p>
<p>The essential idea is that the data is stored in an external database. The
Python instances only hold keys to the database's tables. Descriptors take
care of lookups or updates:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Field</span><span class="p">:</span>
<span class="k">def</span><span class="w"> </span><span class="nf">__set_name__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">owner</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">fetch</span> <span class="o">=</span> <span class="sa">f</span><span class="s1">'SELECT </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s1"> FROM </span><span class="si">{</span><span class="n">owner</span><span class="o">.</span><span class="n">table</span><span class="si">}</span><span class="s1"> WHERE </span><span class="si">{</span><span class="n">owner</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s1">=?;'</span>
<span class="bp">self</span><span class="o">.</span><span class="n">store</span> <span class="o">=</span> <span class="sa">f</span><span class="s1">'UPDATE </span><span class="si">{</span><span class="n">owner</span><span class="o">.</span><span class="n">table</span><span class="si">}</span><span class="s1"> SET </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s1">=? WHERE </span><span class="si">{</span><span class="n">owner</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s1">=?;'</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__get__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">objtype</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="k">return</span> <span class="n">conn</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">fetch</span><span class="p">,</span> <span class="p">[</span><span class="n">obj</span><span class="o">.</span><span class="n">key</span><span class="p">])</span><span class="o">.</span><span class="n">fetchone</span><span class="p">()[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__set__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="n">conn</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">store</span><span class="p">,</span> <span class="p">[</span><span class="n">value</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">key</span><span class="p">])</span>
<span class="n">conn</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
</pre></div>
</div>
<p>We can use the <code class="xref py py-class docutils literal notranslate"><span class="pre">Field</span></code> class to define <a class="reference external" href="https://en.wikipedia.org/wiki/Database_model">models</a> that describe the schema for
each table in a database:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Movie</span><span class="p">:</span>
<span class="n">table</span> <span class="o">=</span> <span class="s1">'Movies'</span> <span class="c1"># Table name</span>
<span class="n">key</span> <span class="o">=</span> <span class="s1">'title'</span> <span class="c1"># Primary key</span>
<span class="n">director</span> <span class="o">=</span> <span class="n">Field</span><span class="p">()</span>
<span class="n">year</span> <span class="o">=</span> <span class="n">Field</span><span class="p">()</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">key</span> <span class="o">=</span> <span class="n">key</span>
<span class="k">class</span><span class="w"> </span><span class="nc">Song</span><span class="p">:</span>
<span class="n">table</span> <span class="o">=</span> <span class="s1">'Music'</span>
<span class="n">key</span> <span class="o">=</span> <span class="s1">'title'</span>
<span class="n">artist</span> <span class="o">=</span> <span class="n">Field</span><span class="p">()</span>
<span class="n">year</span> <span class="o">=</span> <span class="n">Field</span><span class="p">()</span>
<span class="n">genre</span> <span class="o">=</span> <span class="n">Field</span><span class="p">()</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">key</span> <span class="o">=</span> <span class="n">key</span>
</pre></div>
</div>
<p>To use the models, first connect to the database:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="kn">import</span><span class="w"> </span><span class="nn">sqlite3</span>
<span class="gp">>>> </span><span class="n">conn</span> <span class="o">=</span> <span class="n">sqlite3</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="s1">'entertainment.db'</span><span class="p">)</span>
</pre></div>
</div>
<p>An interactive session shows how data is retrieved from the database and how
it can be updated:</p>
<div class="highlight-pycon notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">Movie</span><span class="p">(</span><span class="s1">'Star Wars'</span><span class="p">)</span><span class="o">.</span><span class="n">director</span>
<span class="go">'George Lucas'</span>
<span class="gp">>>> </span><span class="n">jaws</span> <span class="o">=</span> <span class="n">Movie</span><span class="p">(</span><span class="s1">'Jaws'</span><span class="p">)</span>
<span class="gp">>>> </span><span class="sa">f</span><span class="s1">'Released in </span><span class="si">{</span><span class="n">jaws</span><span class="o">.</span><span class="n">year</span><span class="si">}</span><span class="s1"> by </span><span class="si">{</span><span class="n">jaws</span><span class="o">.</span><span class="n">director</span><span class="si">}</span><span class="s1">'</span>
<span class="go">'Released in 1975 by Steven Spielberg'</span>
<span class="gp">>>> </span><span class="n">Song</span><span class="p">(</span><span class="s1">'Country Roads'</span><span class="p">)</span><span class="o">.</span><span class="n">artist</span>
<span class="go">'John Denver'</span>
<span class="gp">>>> </span><span class="n">Movie</span><span class="p">(</span><span class="s1">'Star Wars'</span><span class="p">)</span><span class="o">.</span><span class="n">director</span> <span class="o">=</span> <span class="s1">'J.J. Abrams'</span>
<span class="gp">>>> </span><span class="n">Movie</span><span class="p">(</span><span class="s1">'Star Wars'</span><span class="p">)</span><span class="o">.</span><span class="n">director</span>
<span class="go">'J.J. Abrams'</span>
</pre></div>
</div>
</section>
</section>
<section id="pure-python-equivalents">
<h2><a class="toc-backref" href="#id24" role="doc-backlink">Pure Python Equivalents</a><a class="headerlink" href="#pure-python-equivalents" title="Link to this heading">¶</a></h2>
<p>The descriptor protocol is simple and offers exciting possibilities. Several
use cases are so common that they have been prepackaged into built-in tools.
Properties, bound methods, static methods, class methods, and __slots__ are
all based on the descriptor protocol.</p>
<section id="properties">
<h3><a class="toc-backref" href="#id25" role="doc-backlink">Properties</a><a class="headerlink" href="#properties" title="Link to this heading">¶</a></h3>
<p>Calling <a class="reference internal" href="../library/functions.html#property" title="property"><code class="xref py py-func docutils literal notranslate"><span class="pre">property()</span></code></a> is a succinct way of building a data descriptor that
triggers a function call upon access to an attribute. Its signature is:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="nb">property</span><span class="p">(</span><span class="n">fget</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">fset</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">fdel</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">doc</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span> <span class="o">-></span> <span class="nb">property</span>
</pre></div>
</div>
<p>The documentation shows a typical use to define a managed attribute <code class="docutils literal notranslate"><span class="pre">x</span></code>:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">C</span><span class="p">:</span>
<span class="k">def</span><span class="w"> </span><span class="nf">getx</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">__x</span>
<span class="k">def</span><span class="w"> </span><span class="nf">setx</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">__x</span> <span class="o">=</span> <span class="n">value</span>
<span class="k">def</span><span class="w"> </span><span class="nf">delx</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">del</span> <span class="bp">self</span><span class="o">.</span><span class="n">__x</span>
<span class="n">x</span> <span class="o">=</span> <span class="nb">property</span><span class="p">(</span><span class="n">getx</span><span class="p">,</span> <span class="n">setx</span><span class="p">,</span> <span class="n">delx</span><span class="p">,</span> <span class="s2">"I'm the 'x' property."</span><span class="p">)</span>
</pre></div>
</div>
<p>To see how <a class="reference internal" href="../library/functions.html#property" title="property"><code class="xref py py-func docutils literal notranslate"><span class="pre">property()</span></code></a> is implemented in terms of the descriptor protocol,
here is a pure Python equivalent that implements most of the core functionality:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Property</span><span class="p">:</span>
<span class="s2">"Emulate PyProperty_Type() in Objects/descrobject.c"</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">fget</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">fset</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">fdel</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">doc</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">fget</span> <span class="o">=</span> <span class="n">fget</span>
<span class="bp">self</span><span class="o">.</span><span class="n">fset</span> <span class="o">=</span> <span class="n">fset</span>
<span class="bp">self</span><span class="o">.</span><span class="n">fdel</span> <span class="o">=</span> <span class="n">fdel</span>
<span class="k">if</span> <span class="n">doc</span> <span class="ow">is</span> <span class="kc">None</span> <span class="ow">and</span> <span class="n">fget</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">doc</span> <span class="o">=</span> <span class="n">fget</span><span class="o">.</span><span class="vm">__doc__</span>
<span class="bp">self</span><span class="o">.</span><span class="vm">__doc__</span> <span class="o">=</span> <span class="n">doc</span>
<span class="k">def</span><span class="w"> </span><span class="nf">__set_name__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">owner</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="vm">__name__</span> <span class="o">=</span> <span class="n">name</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__get__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">objtype</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="k">if</span> <span class="n">obj</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">fget</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">AttributeError</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">fget</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__set__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">fset</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">AttributeError</span>
<span class="bp">self</span><span class="o">.</span><span class="n">fset</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__delete__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">):</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">fdel</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">AttributeError</span>
<span class="bp">self</span><span class="o">.</span><span class="n">fdel</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">getter</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">fget</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">type</span><span class="p">(</span><span class="bp">self</span><span class="p">)(</span><span class="n">fget</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">fset</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">fdel</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="vm">__doc__</span><span class="p">)</span>