Skip to content

Commit fdb7dd6

Browse files
committed
Cleanup
2 parents f85ee4f + 039ceff commit fdb7dd6

File tree

6 files changed

+156
-35
lines changed

6 files changed

+156
-35
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,7 @@ nosetests.xml
3434
.mr.developer.cfg
3535
.project
3636
.pydevproject
37+
38+
.venv
39+
dist
40+

Makefile

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
.PHONY: venv test dist upload_test upload rstcheck
2+
3+
venv:
4+
@echo "source .venv/bin/activate"
5+
6+
test:
7+
rm tests/**/*.pyc
8+
python3 -mpytest ./tests
9+
10+
dist:
11+
$(RM) -Rf dist
12+
python3 setup.py sdist bdist_wheel
13+
14+
upload_test: dist
15+
twine upload --repository-url https://test.pypi.org/legacy/ dist/*
16+
17+
upload: dist
18+
twine upload dist/*
19+
20+
rstcheck:
21+
python3 -mrstcheck README.rst
22+

README.rst

+116-25
Original file line numberDiff line numberDiff line change
@@ -9,35 +9,26 @@
99
:target: https://travis-ci.org/kutoga/lazy-load
1010
:alt: Latest Travis CI build status
1111

12-
A minimalistic interface that allows lazy evaluation of expressions / function calls / ...
13-
14-
TODO:
15-
16-
- Add examples
17-
- Create README
18-
- Comment code
19-
- pypi
20-
- Add more tests: E.g. for properties, force_eval for callables
21-
- Tox config
12+
A minimalistic interface that allows lazy evaluation of expressions and function calls.
2213

2314
Note: This small library is highly based on `python-lazy-object-proxy`.
2415

2516
Why using ℒazy-ℒoad? Lazy loading in general may make some software implementations much more efficient.
2617
Especially if it is not known if some data has to be loaded or not. Often the resulting code is less efficient,
27-
because eager loading is used or the code is not elegant.
18+
because eager loading is used or the code is not elegant, because one has to program (somehow) lazy loading.
2819

2920
Advantages of this library are that lazy-loading may be used quite elegant and effective.
3021

3122
Examples
3223
^^^^^^^^
3324

3425
In a loop it might happen that a special condition appears once or even more often. If this is the case,
35-
an expensive function `expensive_function` ha sto be called and on the resulting object an operation has
36-
to be done. The expensive function only has to be called once and the resulting object then might be
37-
reused.
26+
an expensive function `expensive_function` is called and on the resulting object an operation has
27+
to be done. If the expensive function had to called more than once, than the result object may be reused.
3828

3929
Possible implementation:
4030

31+
4132
.. code:: python
4233
4334
def expensive_function():
@@ -54,6 +45,7 @@ Possible implementation:
5445
5546
Given this library, it might be done like this:
5647

48+
5749
.. code:: python
5850
5951
from lazy_load import lazy
@@ -68,9 +60,55 @@ Given this library, it might be done like this:
6860
if test_for_something(x, y, p):
6961
obj.do_something(x, y)
7062
71-
The expensive function is used more often and always a lazy evaluation is done. Therefore, a decorator might
72-
be used to indicate that all function calls to this function shall be lazily evaluated. This makes it possible
73-
to normally use the function. The behaviour is still the same like in the first example:
63+
There are similar situations outside of loops. The implementation without `lazy-load` might look like this:
64+
65+
66+
.. code:: python
67+
68+
def expensive_function():
69+
print("function evaluation")
70+
...
71+
return result
72+
73+
obj = None
74+
def get_obj():
75+
global obj
76+
if obj is None:
77+
obj = expensive_function()
78+
return obj
79+
80+
if condition_a:
81+
get_obj().xyz()
82+
if condition_b:
83+
do_something()
84+
if condition_c:
85+
get_obj().abc()
86+
87+
This code can be realized much easier with `lazy-load`. Not only is the code shorter, but it is also more readable:
88+
89+
90+
.. code:: python
91+
92+
from lazy_load import lazy
93+
94+
def expensive_function():
95+
print("function evaluation")
96+
...
97+
return result
98+
99+
obj = lazy(expensive_function)
100+
101+
if condition_a:
102+
obj.xyz()
103+
if condition_b:
104+
do_something()
105+
if condition_c:
106+
obj.abc()
107+
108+
It might be the case that the expensive function is used more often and always a lazy evaluation is done.
109+
In this case, a decorator might be used to indicate that all function calls to this function shall be lazily
110+
evaluated. This makes it possible to normally use the function. The behaviour is still the same like in the first example:
111+
74112

75113
.. code:: python
76114
@@ -87,11 +125,14 @@ to normally use the function. The behaviour is still the same like in the first
87125
if test_for_something(x, y, p):
88126
obj.do_something(x, y)
89127
90-
A lazy evaluation of function / methods calls might be done with the `@lazy_func` decorator of with the `lazy`-call. This was already
128+
A lazy evaluation of functions / methods calls might be done with the `@lazy_func` decorator of with the `lazy`-call. This was already
91129
shown, therefore the following examples show how to do a one-shot lazy evaluation of a function call:
92130

131+
93132
.. code:: python
94133
134+
from lazy_load import lazy, lz
135+
95136
def expensive_func(x, y):
96137
print(f"function evaluation with arguments x={x}, y={y}")
97138
...
@@ -100,7 +141,7 @@ shown, therefore the following examples show how to do a one-shot lazy evaluatio
100141
# Possibility 1: Use `lazy` with a callable
101142
obj = lazy(lambda: expensive_func(a, b))
102143
103-
# Possibility 2: If it doesn't matter if the arguments for the expensive-function are eager evaluated, the call may be simplified:
144+
# Possibility 2: If it doesn't matter if the argument expressions for the expensive-function are eager evaluated, the call may be simplified:
104145
obj = lazy(expensive_func, a, b)
105146
106147
# Possibility 3: `lazy` has a short version / alias: `lz`
@@ -116,15 +157,16 @@ a function has the exact same signature as the original function.
116157
One might now like to have the possibility to on-the-fly convert a callable to a lazily evaluated callable.
117158
This might be done in the following way:
118159

160+
119161
.. code:: python
120162
163+
from lazy_load import lazy_func, lf
164+
121165
def expensive_func(x):
122-
print(d"function evaluation with argument x={x}")
166+
print(f"function evaluation with argument x={x}")
123167
...
124168
return result
125169
126-
from lazy_load import lazy_func, lf
127-
128170
# Possibility 1: Use `lazy_func`.
129171
my_obj.do_something(f=lazy_func(expensive_func))
130172
@@ -137,8 +179,11 @@ This might be done in the following way:
137179
Actually, I want to go deeper into the `ℒ`azy- or ``-"operator". This operator converts on-the-fly a function
138180
to a lazily evaluated function. Another example:
139181

182+
140183
.. code:: python
141184
185+
from lazy_load import
186+
142187
def test(name):
143188
print(f"hey {name}")
144189
return True
@@ -156,8 +201,11 @@ to a lazily evaluated function. Another example:
156201
157202
It is also possible to convert multiple functions to lazily evaluated functions using ``:
158203

204+
159205
.. code:: python
160206
207+
from lazy_load import
208+
161209
def f1(x):
162210
print(f"f1 {x}")
163211
return True
@@ -177,8 +225,11 @@ value are lazily evaluated. Public methods are all methods that have a name not
177225
Methods with a return value are identificated by the given return type hint which must not be `None`.
178226
This behaviour might be done with the `@lazy_class`-decorator (alias: `lc`):
179227

228+
180229
.. code:: python
181230
231+
from lazy_load import lazy_class
232+
182233
@lazy_class
183234
class MyClass:
184235
def __init__(self):
@@ -198,20 +249,60 @@ This behaviour might be done with the `@lazy_class`-decorator (alias: `lc`):
198249
...
199250
return result
200251
252+
Finally, it is also possible to force the evaluation of a lazy loading object by using `force_eval` (alias `fe`).
253+
This function can safely to used to non-lazy loading objects: It is then just equal to the identity function.
254+
255+
256+
.. code:: python
257+
258+
from lazy_load import lazy, force_eval
259+
260+
def f1(x):
261+
print(f"f1 {x}")
262+
return True
263+
264+
lazy_obj = lazy(f1, 1)
265+
266+
# The following expression prints "f1 1" and returns "True"
267+
force_eval(lazy_obj)
268+
269+
The `force_eval` function may also be applied to lazy-functions (which are created with `lazy_func(x)`, `@lazy_func`
270+
or with ``). This restores the original non-lazy / eager function. For non-lazy functions this call has no effect:
271+
272+
273+
.. code:: python
274+
275+
from lazy_load import lazy_func, force_eval
276+
277+
@lazy_func
278+
def f(x):
279+
print("hey")
280+
return x**2
281+
282+
# The following line prints nothing
283+
obj = f(2)
284+
285+
f_eager = force_eval(f)
286+
287+
# The following line prints "hey" and "obj" has immediatly the value "4"
288+
obj = f_eager(2)
289+
290+
201291
Installation
202292
------------
203293

204-
294+
`pip install lazy-load`
205295

206296
Requirements
207297
^^^^^^^^^^^^
208298

209-
Compatibility
210-
-------------
299+
Python 3.6 or Python 3.7.
211300

212301
Licence
213302
-------
214303

304+
MIT
305+
215306
Authors
216307
-------
217308

lazy_load/_lazy_load.py

+6-5
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@ def _is_lazy_object(obj: Any) -> bool:
1515
_T = TypeVar('T')
1616
def lazy(target: Callable[..., _T], *args: str, **kwargs: str) -> _T:
1717
"""
18-
Create a lazy object given a callable object and optional arguments.
18+
Create a lazy loading object given a callable object and optional arguments.
1919
The created object will be generated by evaluating the given function
2020
as soon as it is used.
2121
2222
Arguments:
23-
target {Callable[..., _T]} -- [The function to create the lazy object]
23+
target {Callable[..., _T]} -- [The function to create the lazy loading object]
2424
2525
Returns:
26-
_T -- [A proxy object for the lazy object.]
26+
_T -- [A proxy object for the lazy loading object.]
2727
"""
2828
if not callable(target):
2929
raise ValueError('Invalid target.')
@@ -78,7 +78,7 @@ def __getitem__(self, original_functions: Callable) -> Callable:
7878
return [self(original_function) for original_function in original_functions]
7979
return self(original_functions)
8080

81-
lazy_func = _LazyFunc()
81+
lazy_func: _LazyFunc = _LazyFunc()
8282
"""
8383
This object might be used to create lazy versions of functions, e.g. by calling:
8484
f_lazy = lazy_func(f)
@@ -106,7 +106,8 @@ def lazy_class(cls: Type) -> Type:
106106
"""
107107
A decorator for classes: It makes all methods of the class lazy which
108108
- are public (their name does not start with)
109-
- have type hints for the return type and this hint indicates that the method does return a value
109+
- have type hints for the return type and this hint indicates that the method
110+
does return a value
110111
111112
Arguments:
112113
cls {Type} -- [The target class]

requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
lazy-object-proxy==1.3.1
1+
lazy-object-proxy>=1.3.1

setup.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,21 @@ def read(filename):
1515

1616
setup(
1717
name="lazy_load",
18-
version="0.1.0",
18+
version="0.8.1",
1919
url="https://github.com/kutoga/lazy-load",
2020
license='MIT',
2121

2222
author="Benjamin Bruno Meier",
2323
author_email="[email protected]",
2424

25-
description="A minimalistic interface that allow lazy evaluation of expressions / function results / ...",
25+
description="ℒazy-ℒoad - A minimalistic interface that allows the lazy evaluation " +\
26+
"of expressions. Additional functions and wrappers allow it to easily " +\
27+
"use the lazy evaluation for functions and classes.",
2628
long_description=read("README.rst"),
2729

2830
packages=find_packages(exclude=('tests',)),
2931

30-
install_requires=[],
32+
install_requires=['lazy-object-proxy>=1.3.1'],
3133

3234
classifiers=[
3335
'Development Status :: 2 - Pre-Alpha',
@@ -36,5 +38,6 @@ def read(filename):
3638
'Programming Language :: Python :: 3.5',
3739
'Programming Language :: Python :: 3.6',
3840
'Programming Language :: Python :: 3.7',
39-
],
41+
]
4042
)
43+

0 commit comments

Comments
 (0)