Skip to content

Commit 5d96d0a

Browse files
committed
Move the usage docs here
1 parent 4798ed4 commit 5d96d0a

File tree

1 file changed

+228
-2
lines changed

1 file changed

+228
-2
lines changed

docs/usage.rst

Lines changed: 228 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,232 @@
22
Usage
33
=====
44

5-
To use lazy-object-proxy in a project::
5+
The lazy object proxy class is available as ``lazy_object_proxy.Proxy``.
66

7-
import lazy_object_proxy
7+
.. code:: pycon
8+
9+
>>> table = {}
10+
>>> import lazy_object_proxy
11+
>>> proxy = lazy_object_proxy.Proxy(lambda: table)
12+
>>> proxy['key-1'] = 'value-1'
13+
>>> proxy['key-2'] = 'value-2'
14+
15+
>>> sorted(proxy.keys())
16+
['key-1', 'key-2']
17+
>>> sorted(table.keys())
18+
['key-1', 'key-2']
19+
20+
>>> isinstance(proxy, dict)
21+
True
22+
23+
>>> dir(proxy)
24+
['__class__', ...'__contains__', '__delattr__', '__delitem__', ...'__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', ...'__setattr__', '__setitem__', ...'__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', ...]
25+
26+
27+
This ability for a proxy to stand in for the original goes as far as
28+
arithmetic operations, rich comparison and hashing.
29+
30+
::
31+
32+
>>> value = 1
33+
>>> proxy = lazy_object_proxy.Proxy(lambda: value)
34+
35+
>>> proxy + 1
36+
2
37+
38+
>>> int(proxy)
39+
1
40+
>>> hash(proxy)
41+
1
42+
>>> hash(value)
43+
1
44+
45+
>>> proxy < 2
46+
True
47+
>>> proxy == 0
48+
False
49+
50+
Do note however, that when wrapping an object proxy around a literal value,
51+
the original value is effectively copied into the proxy object and any
52+
operation which updates the value will only update the value held by the
53+
proxy object.
54+
55+
::
56+
57+
>>> value = 1
58+
>>> proxy = lazy_object_proxy.Proxy(lambda: value)
59+
>>> type(proxy)
60+
<... 'Proxy'>
61+
62+
>>> proxy += 1
63+
64+
>>> type(proxy)
65+
<... 'Proxy'>
66+
67+
>>> print(proxy)
68+
2
69+
>>> print(value)
70+
1
71+
72+
Object wrappers may therefore have limited use in conjunction with literal
73+
values.
74+
75+
Type Comparison
76+
---------------
77+
78+
The type of an instance of the object proxy will be ``ObjectProxy``, or that
79+
of any derived class type if creating a custom object proxy.
80+
81+
::
82+
83+
>>> value = 1
84+
>>> proxy = lazy_object_proxy.Proxy(lambda: value)
85+
>>> type(proxy)
86+
<... 'Proxy'>
87+
88+
>>> class CustomProxy(lazy_object_proxy.Proxy):
89+
... pass
90+
91+
>>> proxy = CustomProxy(lambda: 1)
92+
93+
>>> type(proxy)
94+
<class '...CustomProxy'>
95+
96+
Direct type comparisons in Python are generally frowned upon and allowance
97+
for 'duck typing' preferred. Instead of direct type comparison, the
98+
``isinstance()`` function would therefore be used. Using ``isinstance()``,
99+
comparison of the type of the object proxy will properly evaluate against
100+
the wrapped object.
101+
102+
::
103+
104+
>>> isinstance(proxy, int)
105+
True
106+
107+
This works because the ``__class__`` attribute actually returns the class
108+
type for the wrapped object.
109+
110+
::
111+
112+
>>> proxy.__class__
113+
<... 'int'>
114+
115+
Note that ``isinstance()`` will still also succeed if comparing to the
116+
``ObjectProxy`` type. It is therefore still possible to use ``isinstance()``
117+
to determine if an object is an object proxy.
118+
119+
::
120+
121+
>>> isinstance(proxy, lazy_object_proxy.Proxy)
122+
True
123+
124+
>>> class CustomProxy(lazy_object_proxy.Proxy):
125+
... pass
126+
127+
>>> proxy = CustomProxy(lambda: 1)
128+
129+
>>> isinstance(proxy, lazy_object_proxy.Proxy)
130+
True
131+
>>> isinstance(proxy, CustomProxy)
132+
True
133+
134+
135+
Custom Object Proxies
136+
---------------------
137+
138+
A custom proxy is where one creates a derived object proxy and overrides
139+
some specific behaviour of the proxy.
140+
141+
::
142+
143+
>>> def function():
144+
... print(('executing', function.__name__))
145+
146+
>>> class CallableWrapper(lazy_object_proxy.Proxy):
147+
... def __call__(self, *args, **kwargs):
148+
... print(('entering', self.__wrapped__.__name__))
149+
... try:
150+
... return self.__wrapped__(*args, **kwargs)
151+
... finally:
152+
... print(('exiting', self.__wrapped__.__name__))
153+
154+
>>> proxy = CallableWrapper(lambda: function)
155+
156+
>>> proxy()
157+
('entering', 'function')
158+
('executing', 'function')
159+
('exiting', 'function')
160+
161+
Any method of the original wrapped object can be overridden, including
162+
special Python methods such as ``__call__()``. If it is necessary to change
163+
what happens when a specific attribute of the wrapped object is accessed,
164+
then properties can be used.
165+
166+
If it is necessary to access the original wrapped object from within an
167+
overridden method or property, then ``self.__wrapped__`` is used.
168+
169+
Proxy Object Attributes
170+
-----------------------
171+
172+
When an attempt is made to access an attribute from the proxy, the same
173+
named attribute would in normal circumstances be accessed from the wrapped
174+
object. When updating an attributes value, or deleting the attribute, that
175+
change will also be reflected in the wrapped object.
176+
177+
::
178+
179+
>>> proxy = CallableWrapper(lambda: function)
180+
181+
>>> hasattr(function, 'attribute')
182+
False
183+
>>> hasattr(proxy, 'attribute')
184+
False
185+
186+
>>> proxy.attribute = 1
187+
188+
>>> hasattr(function, 'attribute')
189+
True
190+
>>> hasattr(proxy, 'attribute')
191+
True
192+
193+
>>> function.attribute
194+
1
195+
>>> proxy.attribute
196+
1
197+
198+
If an attribute was updated on the wrapped object directly, that change is
199+
still reflected in what is available via the proxy.
200+
201+
::
202+
203+
>>> function.attribute = 2
204+
205+
>>> function.attribute
206+
2
207+
>>> proxy.attribute
208+
2
209+
210+
Custom attributes can be specified as a class attribute, with
211+
that then being overridden if necessary, with a specific value in the
212+
``__init__()`` method of the class.
213+
214+
::
215+
216+
>>> class CustomProxy(lazy_object_proxy.Proxy):
217+
... attribute = None
218+
... def __init__(self, wrapped):
219+
... super(CustomProxy, self).__init__(wrapped)
220+
... self.attribute = 1
221+
222+
>>> proxy = CustomProxy(lambda: 1)
223+
>>> proxy.attribute
224+
1
225+
>>> proxy.attribute = 2
226+
>>> proxy.attribute
227+
2
228+
>>> del proxy.attribute
229+
>>> print(proxy.attribute)
230+
None
231+
232+
Just be aware that although the attribute can be deleted from the instance
233+
of the custom proxy, lookup will then fallback to using the class attribute.

0 commit comments

Comments
 (0)