According to this video (https://youtu.be/ka70COItN40?t=1185), it is not a good idea to store many intermediate variables with the same name in a program because the state of this variable would change during the execution of the program.
A better way, according to the author of the video, would be to use reduce from functools and define compose() function to chain the different data transformations.
To illustrate with an example, imagine that I'm writing a program to scrape some text from the web, and I need to clean it for analyzing it later. The "wrong" way to do it would be to implement it along the following lines:
import regex
from html import unescape
from bs4 import BeautifulSoup
class MyAssignedClass():
def __init__(self, description):
self.description = description
def clean_description(self):
self.description = unescape(self.description)
self.description = BeautifulSoup(self.description, "html.parser").text
self.description = regex.sub("\n", " ", self.description)
self.description = regex.sub("\xa0", ' ', self.description)
self.description = regex.sub(" +", " ", self.description)
def main():
text = "I'm full of <p> <tags> <li> <a> <whatever> \n \xa0 things to clean & to skip."
A = MyAssignedClass(description=text)
A.clean_description()
print(f'MyAssignedClass.description: {A.description}')
if __name__ == "__main__":
main()
Output:
>>> MyAssignedClass.description: I'm full of things to clean & to skip.
If I follow the guidelines proposed in the video, I should create a compose() function instead to chain the transformations. The problem here is that I must use lambda functions everywhere to make it work:
import regex
from html import unescape
from bs4 import BeautifulSoup
from typing import Callable
import functools
ComposableFunction = Callable[[str], str]
def compose(*functions: ComposableFunction) -> ComposableFunction:
return functools.reduce(lambda f, g: lambda x: g(f(x)), functions)
class MyComposedClass():
def __init__(self, description):
self.description = description
def clean_description(self):
myfunc = compose(
unescape,
lambda _: BeautifulSoup(_, features="html.parser").text,
lambda _: regex.sub("\n", " ", _),
lambda _: regex.sub("\xa0", ' ', _),
lambda _: regex.sub(" +", " ", _)
)
self.description = myfunc(self.description)
def main():
text = "I'm full of <p> <tags> <li> <a> <whatever> \n \xa0 things to clean & to skip."
B = MyComposedClass(description=text)
B.clean_description()
print(f'MyComposedClass.description: {B.description}')
if __name__ == "__main__":
main()
Output:
>>> MyComposedClass.description: I'm full of things to clean & to skip.
However, wouldn't it be more readable to just use intermediate variables and assign self.description only at the end of the method?
import regex
from html import unescape
from bs4 import BeautifulSoup
class MyUnderAssignedClass():
def __init__(self, description):
self.description = description
def clean_description(self):
_ = unescape(self.description)
_ = BeautifulSoup(_, "html.parser").text
_ = regex.sub("\n", " ", _)
_ = regex.sub("\xa0", ' ', _)
_ = regex.sub(" +", " ", _)
self.description = _
def main():
text = "I'm full of <p> <tags> <li> <a> <whatever> \n \xa0 things to clean & to skip."
C = MyUnderAssignedClass(description=text)
C.clean_description()
print(f'MyUnderAssignedClass.description: {C.description}')
if __name__ == "__main__":
main()
Output:
>>> MyUnderAssignedClass.description: I'm full of things to clean & to skip.
Which are the most pythonic way of writing such piece of code? What are the pitfalls associated with using intermediate variables (and reasssigning the attribute many times within a method)?