Skip to content

Create a primer section for the descriptor howto guide #22906

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 33 commits into from
Oct 23, 2020
Merged
Changes from 1 commit
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
4285090
Simplest example: Return a constant
rhettinger Oct 22, 2020
fc4865a
Add directory size example of a dynamic descriptor
rhettinger Oct 23, 2020
83d7240
Add the managed attributes example
rhettinger Oct 23, 2020
6e1bae3
Add stub sections
rhettinger Oct 23, 2020
5cd1b8d
Fix hardwired value
rhettinger Oct 23, 2020
e8db254
Add the set_name example.
rhettinger Oct 23, 2020
3624731
Add the data validators complete practical example.
rhettinger Oct 23, 2020
dab7c25
Minor touch-ups
rhettinger Oct 23, 2020
95e1513
Add technical section for __set_name__
rhettinger Oct 23, 2020
65a0ab3
Line wrap
rhettinger Oct 23, 2020
8e5f962
Use the @-notation where possible
rhettinger Oct 23, 2020
7f52aee
Modern style uses "cls" instead of "klass"
rhettinger Oct 23, 2020
48329d2
Fix typo
rhettinger Oct 23, 2020
1583c25
Missing colon
rhettinger Oct 23, 2020
45466fc
Note false positive in the suspcious entry extension
rhettinger Oct 23, 2020
31fb230
Note false positive in the suspcious entry extension
rhettinger Oct 23, 2020
58275e7
Note false positive in the suspcious entry extension
rhettinger Oct 23, 2020
80bfd08
Note false positive in the suspcious entry extension
rhettinger Oct 23, 2020
112a272
Fix typos. Minor grammar edits.
rhettinger Oct 23, 2020
1a899c8
Fix method name
rhettinger Oct 23, 2020
b3934e8
Fix SyntaxError and misspelled predicate name
rhettinger Oct 23, 2020
90992bf
Add references to and from the glossary
rhettinger Oct 23, 2020
2599cff
Fix markup
rhettinger Oct 23, 2020
11e790a
Update Doc/howto/descriptor.rst
rhettinger Oct 23, 2020
b0c435c
Minor comment and variable name improvements
rhettinger Oct 23, 2020
0f6df85
Simplify examples. Add closing thoughts section.
rhettinger Oct 23, 2020
1e9482e
Add more section headings
rhettinger Oct 23, 2020
512fa4b
More wordsmithing
rhettinger Oct 23, 2020
0dc5f72
Wrap long lines
rhettinger Oct 23, 2020
10b9cd4
Clarify the motivation and the caller/callee relationship
rhettinger Oct 23, 2020
12b0fa2
Beautify technical tutorial sections
rhettinger Oct 23, 2020
8ca8c77
Move comments of the the code and into the main text
rhettinger Oct 23, 2020
cc22b88
Remove outdated references to Python 2 and new-style classes
rhettinger Oct 23, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add directory size example of a dynamic descriptor
  • Loading branch information
rhettinger committed Oct 23, 2020
commit fc4865a92014db2b69a300d1f9780a6a0e1ff637
53 changes: 44 additions & 9 deletions Doc/howto/descriptor.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,35 +19,70 @@ Primer

In this primer, we start with most basic possible example and then we'll add new capabilities one by one.

Super simple example: A descriptor that returns a constant
----------------------------------------------------------
Simple example: A descriptor that returns a constant
----------------------------------------------------

The :class:`Ten` is a descriptor that always returns a constant ``10``::
The :class:`Ten` class is a descriptor that always returns a constant ``10``::


class Ten:

def __get__(self, obj, objtype=None):
return 10

To use the descriptor, it must be stored as a class variable in another class::

class A:
x = 5 # Regular attribute
y = Ten() # Descriptor
x = 5 # Regular class attribute
y = Ten() # Descriptor

An interactive session shows the difference between normal attribute lookup and descriptor lookup::

>>> a = A() # Make an instance of class A
>>> a.x # Normal attribute lookup
>>> a = A() # Make an instance of class A
>>> a.x # Normal attribute lookup
5
>>> a.y # Descriptor lookup
>>> a.y # Descriptor lookup
10

In the ``a.x`` attribute lookup, the dot operator finds the value ``5`` stored in the class dictionary. In the ``a.y`` descriptor lookup, the dot operator calls the :meth:`get()` on the descriptor. That method returns ``10``. Note that the value ``10`` is not stored in either the class dictionary or instance dictionary. The value ``10`` is computed on demand.

This example shows how a simple descriptor works, but it isn't very useful. For retrieving constants, normal attribute lookup would almost always be better.

In the next section, we'll create something most useful, a dynamic lookup.

Dynamic lookups
---------------

Interesting descriptors typically run computations instead of doing lookups::


import os

class DirectorySize:

def __get__(self, obj, objtype=None):
return len(os.listdir(obj.dirname))

class Directory:

size = DirectorySize() # Descriptor

def __init__(self, dirname):
self.dirname = dirname # Regular instance attribute

An interactive session shows that the lookup is dynamic — it computes different, updated answers each time::

>>> g = Directory('games')
>>> s = Directory('songs')
>>> g.size # The games directory has three files
3
>>> os.system('touch games/newfile') # Add a fourth file to the directory
0
>>> g.size:
4
>>> s.size # The songs directory has twenty files
20

Besides showing how descriptors can run computations, this example also reveals the purpose of the parameters to :meth:`__get__`. The *self* parameter is *size*, an instance of *DirectorySize*. The *obj* parameter is either *g* or *s*, an instance of *Directory*. It is *obj* parameter that lets the :meth:`__get__` method learn the target directory. The *objtype* parameter is the class *Directory*.


Technical Tutorial
Expand Down