☰
Current Page
Main Menu
Home
Home
Editing 10 Ways to Write Better Python Codes
Edit
Preview
H1
H2
H3
default
Set your preferred keybinding
default
vim
emacs
markdown
Set this page's format to
AsciiDoc
Creole
Markdown
MediaWiki
Org-mode
Plain Text
RDoc
Textile
Rendering unavailable for
BibTeX
Pod
reStructuredText
Help 1
Help 1
Help 1
Help 2
Help 3
Help 4
Help 5
Help 6
Help 7
Help 8
Autosaved text is available. Click the button to restore it.
Restore Text
https://levelup.gitconnected.com/10-ways-to-write-better-python-codes-55fc862ab0ef # 10 Ways to Write Better Python Codes | by Nikita Prasad | Level Up Coding We’re almost halfway into 2024, the tech industry is moving faster than ever, due to the rise of **_Generative AI and Large Language Models_**. > **Bonus:** Read about the trending **Technology behind Generative AI!**, [**here**](https://ai.plainenglish.io/how-large-language-models-work-ae40b277ff5c).👈🏻 As a data enthusiast and python developer you should be knowing these cool tips and trick to stay relevant in todays competitive market, where lakhs of tech-people were laid off. First thing to note here is that, Why Python? ----------- Python is an extremely powerful general purpose programming language, highly suitable for Data Science related tasks — utilizing it’s extensive libraries and awesome frameworks like **_SciKit-Learn, TensorFlow, PyTorch_**, etc., due to it’s easy-to-understand syntax. This means having Python under your belt can provide you with flexibility to create large variety of applications, for example, **_automation tasks_** or **_building chatbots to interact with open-source LLMs_**, especially if you’re a beginner. As it’s quite straightforward and quick to learn as compared to other programming languages. So, that being said let’s get into the coding, and explore, How to write better Python codes? --------------------------------- I’ll be discussing **_10 tips_** that can surely **_transform and elevate your coding practices_**. > If you find these tips helpful, do not forget to **clap** 👏🏻 and **comment**🖋️ down your thoughts! 1\. Using “Generators” to Save Memory ------------------------------------- Let’s say, you need to analyze a \`large\_file\` that _cannot fits into your RAM_. To tackle this problem efficiently, you can create a \`process\_large\_file\` generator function, **_to read the large file line by line_**. > Generators not only helps in processing the large data into batches, but are also **memory-efficient**, as you don’t need to store the whole data in memory. ``` def process_large_file(file_path): """ Generator function to read a large file line by line. """ with open(file_path, 'r') as file: for line in file: yield line # Specify the path to your large file log_file_path = 'path/to/your/large_file.txt' # Use the generator to process the large file line by line for line in process_large_file(log_file_path): print(line.strip()) # .strip() removes any extra whitespace or newline characters ``` The above code displays the line by line output which is written in your \`large\_file.txt\` file. > Even **Keras Framework** uses **generator** to leverage multi-processing (to load and process data in batches) in parallel to reduce training time. 2\. Using “.setdefault()” in Dictionaries ----------------------------------------- Suppose, you’re _managing an inventory system_, and want to keep track of stock-levels of various products. When a new product is added to the system, _to ensure that it has default stock level if it hasn’t been set yet_. You can use `setdefault()` function to streamline this process by inserting key with a specified default value if the key is not already present in a dictionary. ``` # Initial inventory inventory: dict[str, int] = {"jeans": 500, "top": 600} # Add more products with default stock levels if they are not already present products_to_add: list[str] = ["skirt", "shirt", "tee"] for product in products_to_add: inventory.setdefault(product, 500) # Print the final updated inventory print("Final updated inventory:", inventory) """ # Output: Final updated inventory: {'jeans': 500, 'top': 600, 'skirt': 500, 'shirt': 500, 'tee': 500} """ ``` This way you can avoid the need for **_explicit checks and assignments_**, making code more concise and readable. > **⏸️ Quick Pause:** > > Read about the breakthroughs in **Generative AI, Data Science, Data Analytics,** and much more directly into your Inbox, **Subscribe** [**HERE**](https://analyticalnikita.substack.com/)👈🏻 3\. Using Dictionaries to avoid “if-elif” hell ---------------------------------------------- Suppose you have several functions, that you want to call depending upon the user input. Well, the most common way of solving this problem is to use `if-elif` conditions, but that approach can become very long and complex, while dealing with hundreds of functions. The alternative approach would be to **_create a dictionary_**, that contains the key — you want to check against the functions to be run as value. ``` from collections.abc import Callable # Function 1 def first(): print("Calling First Function...") # Function 2 def second(): print("Calling Second Function...") # Function 3 def third(): print("Calling Third Function...") # Function Default def default(): print("Calling Default Function...") # User Input options: int = int(input("Enter an option :", )) # Dictionary to hold options as key and functions as values funcs_dict : dict[int, Callable[[], None]] = {1: first, 2: second, 3: third} # Checking if the key exist and incase it doesn't then deafult function will run final_result = funcs_dict.get(options, default) final_result() ``` When you run the program, you can see the following results on your screen. ``` """ # Output: # When Option send was 0 Enter an option :0 Calling Default Function... # When Option send was 1 Enter an option :1 Calling First Function... # and so on... """ ``` **_Note_**: If user asks for anything random, it’ll run the \`default()\` function. 4\. Using “Counter” from Collections Module ------------------------------------------- Trust me on this, while working with the large text data, _the most common task in text analysis_ includes **_identifying key terms_**, for which you’ll need to determine the **_frequency of each word in the particular document or whole corpus_** depending upon the problem statement. `Counter` provides a simple and efficient way to count elements in an iterable, abstracting the complexity of writing custom counting logic. Let’s implement this, ``` from collections import Counter import re # Read the text file with open("sample_text.txt", "r") as file: text = file.read() # Clean and Tokenize the text cleaned_text: str = re.sub(r'[^\w\s]', '', text.lower().split()) # Use Counter() to count the words word_counts: Counter = Counter(cleaned_text) # Printing second highest most common word most_commmon = counter.most_common(2) # passed in Number will denotes how many common numbers we want (counting starts from 1-n) print("Second Most Common Word is: ", most_commmon[0]) # print in zeroth index element from 2 most common ones """ # Output: Second Most Common Number is: ('data', 82) """ ``` **_Note_**: Additionally, you can also perform _arithmetic operations_, easily convert `Counter` object to other data structure like _dictionaries_, and utilize it’s useful methods like `element()`, `most_common()`, etc. 5\. Using “Memoization” to Optimize Code ---------------------------------------- Memoization is a technique used in dynamic programming to **_improve the time complexity of recursive algorithms_**, by _reusing the expensive function calls when the same input occur again._ The classic example of this is **the Rabbit Problem**, popularly known as the **Fibonacci Series**. ``` import time def memo_fibonacci(num: int, dictionary: dict[int, int]): if num in dictionary: return dictionary[num] else: dictionary[num] = memo_fibonacci(num-1, dictionary) + memo_fibonacci(num-2, dictionary) return dictionary[num] # Catching using a Dictionary dictionary: dict[int, int] = {0: 1, 1: 1} # Elapsed time start_time: float = time.time() # Calling the function result: int = memo_fibonacci(48, dictionary) end_time: float = time.time() # Calculating the elapsed time elapsed_time: float = end_time - start_time print(f"Result: {result}") # Result: 7778742049 print(f"Elapsed time: {elapsed_time:.6f} seconds") # Elapsed time: 0.000133 seconds ``` **_Note_**: Certainly, this significantly reduces the time complexity. But remember that it comes with the **_space-time tradeoff_**, as you maintain a cache to store the results, that you need to take-care off. 6\. Using “@decorators” to avoid Repetitiveness ----------------------------------------------- Let’s say you’re building a python project and you want to time, **_how long a function takes to run_**. Surely as above, you can use `time` functionality for that function but _what if you’ve tens or may be hundreds of functions_? It would take forever to write ‘start-time’ and ‘end-time’, _instead we can create a function \`elapsed\_time\` to do the same for us_. We’ll only have to **_add \`@elapsed\_time\`_** over the function that we want to time. > What are the \`**@decorators\`** ? > > Decorators are unique python functionality that **wrap round your existing function** to allow you to **modify or enhance functions** without changing the core logic, before or after a function executes. Python sees, the `@` symbol, and understand that this function under it needs to be passed into a function called \`elapsed\_time\`, then the function runs in \`elapsed \_time\` with the extra lines of code wrapped round it, to time any number of functions. ``` import time def elapsed_time(func): def wrapper(): start_time: float = time.time() func() end_time: float = time.time() - start_time print(f"{func.__name__}() took {end_time:.6f} seconds") return wrapper @elapsed_time def some_code(): # Simulating running code.. time.sleep(0.00002) # Calling the function some_code() # some_code() took 0.000009 seconds ``` They’re widely used for **_logging, timing, enforcing access control_** and more. > **_Note_**: But, it is recommended to don’t over do it though, because they can also obfuscate, what you're code is actually doing. 7\. Using \`dataclass\` for Clean Data Structures ------------------------------------------------- It’s really tedious to write the `__init__` method repeatedly in regular classes that’s designed to hold only the data values, due to the rise of potential errors. (Image by Author) However, featured in Python 3.7, the `dataclasses` module is a more efficient way to store data that will be passed between different parts of a program. > Notice, in just few lines we can create less error prone data classes, without the need of writing a constructor and several other already implemented method manually. ``` from dataclasses import dataclass @dataclass class Employee: id_: int name: str salary: float e1 = Employee(id_=1, name='Tusk', salary=69999.99) print(e1) # Employees(id_=1, name='Tusk', salary=69999.99) ``` Here, the output is also equivalent to the standard Python class implemented using `__repr__`. **_Note_:** We can also, customize the representation of our Employee class: ``` from dataclasses import dataclass @dataclass class Employee: id_: int name: str salary: float def __repr__(self): return f"Employee(id_={self.id_}, name={self.name}, salary={self.salary})" def __str__(self): return f"{self.name} earns ${self.salary}." e1 = Employee(id_=1, name='Tusk', salary=69999.99) print(repr(e1)) # Employee(id_=1, name=Tusk, salary=69999.99) print(str(e1)) # Tusk earns $69999.99. ``` > **Note**: > > — The `__repr__` method provides an unambiguous representation of the `Employee` object. > > — The `__str__` method provides a more readable and concise description of the `Employee` object. Start using `dataclass` if you aren’t, to reduce the boilerplate code, making your code much more readable and maintainable. 8\. Using “match” for Clean Input Handling ------------------------------------------ Hot off since Python 3.10, the structural pattern matching has been added as `match` patterns with associated `case` statements. Let’s say we have a class “Point”, that represents a point in the 2D coordinate system. Now, we’ll create a function “where\_is” to handle the cases that a user inputs to find the point in the 2D plane. A \`**match**\` statement takes an expression and compares its value to the successive patterns case block/s. ``` from dataclasses import dataclass # Defining a class using dataclass @dataclass class Point: x: int y: int # Match statements to handle different cases def where_is(point): match point: case Point(x=0, y=0): return ("Origin") case Point(x=0, y=y): return (f"Y={y}") case Point(x=x, y=0): return (f"X={x}") case Point(x, y): return("Somewhere else") # To catch anything else that the user inputs case _: return("Not a point") # Examples print(where_is(Point(0, 0))) # Output: Origin print(where_is(Point(0, 10))) # Output: Y=10 print(where_is(Point(10, 0))) # Output: X=10 print(where_is(Point(10, 10))) # Output: Somewhere else print(where_is("Not a point")) # Output: Not a point ``` Using `match-case` statements you can handle all the possible cases, ensuring _exhaustive pattern matching_. 9(A). Using “all” Operator instead “and” ---------------------------------------- Imagine you’re building a user profile system, and want to validate that all required fields in a form are filled out by the user (I don’t know why would you not mark \*required in the form instead,🤷🏻♀️but let’s just concentrate here👇). Well, you can use `all` function that will return `True`, if and only if all elements in the provided iterable are `True`, instead of `and` conditions. ``` # User input from a registration form form_data: dict[str, str] = {"name" : "Nikita", "email": "analyticalnikita@gmail.com", "phone": "123478911"} # List of reuired fields required_fields: list[str] = ["name", "email", "phone"] # Using all operator if all(field in form_data for field in required_fields): print("All required fields are filled out.") else: print("Some required fields are missing or empty.") """ # Output: All required fields are filled out. """ ``` 9(B). Using “any” Operator instead “or” --------------------------------------- The `any` function returns `True`, if any element provided in the iterable is `True`. For instance, you have to restrict some permissions to certain users, based upon certain criteria for that you can use `any`, instead of `or` condition. ``` # List of permissions to the user user_permission: list[str] = ["read", "execute"] # Check if user has at least one of the required permissions required_permissions: list[str] = ["write", "read", "admin"] # Using "all" operator if any(permission in user_permission for permission in required_permissions): print(f"Since you have required permissions. Access is allowed.") else: print("You're a standard user. Not allowed the access.") """ # Output: Since you have required permissions. Access is allowed. """ ``` These are some examples that shows how `any` and `all` can be used to simplify conditions that would otherwise require multiple `or` or `and` statements, respectively. Last but not the least, every programmer’s all-time _(hey, if not yours, then stick with me to learn these😉)_. 10\. Using the Comprehensions for Shorter Syntax ------------------------------------------------ Comprehension is the powerful toolkit python provide you with for all the iterable datatypes. This gives a concise way to avoid multiple-line loops using single line, depending upon the situation. Let’s explore them one-by-one: 10(A). List Comprehensions -------------------------- Here, I’m using a nested-if statements example, to show you the power of list comprehension ``` # Nested-if using List Comprehension fruits: list[str] = ["apple", "orange", "avacado", "kiwi", "banana"] basket: list[str] = ["apple", "avacado", "apricot", "kiwi"] [i for i in fruits if i in basket if i.startswith("a")] # ['apple', 'avacado'] ``` Similarly, you can also apply _nested-for loops_, until it make your comprehension hard-to-read. 10(B). Tuple Comprehensions --------------------------- As such tuple comprehensions doesn’t exist in Python. Instead you can use **generator expressions** to create tuples. ``` # Generator expression converted to a tuple tuple(i**2 for i in range(10)) # (0, 1, 4, 9, 16, 25, 36, 49, 64, 81) ``` 10(C). Dictionary Comprehensions -------------------------------- Let say you’ve a list \`apple\_names\` and you want to print the new list that contains the length of each elements of the list \`apple\_names\`. Surely, you can use _list comprehension_ here. But do you know, you can actually use this notation to create a dictionary, it’s called, well yes, the _dictionary comprehension_. ``` # Creating a list of apple names apple_names: list[str] = ["apple", "pineapple", "green apple"] # Creating a dictionary with apple names as keys and their lengths as values print({apple_name: len(apple_name) for apple_name in apple_names}) # {"apple": 5, "pineapple": 9, "green apple": 11} ``` Alternatively, it’s more readable than using loops or `dict` constructor to create a dictionary. 10(D). Set Comprehensions ------------------------- You can also use filtering based on certain conditions, in comprehensions. ``` # Creating a set with condition print({i**2 for i in range(1, 11) if i > 5}) # {64, 36, 100, 49, 81} ``` **_Note_**: While comprehensions are expressive, this doesn’t means they’re suitable for all the cases, especially involving too complex logic. Conclusion ---------- Remember, **_you don’t write codes for computers but for the team you’re working with_**. So, writing better codes is really important, for others to understand your production-level codes. > Find the full scope of codes [**here**](https://github.com/nikitaprasad21/Python-Cheat-Codes-2024)**.** Star⭐ the GitHub repository. By adopting these tips, you not only **_write efficient code_** but also boost your **_productivity_**. > Additionally, to take your coding skills to next level, understand Python **Namespaces and Scopes** to better **Optimize Your Code**, [**here**](https://levelup.gitconnected.com/stop-struggling-with-namespaces-and-scopes-in-python-837c82cc8c19).👈🏻 Before you go… If you have any _questions/suggestions/thoughts_, do drop me a line below. 🖋️ And, if you enjoy this read **Clap _50 👏_** times and, do not forget to [**_follow_**](https://medium.com/@nikita-prasad-analyst) for future updates. > 👉 [**SUBSCRIBE**](https://analyticalnikita.substack.com/) to my newsletter, **Epochs of Data Insights,** and receive bi-weekly in-depth Data, AI & ML tips and insights_,_ right in your inbox! That’s it from me. Will talk soon! 🙋🏻♀️ — [_Nikita Prasad_](https://medium.com/@nikita-prasad-analyst)
Uploading file...
Edit message:
Cancel