Skip to content

Commit b1bae5e

Browse files
authored
Remove unfinished functools concept (#4196)
It should be reworked and re-added at a later date.
1 parent 43438ca commit b1bae5e

2 files changed

Lines changed: 2 additions & 355 deletions

File tree

concepts/functools/about.md

Lines changed: 1 addition & 312 deletions
Original file line numberDiff line numberDiff line change
@@ -1,312 +1 @@
1-
# About
2-
3-
The functools module is for higher-order functions: functions that act on or return other ***[functions](https://docs.python.org/3/tutorial/controlflow.html#defining-functions)***. It provides functions for working with other functions and callable objects to use or extend them without completely rewriting them.
4-
5-
## Memoizing the function calls
6-
7-
**Memoizing:** Storing the result of some expensive function, which is called with the same input again and again. So, we don't have to run the function repeatedly.
8-
9-
### ```@functools.lru_cache(maxsize=128, typed=False)```
10-
11-
***[@functools.lru_cache(maxsize=128, typed=False)](https://docs.python.org/3/library/functools.html#functools.lru_cache)*** Decorator to wrap a function with a memoizing callable that saves up to the maxsize most recent calls. It can save time when an expensive or I/O bound function is periodically called with the same arguments.
12-
13-
Since a dictionary is used to cache results, the positional and keyword arguments to the function must be hashable.
14-
15-
Here ```maxsize = 128``` means that it is going to memoize latest 128 function calls at max.
16-
17-
The lru_cache works the same way but it can cache at max maxsize calls and if type = True, then the function arguments of different types will be cached separately i.e. 5 and 5.0 will be cached differently.
18-
19-
### ```@functools.cache(user_function)```
20-
21-
***[@functools.cache(user_function)](https://docs.python.org/3/library/functools.html#functools.cache)*** the same as lru_cache(maxsize=None), creating a thin wrapper around a dictionary lookup for the function arguments. Because it never needs to evict old values, this is smaller and faster than ```lru_cache()``` with a size limit.
22-
23-
```python
24-
25-
>>> @cache
26-
>>> def factorial(n):
27-
>>> return n * factorial(n-1) if n else 1
28-
29-
>>> factorial(10) # no previously cached result, makes 11 recursive calls
30-
3628800
31-
>>> factorial(5) # just looks up cached value result
32-
120
33-
>>> factorial(12) # makes two new recursive calls, the other 10 are cached
34-
479001600
35-
36-
# The lru_cache works the same way but it can cache at max maxsize calls and if type = True, then the function arguments of different types will be cached separately.
37-
38-
# Some types such as str and int may be cached separately even when typed is false.
39-
40-
>>> @lru_cache(maxsize = 128)
41-
>>> def factorial(n):
42-
>>> return n * factorial(n-1) if n else 1
43-
44-
>>> factorial(10)
45-
3628800
46-
47-
# by the Following we can fetch the information about the cache.
48-
>>> factorial.cache_info()
49-
CacheInfo(hits=0, misses=11, maxsize=128, currsize=11)
50-
```
51-
52-
## Generic functions
53-
54-
***[Generic functions](https://pymotw.com/3/functools/#generic-functions)*** are those which perform the operation based on the argument given to them. In statically typed languages it can be done by function overloading.
55-
56-
In python functools provides the `singledispatch()` decorator to register a set of generic functions for automatic switching based on the type of the first argument to a function.
57-
58-
The ```register()``` attribute of the function serves as another decorator for registering alternative implementations.To add overloaded implementations to the function, use the ```register(type)``` attribute of the generic function.
59-
60-
When user is going to call the function with the integer argument, then it will be redirected to the function decorated with ```register(int)``` decorator.
61-
62-
The first function wrapped with singledispatch() is the default implementation if no other type-specific function is found, default implementation will be called.
63-
64-
```python
65-
66-
>>> from functools import singledispatch
67-
68-
>>> @singledispatch
69-
def fun(arg):
70-
print("default argument string: ", arg)
71-
72-
73-
>>> fun.register(int)
74-
def _(arg):
75-
print("This is an integer: ", arg)
76-
77-
>>> fun.register(list)
78-
def _(arg):
79-
print("This is a list: ", arg)
80-
81-
>>> fun("Hello")
82-
"default argument string: Hello"
83-
84-
>>> fun(10)
85-
"This is an integer: 10"
86-
87-
>>> fun([1,2,3])
88-
"This is a list: [1,2,3]"
89-
90-
# This will call the default function as we didn't registered any function with float.
91-
>>> fun(2.45)
92-
"default argument string: 2.45"
93-
94-
```
95-
96-
For class methods we can use ***[singledispatchmethod(func)](https://docs.python.org/3/library/functools.html#functools.singledispatchmethod)*** to register a set of generic methods for automatic switching based on the type of the first non-self or non-class argument to a function.
97-
98-
```python
99-
100-
>>> class Negator:
101-
@singledispatchmethod
102-
def neg(self, arg):
103-
raise NotImplementedError("Cannot negate a")
104-
105-
@neg.register(int)
106-
def _(self, arg):
107-
return -arg
108-
109-
@neg.register(bool)
110-
def _(self, arg):
111-
return not arg
112-
113-
>>> obj = Negator()
114-
115-
# Going to call function which is register with bool datatype.
116-
>>> obj.neg(True)
117-
False
118-
119-
# Going to call function which is register with int datatype.
120-
>>> obj.neg(10)
121-
-10
122-
123-
# Going to call default function and will display an error message.
124-
>>> obj.neg("String")
125-
126-
```
127-
128-
## Partial
129-
130-
`functools.partial(func, /, *args, **keywords)` return a new ***[partial object](https://docs.python.org/3/library/functools.html#partial-objects)*** which when called will behave like func called with the positional arguments args and keyword arguments keywords. If more arguments are supplied to the call, they are appended to args.The ***[partial](https://docs.python.org/3/library/functools.html#functools.partial)*** is used for partial function application which “freezes” some portion of a function’s arguments and/or keywords resulting in a new object with a simplified signature.
131-
132-
```python
133-
134-
>>> def add(a, b):
135-
print(f"got a={a}, b={b}")
136-
print(a+b)
137-
138-
>>> a = partial(add, 10)
139-
>>> a(4)
140-
"got a=10, b=4"
141-
14
142-
143-
# 10 got assigned to a because partial start assigning arguments from the left.
144-
145-
>>> a = partial(add, b=10)
146-
>>> a(4)
147-
"got a=4, b=10"
148-
14
149-
150-
# But By using the keywords we can assign the value to the arguments at right
151-
152-
```
153-
154-
### partial Objects
155-
156-
partial objects are callable objects created by partial(). They have three read-only attributes:
157-
158-
```partial.func```
159-
160-
A callable object or function. Calls to the partial object will be forwarded to func with new arguments and keywords.
161-
162-
```partial.args```
163-
164-
The leftmost positional arguments that will be prepended to the positional arguments provided to a partial object call.
165-
166-
```partial.keywords```
167-
168-
The keyword arguments that will be supplied when the partial object is called.
169-
170-
```python
171-
172-
>>> from functools import partial
173-
174-
>>> pow_2 = partial(pow, exp = 2)
175-
176-
>>> pow_2.func == pow
177-
True
178-
179-
>>> pow_2.args
180-
()
181-
182-
>>> pow_2.keywords
183-
{'exp': 2}
184-
185-
>>> two_pow = partial(pow, 2)
186-
187-
>>> two_pow(3) # 2(frezzed) ^ 3 = 8 == pow(2 [fixed] ,3 [passed by user])
188-
8
189-
190-
>>> pow_2.args
191-
(2,)
192-
193-
```
194-
195-
The ```pow_2.func``` is same as the ```pow``` function.
196-
197-
Here ```pow_2.args``` returns an empty tuple because we do not pass any positional argument to our partial object call.
198-
199-
```pow_2.keywords``` returns a dictionary of keywords argument which will be supplied when the partial object is called.
200-
201-
Here ```two_pow.args``` returns a ```(2,)``` tuple because we passed 2 as an argument while creating the partial object, which fixed the value of ```base``` argument as ```2```.
202-
203-
### ```partialmethod```
204-
205-
***[functools.partialmethod(func, /, *args, **keywords)](https://docs.python.org/3/library/functools.html#functools.partialmethod)*** Return a new partialmethod descriptor which behaves like partial except that it is designed to be used as a method definition rather than being directly callable.
206-
207-
```python
208-
209-
>>> class Cell:
210-
def __init__(self):
211-
self.alive = False
212-
213-
def set_state(self, state):
214-
self.alive = bool(state)
215-
216-
# going to return a method set_state with argument state = True
217-
set_alive = partialmethod(set_state, True)
218-
# going to return a method set_state with argument state = False
219-
set_dead = partialmethod(set_state, False)
220-
221-
>>> c = Cell()
222-
>>> c.alive
223-
False
224-
>>> c.set_alive()
225-
>>> c.alive
226-
True
227-
228-
```
229-
230-
## Wraps
231-
232-
### `functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)`
233-
234-
***[functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)](https://docs.python.org/3/library/functools.html#functools.update_wrapper)*** Update a wrapper function to look like the wrapped function. The optional arguments are tuples to specify which attributes of the original function are assigned directly to the matching attributes on the wrapper function and which attributes of the wrapper function are updated with the corresponding attributes from the original function.
235-
236-
WRAPPER_ASSIGNMENTS (which assigns to the wrapper function’s `__module__`, `__name__`, `__qualname__`, `__annotations__` and `__doc__`, the documentation string)
237-
238-
WRAPPER_UPDATES (which updates the wrapper function’s `__dict__`, i.e. the instance dictionary).
239-
240-
```python
241-
242-
# without update_wrapper()
243-
244-
>>> def decorator(func):
245-
def wrapper(name):
246-
"""Going to say Hello"""
247-
print("hello",name)
248-
func(name)
249-
return wrapper
250-
251-
252-
>>> @decorator
253-
def fun(name):
254-
"""Going to Wish"""
255-
print("good morning",name)
256-
257-
# In bigger python code base this will cause problem while debugging the code.
258-
>>> fun.__name__
259-
'wrapper'
260-
>>> fun.__doc__
261-
'Going to say Hello'
262-
263-
# with update_wrapper()
264-
265-
>>> def decorator(func):
266-
def wrapper(name):
267-
"""Going to say Hello"""
268-
print("hello",name)
269-
func(name)
270-
update_wrapper(wrapper, func)
271-
return wrapper
272-
273-
274-
>>> @decorator
275-
def fun(name):
276-
"""Going to Wish"""
277-
print("good morning",name)
278-
279-
# Now the wrapper function just look like the wrapped(fun) function
280-
>>> fun.__name__
281-
'fun'
282-
>>> fun.__doc__
283-
'Going to Wish'
284-
```
285-
286-
### `functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)`
287-
288-
***[functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)](https://docs.python.org/3/library/functools.html#functools.wraps)*** is a convenience function for invoking update_wrapper() as a function decorator when defining a wrapper function. It is equivalent to partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated).
289-
290-
```python
291-
292-
# This going to work same as the above where we are using the update_wrapper() function
293-
>>> def decorator(func):
294-
@wraps(fun)
295-
def wrapper(name):
296-
"""Going to say Hello"""
297-
print("hello",name)
298-
func(name)
299-
return wrapper
300-
301-
302-
>>> @decorator
303-
def fun(name):
304-
"""Going to Wish"""
305-
print("good morning",name)
306-
307-
# Now the wrapper function just look like the wrapped(fun) function
308-
>>> fun.__name__
309-
'fun'
310-
>>> fun.__doc__
311-
'Going to Wish'
312-
```
1+
#TODO: Add about for this concept.

concepts/functools/introduction.md

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1 @@
1-
# Introduction
2-
3-
The functools module is for higher-order functions: functions that act on or return other ***[functions](https://docs.python.org/3/tutorial/controlflow.html#defining-functions)***. It provides functions for working with other functions and callable objects to use or extend them without completely rewriting them.
4-
5-
## Memoizing the function calls
6-
7-
**Memoizing:** Storing the result of some expensive function, which is called with the same input again and again. So, we don't have to run the function repeatedly.
8-
9-
### ```@functools.lru_cache(maxsize=128, typed=False)```
10-
11-
***[@functools.lru_cache(maxsize=128, typed=False)](https://docs.python.org/3/library/functools.html#functools.lru_cache)*** Decorator to wrap a function with a memoizing callable that saves up to the maxsize most recent calls. It can save time when an expensive or I/O bound function is periodically called with the same arguments.
12-
13-
Since a dictionary is used to cache results, the positional and keyword arguments to the function must be hashable.
14-
15-
Here ```maxsize = 128``` means that it is going to memoize latest 128 function calls at max.
16-
17-
### ```@functools.cache(user_function)```
18-
19-
***[@functools.cache(user_function)](https://docs.python.org/3/library/functools.html#functools.cache)*** the same as lru_cache(maxsize=None), creating a thin wrapper around a dictionary lookup for the function arguments. Because it never needs to evict old values, this is smaller and faster than ```lru_cache()``` with a size limit.
20-
21-
## Generic functions
22-
23-
***[Generic functions](https://pymotw.com/3/functools/#generic-functions)*** are those which preform the operation based on the argument given to them.
24-
25-
In statically typed languages it can be done by function overloading, In python functools provides the ```singledispatch(func)``` decorator to register a set of generic functions for automatic switching based on the type of the first argument to a function.
26-
27-
For class methods we can use ***[singledispatchmethod(func)](https://docs.python.org/3/library/functools.html#functools.singledispatchmethod)*** to register a set of generic methods for automatic switching based on the type of the first non-self or non-class argument to a function.
28-
29-
## Partial
30-
31-
`functools.partial(func, /, *args, **keywords)` return a new ***[partial object](https://docs.python.org/3/library/functools.html#partial-objects)*** which when called will behave like func called with the positional arguments args and keyword arguments keywords. If more arguments are supplied to the call, they are appended to args.The ***[partial](https://docs.python.org/3/library/functools.html#functools.partial)*** is used for partial function application which “freezes” some portion of a function’s arguments and/or keywords resulting in a new object with a simplified signature.
32-
33-
***[functools.partialmethod(func, /, *args, **keywords)](https://docs.python.org/3/library/functools.html#functools.partialmethod)*** Return a new partialmethod descriptor which behaves like partial except that it is designed to be used as a method definition rather than being directly callable.
34-
35-
## Wraps
36-
37-
### `functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)`
38-
39-
***[functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)](https://docs.python.org/3/library/functools.html#functools.update_wrapper)*** Update a wrapper function to look like the wrapped function. The optional arguments are tuples to specify which attributes of the original function are assigned directly to the matching attributes on the wrapper function and which attributes of the wrapper function are updated with the corresponding attributes from the original function.
40-
41-
### `functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)`
42-
43-
***[functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)](https://docs.python.org/3/library/functools.html#functools.wraps)*** is a convenience function for invoking update_wrapper() as a function decorator when defining a wrapper function. It is equivalent to partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated).
1+
#TODO: Add introduction for this concept.

0 commit comments

Comments
 (0)