I would like to introduce a Python coding technique that I haven't seen much. I'm sorry if it was unexpectedly major. I would like to include as much sample code as possible to show how it actually works. Since it summarizes niche technologies, it is assumed that you can write Python to some extent.
functools.partial
You can use this to ** create a function with fixed arguments ** for a function. I think it's faster to look at the code.
import functools
def add(x, y):
return x + y
add_fixed_y = functools.partial(add, y=5)
print(add_fixed_y(2)) # 7
I don't know what this alone will be useful for, so I would like to introduce my own usage. I use it for the ** ʻopen` function ** of the file.
open_utf_8 = functools.partial(open, encoding="utf-8")
r_open_utf_8 = functools.partial(open_utf_8, mode="r")
w_open_utf_8 = functools.partial(open_utf_8, mode="w")
with w_open_utf_8("something_great.txt") as wf:
wf.write("Hello World!")
By doing this, you can reduce the constants from within the code and prevent typographical errors in the ʻencoding` part.
next
and generatorThe expressions inside the list comprehension can be used for generator expressions, and creating generator objects has been introduced in other articles.
gen = (i ** 2 for i in range(10))
print(gen) # <generator object <genexpr> at 0x~~>
How is this useful in actual coding? I use it to find the first index ** that meets the ** criteria. It is used for the process of ** rounding the character string to the specified number of bytes or less **.
from itertools import accumulate
def str_clamp_bytes(value: str, max_bytes: int) -> str:
byte_count = (len(s.encode("utf-8")) for s in value)
try:
end_slice = next(i for i, v in enumerate(accumulate(byte_count)) if v > max_bytes)
except StopIteration:
return value
return value[:end_slice]
(i for i, v in enumerate (accumulate (byte_count)) if v> max_bytes)
is the generator and ʻif v> max_bytes is the conditional expression. ʻEnd_slice
is assigned the first index that satisfies v> max_bytes
. If there is no index that meets the conditions, a StopIteration
exception will occur, so catch it and return the original string. The generator is sufficient because byte_count
is only used for ʻaccumulate`.
__debug__
This variable becomes False
when the -O
(not zero) option is added at startup. If you add the -O
option, the ʻassert` statement becomes invalid and you can speed up the program, so you can add it in the product version without any problem. I use it for the process of ** delivering only the product version **.
def broadcast_debug():
pass
def broadcast_prod():
pass
(broadcast_debug if __debug__ else broadcast_prod)()
lru_cache
decoratorBy remembering the function argument / return value pair in the dictionary, if the argument was passed before, the previous return value will be returned without processing.
from functools import lru_cache
@lru_cache
def fib(n):
if n < 2:
return n
return fib(n - 1) + fib(n - 2)
print(fib(10))
Since it is a dictionary, the access speed is inferior to pure memoization, but the ease of use is great. After that, it can be handled even if ** character strings etc. are used as arguments **, which cannot be handled by memoization.
inspect.getmembers
You can ** get all members from the module **. You can specify the member type with the second argument.
import inspect
import copy
print(inspect.getmembers(copy, inspect.isclass))
# >> [('Error', <class 'copy.Error'>), ('error', <class 'copy.Error'>)]
** You can extract all the classes from the module **, so you can get them all at once without the hassle of loaded_classes = [class_A, class_B, ...]
. And you don't have to worry about forgetting to add it. The class object has a __bases__
member, and all the inherited class objects are included as tuples, so I think you can use this area to filter.
I introduced a trick that I think is "convenient but I don't see much". Please let me know if you have any typos or mistakes.
Recommended Posts