25 Python Habits That Scream Beginner
A comprehensive guide to 25 common Python beginner mistakes, from string formatting and file handling to comprehension overuse and Python 2 misconceptions. The video covers both actual code issues and style choices that signal inexperience.
String & File Handling
Manual string formatting with plus signs
Concatenating strings with + is less readable and error-prone than f-strings. F-strings are the modern Python standard for string formatting.
Manually closing files instead of using with statements
Calling close() directly can fail if an exception occurs during file operations. The with statement (context manager) guarantees file closure even when exceptions are thrown.
Using try-finally instead of context managers
Most Python resources that need cleanup have built-in context managers. Using with statements is cleaner and more Pythonic than try-finally blocks.
Exception Handling & Type Checking
Bare except clauses trap users
A bare except catches keyboard interrupts (Ctrl+C) and system exits, preventing users from exiting. Use except Exception or catch specific exceptions instead.
Confusing caret with exponentiation
The ^ operator performs bitwise XOR, not exponentiation. Use ** for exponentiation.
Using == to check types instead of isinstance
Type equality checks violate the Liskov substitution principle. Use isinstance() to allow subclasses to work where parent classes are expected.
Using == for None, True, False instead of is
Identity checks with is are the correct way to compare against None, True, and False. It's more direct and idiomatic than equality checks.
Function Arguments & Defaults
Mutable default arguments shared across calls
Function defaults are evaluated once at definition time, not at each call. Using a mutable default (like a list) means all calls share the same object. Set mutable defaults to None and initialize inside the function.
Loops & Iteration
Never using comprehensions or only list comprehensions
Dictionary, set, and generator comprehensions make code shorter and clearer. Learn when to use each type of comprehension appropriately.
Always turning loops into comprehensions
Not every loop should become a comprehension. Sometimes explicit loops are more readable. Use comprehensions when they genuinely improve clarity.
Using range(len()) to loop over containers
Looping over indices when you only need elements is verbose. Loop directly over the container instead. Use enumerate() if you need both index and element.
Looping over .keys() of a dictionary
Iterating over a dictionary already loops over keys by default. Explicitly calling .keys() is redundant and shows inexperience.
Not using dictionary .items() method
When you need both keys and values, loop over .items() to get key-value pairs directly instead of looping keys and fetching values separately.
Not using tuple unpacking
Tuple unpacking lets you assign multiple tuple elements to separate variables in one operation, making code more concise and readable.
Creating manual index counter variables
If you're incrementing a counter in a loop, use enumerate() instead. It's cleaner and more Pythonic than managing your own counter variable.
Using range(len()) to sync multiple sequences
When you need corresponding elements from multiple objects, use zip() instead of range(len()). Use enumerate(zip()) if you also need the index.
Using if bool() or if len() checks
These are equivalent to just if x. Python objects have truthiness; empty containers are falsy, non-empty are truthy. Just use if x directly.
Performance & Debugging
Using time.time() to measure code execution
time.time() tells you the current time, not elapsed time. Use time.perf_counter() for accurate timing. Subtract two perf_counter() calls to measure duration.
Littering code with print statements instead of logging
Use the logging module instead of print for debugging. Set up logging in main with custom format, adjust logging level, and filter messages you don't need.
Using shell=True in subprocess calls
shell=True creates security vulnerabilities. Avoid it even though it seems easier than formatting arguments as a list.
Code Organization & Dependencies
Doing data analysis in pure Python instead of NumPy/Pandas
Use NumPy for array operations and Pandas for general data analysis. These libraries are far more efficient than pure Python for numerical work.
Using import * outside interactive sessions
import * clutters your namespace with many unwanted variables. Import only what you actually need.
Depending on flat directory structure for imports
Assuming all source files are in one directory and relying on Python's sys.path breaks with multiple scripts in different directories. Learn to package your code properly.
Python Fundamentals & Style
Misconception that Python is not compiled
Python is compiled to bytecode (stored in .pyc files or __pycache__), which is then interpreted. It's both compiled and interpreted, just not to machine code.
Not following PEP 8 style guide
PEP 8 doesn't affect runtime behavior, but following it is expected by co-workers and contributors. It's a professional standard in Python.
Using Python 2 or Python 2 patterns
Python 2 reached end-of-life years ago. All new projects should use Python 3. Many Python 2 behaviors changed: ranges are lazy, dict.keys() returns a view, etc.
Notable quotes
Readability is really in the eye of the beholder. — mCoding
You don't need to turn every single loop into a comprehension. — mCoding
All new projects moving forward should be using Python 3. — mCoding
Action items
- Replace all string concatenation with + to f-strings in your code
- Convert all manual file operations to use with statements
- Replace bare except clauses with except Exception or specific exceptions
- Audit your functions for mutable default arguments and refactor to use None
- Review loops using range(len()) and replace with direct iteration or enumerate()
- Replace print debugging with proper logging module setup
- Check for import * statements and replace with explicit imports
- Ensure your project is properly packaged and installable
- Replace time.time() timing calls with time.perf_counter()
- Review type checking code and replace == comparisons with isinstance()