Things That I Can Do in Python, but Usually Don’t
Semicolon
Yes, we can use semicolons in Python. But not braces.
def foo(x): increment = 2; print(x + increment); foo(10); # 12 foo(5); # 7
>>> from __future__ import braces SyntaxError: not a chance
The role of the semicolon here is the same as in other programming languages: to terminate a statement. While this option can be very useful in some cases, it does not apply to regular code. This is unnecessary.
In the following example, we intend to call a function with an argument. This is a typo, but it is perfectly valid in Python. No errors. No output. These two statements, separated by a semicolon, have no effect.
foo;(10);
>>> foo;(10); <function foo at 0x0000021812E0CB80> 10
What is a (10)
? Just an integer. Parentheses are used in Python to enclose expressions or to create tuples. In this case, this expression does nothing.
Parentheses: if/while (condition)
This is what is used in other languages. Python allows it too. But you don't need to do this.
ext = "txt" if (ext == "txt"): print(ext) # txt
The exception is the forced order of operations.
>>> (True or False) and (True and False) False >>> True or False and True and False True
Using Reserved Names for Variables and Filenames
Python allows you to reuse names, including some reserved ones, like the names of built-ins and PSL modules. Problems arise when you want to use these names for their intended purpose.
>>> list = [1, 2, 3] >>> list [1, 2, 3] >>> list("abcd") Traceback (most recent call last): ... list("abcd") TypeError: 'list' object is not callable >>> import sys >>> sys <module 'sys' (built-in)> >>> sys = "qwerty" >>> sys 'qwerty' >>> sys.path Traceback (most recent call last): ... sys.path AttributeError: 'str' object has no attribute 'path'
A special case is to name your file the same as the PSL module and try to import and use it.
Imagine we want to execute the following code.
import json obj = json.dumps({'a': 2, 'b': 5}, indent=4)
1) If this code is in a file we named json.py
(just like a standard module).
AttributeError: partially initialized module 'json' has no attribute 'dumps' (most likely due to a circular import)
2) If this code is in another file, for example, main.py
. The file named json.py
is in the same folder.
AttributeError: module 'json' has no attribute 'dumps'
The first place Python looks for modules to import, is the directory where the script we are running is located.
Changing Conventional Names
There are certain conventions for using certain names, such as self
, cls
, *args
, *kwargs
. The following example will work, but it is less familiar and readable.
class A: def __init__(inst, value): inst.value = value @classmethod def SomeMethod(cl, *a, **b): print(cl, a, b) a = A(12) print(a.value) # 12 A.SomeMethod() # <class '__main__.A'> () {}
Creating Global Variables in Functions
You need to make sure you create this variable before using it in other parts of your code.
def use_global_variable(): print("Use a new variable:", new_variable) def create_global_variable(): global new_variable # this line is required here new_variable = "value" # use_global_variable() # NameError: name 'new_variable' is not defined # creates a new global variable when the function is called create_global_variable() print(globals()) print("New variable:", new_variable) use_global_variable()
{'__name__': '__main__', ..., 'create_global_variable': <function create_global_variable at 0x0000028D2E50CC20>, 'new_variable': 'value'} New variable: value Use new variable: value
range(len(iterable))
Pattern
This is an anti-pattern if you only need to loop over some iterable. Example:
text = "abcd" for i in range(len(text)): print(text[i])
In Python you can do this more easily: for item in iterable
.
text = "abcd" for char in text: print(char) # if you need index for index, char in enumerate(text): print(index, char)
range-len
can be used if you want to modify/delete list items in-place or loop through multiple lists (see on StackOverflow). Depending on the task, there may be more Pythonic alternatives.
Wildcard Import: from <module> import *
This import brings all names defined in the <module> to the current namespace. If the <module> also has imports, names from other unrelated modules will also be added.
If modules have classes or functions with the same names, this can lead to confusion. A notable example is the tkinter tk and ttk widgets with the same class names.
Let's say we have two files: utils.py
and main.py
.
# utils.py def choice(x): if x in ["Y", "y"]: return True elif x in ["N", "n"]: return False else: return None
# main.py from random import * print(choice) # <bound method Random.choice of <random.Random object at 0x000002D9D449C750>> from utils import * print(choice) # <function choice at 0x000002D9D682CCC0> print(choice([1, 2, 3, 4, 5])) # None print(choice("y")) # True
If we run main.py
, we will see that the name "choice" refers to a function defined in utils.py
. There are no errors here, as utils.choice
accepts almost any argument.
If we switch imports, there will be no errors here either. random.choice
takes sequence as argument. The string "y" is a sequence.
# main.py from utils import * from random import * print(choice) # <bound method Random.choice of <random.Random object at 0x000001A5275BF1E0>> print(choice([1, 2, 3, 4, 5])) # 5 print(choice("y")) # "y"
Your options are to rename utils.choice
, or make sure the functions have different names in the current namespace, or be more explicit when using functions (module.funcname
).
from random import choice as rchoice from utils import choice as uchoice print(rchoice([1, 2, 3, 4, 5])) # 3 print(uchoice("y")) # True import random import utils print(random.choice([1, 2, 3, 4, 5])) # 1 print(utils.choice("n")) # False
Not Using Context Managers
Context managers are used to ensure proper resource management, for example, for working with file objects, thread lock objects, database transactions, etc. You can also write your context manager.
A basic example of working with a file object
f = open("test.txt", mode="r", encoding="utf-8") print(type(f)) # <class '_io.TextIOWrapper'> # try-except-finally block try: data = f.read(500) print(data) # file content # raise Exception("Unknown error") except Exception as e: print(e) finally: f.close() print(f.closed) # True
Example with a context manager
The base class of all io
classes is the context manager, so it supports the with operator.
In this example, the file is closed after the with statement completes - even if an exception occurs. This is similar to the try-finally block above, but if an error occurs, an exception will be thrown.
# try-finally block with open("test.txt", mode="r", encoding="utf-8") as f: data = f.read(500)
- PEP 8: Naming Conventions
- Python Docs: import best practices
- Python Docs: range(len(mylist)) example
- Python Docs: Context Manager Types
- PEP 343 – The "with" Statement (and examples)
- Python Docs: contextlib - Utilities for with-statement contexts (and examples)