3.6 KiB
[ Index | Exercise 5.3 | Exercise 5.5 ]
Exercise 5.4
Objectives:
- Learn more about closures
In this section, we look briefly at a few of the more unusual aspects of closures.
(a) Closures as a data structure
One potential use of closures is as a tool for data encapsulation. Try this example:
def counter(value):
def incr():
nonlocal value
+= 1
value return value
def decr():
nonlocal value
-= 1
value return value
return incr, decr
This code defines two inner functions that manipulate a value. Try it out:
>>> up, down = counter(0)
>>> up()
1
>>> up()
2
>>> up()
3
>>> down()
2
>>> down()
1
>>>
Notice how there is no class definition involved here. Moreover,
there is no global variable either. Yet, the up()
and
down()
functions are manipulating some “behind the scenes”
value. It’s fairly magical.
(b) Closures as a code generator
In Exercise 4.3, you developed a collection of descriptor classes that allowed type-checking of object attributes. For example:
class Stock:
= String()
name = Integer()
shares = Float() price
This kind of thing can also be implemented using closures. Define a
file typedproperty.py
and put the following code in it:
# typedproperty.py
def typedproperty(name, expected_type):
= '_' + name
private_name
@property
def value(self):
return getattr(self, private_name)
@value.setter
def value(self, val):
if not isinstance(val, expected_type):
raise TypeError(f'Expected {expected_type}')
setattr(self, private_name, val)
return value
This look pretty wild, but the function is effectively making code. You’d use it in a class definition like this:
from typedproperty import typedproperty
class Stock:
= typedproperty('name', str)
name = typedproperty('shares', int)
shares = typedproperty('price', float)
price
def __init__(self, name, shares, price):
self.name = name
self.shares = shares
self.price = price
Verify that this class performs type-checking in the same way as the descriptor code.
Add function String()
, Integer()
, and
Float()
to the typedproperty.py
file so that
you can write the following code:
from typedproperty import String, Integer, Float
class Stock:
= String('name')
name = Integer('shares')
shares = Float('price')
price def __init__(self, name, shares, price):
self.name = name
self.shares = shares
self.price = price
(c) Challenge: Eliminating names
Modify the typedproperty.py
code so that attribute names
are no-longer required:
from typedproperty import String, Integer, Float
class Stock:
= String()
name = Integer()
shares = Float()
price def __init__(self, name, shares, price):
self.name = name
self.shares = shares
self.price = price
Hint: To do this, recall the __set_name__()
method of
descriptor objects that gets called when descriptors are placed in a
class definition.
[ Solution | Index | Exercise 5.3 | Exercise 5.5 ]
>>>
Advanced Python Mastery
...
A course by dabeaz
...
Copyright 2007-2023
.
This work is licensed under a Creative Commons
Attribution-ShareAlike 4.0 International License