There’s never an excess of useful tricks in Python. The more of them you learn, the more likely you’ll be able to quickly tackle a challenge in practice. Or show your best side in a technical interview. In this article, I’ll talk about tricks that can help beginners (and beyond) develop the skill. I’ve collected a cartload of tricks and am still finding new ones – and I’m portioning them out to share with you.
Checking for the presence of an element in a set
We can check if an element is in a list using the in operator:
large_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
if 5 in large_list:
print("5 found in list.")
else:
print("5 not found in the list.")
The same applies for sets (set):
large_set = set(large_list)
if 5 in large_set:
print("5 found in set.")
else:
print("5 not found in set.")
Automatically adding a key to a dictionary
If you create a dictionary using collections.defaultdict, add at least one element and then refer to a non-existent key:
>>> from collections import defaultdict
>>>
>>> my_dict = defaultdict(int)
>>>
>>> my_dict['apple'] = 3
>>> my_dict ['banana' ] = 2
>>> print(my_dict['orange'])
>>> print(my_dict)
then the interpreter will add the new element on its own:
... 0
... defaultdict(<class 'int'>, {'apple': 3, 'banana': 2, 'orange': 0})
try / except + else + finally
Masthead for beginners is a pair of try / except, an exception handler. In my opinion, a great addition to such a block is else: it will describe the behavior of the program in case of other errors, which can be many!
Moreover, by adding finally, you will perform terminating operations, for example, you will command to send a notification:
>>> try:
>>> result = 10 / 2
>>> except ZeroDivisionError:
>>> print("Error: division by zero is not possible.")
>>> else:
>>> print("Result:", result)
>>> finally:
>>> print("Program executed.")
... Result: 5.0
... Program executed.
Checking the existence of a variable using the “walrus” operator
If we have declared two variables but haven’t set a value to any of them, we can use the :=
operator (Walrus Operator) to save the program from crashing and add processing logic. In the first case, we haven’t written anything into the variables name1, name2, so the program will go into the else block:
>>> if __name__ == '__main__':
>>> name1, name2 = '', ''
>>>
>>> if name := name1 or name2:
>>> print(name1)
>>> print('Successful!')
>>> else:
>>> print('Name not found...')..... Name not found...
In the second case, only one of the variables is filled, but the program will work without an error:
>>> if __name__ == '__main__':
>>> name1, name2 = 'Sergey', ''
>>>
>>> if name := name1 or name2:
>>> print(name1)
>>> print('Successful!')
>>> else:
>>> print('Name not found...')
... Sergei
... Successful!
The match operator for type checking
Now you will definitely speed up learning new libraries.
Sometimes it is difficult to understand what type of data a function or method returns. In such cases, match and built-in functions of conversion to one or another data type help:
>>> var = 1
>>>
>>> match var:
>>> case str():
>>> print('String type').
>>> case bool():
>>> print('Boolean type')
>>> case float():
>>> print('Floating point number')
>>> case int():
>>> print('Integer type')
>>> case list():
>>> print('List')
>>> case None:
>>> print('None')
>>> case _:
>>> print('Other data type').... Integer type
Built-in functions will try to cast a variable to its value. But if we put one in var, as in the example above, we will bypass float()
.
To prevent the program from crashing, it’s worth putting further var processing only in suitable inheritance functions of cases.
Enabling generators, sets
In the previous article with chips, I told you that there is an efficient way to generate lists – List Comprehension:
>>> names = [
>>>> 'Danil',
>>> 'Mikhail',
>>> 'Olya']
>>> [x for x in names if "a" in x]] # Will select names with 'a' in them... ['Danil', 'Mikhail']]
It turns out that other compound types – generators and sets – also lend themselves to this.
>>> s = {s*2 for s in range(10)} # Multiply numbers 1-10 by two
>>> print(s)
... {0, 2, 4, 6, 8, 10, 12, 14, 16, 18}
Pretty Print dictionary
Outputting multi-level dictionaries with print()
is a pain: line breaks will disappear, file will become unreadable:
>>> my_d: dict = {
>>> "data": {
>>> "TICKET_ID": "64278401",
>>> "livechatStatus": {
>>> "enabled": False
>>> },
>>> "requestHeaders": {
>>> "x-forwarded-host": [
>>> "bot.jaicp.com:443"
>>> ],
>>> "x-forwarded-server": [
>>> "bot.jaicp.com"
>>> ],
>>> "x-forwarded-for": [
>>> "80.93.184.190"
>>> ],
>>> "host": [
>>> "zfl-chatadapter-direct-upstream".
>>> ],
>>> "content-length": [
>>> "978"
>>> ],
>>> "accept": [
>>> "*/*"
>>> ],
>>> "content-type": [
>>> "application/json"
>>> ]
>>> }
>>> }
>>> }
>>> print(my_d)
... {'data': {'TICKET_ID': '64278401', 'livechatStatus': {'enabled': False}, 'requestHeaders': {'x-forwarded-host': ['bot.jaicp.com:443'], 'x-forwarded-server': ['bot.jaicp.jaicp.com'], 'x-forwarded-for': ['80.93.184.190'], 'host': ['zfl-chatadapter-direct-upstream'], 'content-length': ['978'], 'accept': ['*/*'], 'content-type': ['application/json']}}}
But the built-in pprint utility, will output a “combed” dictionary on the command line:
>>> from pprint import pprint
>>> pprint(my_d)
... {'data': {'TICKET_ID': '64278401',
... {'livechatStatus': {'enabled': False},
... 'requestHeaders': {'accept': ['*/*'],
... 'content-length': ['978'],
... 'content-type': ['application/json'],
... 'host': ['zfl-chatadapter-direct-upstream'],
... 'x-forwarded-for': ['80.93.184.190'],
... 'x-forwarded-host': ['bot.jaicp.com:443'],
... 'x-forwarded-server': ['bot.jaicp.com']}}}
TypeVarTuple
Suppose we create a convert_first_int()
function that returns a tuple almost unchanged, only the first element results in an integer type:
>>>> from typing import TypeVarTuple
>>>
>>> Ts = TypeVarTuple("Ts")
>>>
>>> def convert_first_int(values: tuple[int|str|float, *Ts]) -> tuple[int, *Ts]:
>>> return (int(values[0]), *values[1:])
>>>
>>> print(repr(convert_first_int((("1", "2", "3"))))
... (1, '2', '3')
TypeVarTuple
represents an arbitrary tuple of potentially different types. It is useful if the function only deals with the first element of the tuple, and we will be “allowed” any remaining types.
Import
In the latest version of Python 3.12, it is finally possible to import modules in English word order!
import LinearRegression from sklearn.linear_model
Protection against SQL injection
Security experts will tell you that: it is possible to inject a SQL query inside a text (for example, in a questionnaire field) and even drop a database. But God forbid you and me to see such a thing on the proda.
Starting with Python 3.11, we can use LiteralString to avoid such vulnerabilities:
def caller(
arbitrary_string: str,
query_string: LiteralString,
table_name: LiteralString,
) -> None:
run_query("SELECT * FROM students") # ok
run_query(query_string) # ok
run_query("SELECT * FROM " + table_name) # ok
run_query(arbitrary_string) # type checker error
run_query( # type checker error
f "SELECT * FROM students WHERE name = {arbitrary_string}"
)
As you can see from the first half of the snippet, arbitrary_string
is a familiar string variable. But query_string
and table_name
are already “literal strings”, without possible F-string substitutions.
Conclusion
Some techniques may seem incomprehensible in the highlites of the documentation of the language, and become clear only after running the example. So don’t get angry with yourself if such code is forgotten. Just save the article to your bookmarks so that you can return to the described tricks one day.
Collected another regular cart of tricks in several versions of Python.
In case you have found a mistake in the text, please send a message to the author by selecting the mistake and pressing Ctrl-Enter.
#Python #tricks #beginners