Photo by Alex Chumak on Unsplash
Python is the third most commonly-used programming language. It has introduced many new features, simplifying and enhancing the development process. Yet, many developers remain unaware of the full potential that Python 3 has to offer.
This article will uncover ten lesser-known and underutilized capabilities in Python (v3.5 to v3.11). These hidden gems can improve your coding efficiency and productivity. So, without further ado, let’s dive into these fascinating Python 3 features.
1. Walrus Operator
It’s a new syntax introduced in Python 3.11 that assigns values to variables as part of a larger expression.
Old way
a = "This is a sample string text"if len(a) > 10:print("Length of string a = ", len(a))a = "This is a sample string text" if len(a) > 10: print("Length of string a = ", len(a))a = "This is a sample string text" if len(a) > 10: print("Length of string a = ", len(a))
Enter fullscreen mode Exit fullscreen mode
New way
a = "This is a sample string text"if (len_a := len(a)) > 10: # Note the walrus operator will compute value of len(a) and assign it to variable len_aprint("Length of string a = ", len_a)a = "This is a sample string text" if (len_a := len(a)) > 10: # Note the walrus operator will compute value of len(a) and assign it to variable len_a print("Length of string a = ", len_a)a = "This is a sample string text" if (len_a := len(a)) > 10: # Note the walrus operator will compute value of len(a) and assign it to variable len_a print("Length of string a = ", len_a)
Enter fullscreen mode Exit fullscreen mode
2. Structural Pattern Matching
You might remember reading that Python doesn’t support switch statements because if-else can achieve the same result. You are in for a treat. Python3.10 introduced something similar.
Old way
def http_error(status):if status == 400:return "Bad request"elif status == 404:return "Not found"elif status == 418:return "I'm a teapot"else:return "Something's wrong with the internet"def http_error(status): if status == 400: return "Bad request" elif status == 404: return "Not found" elif status == 418: return "I'm a teapot" else: return "Something's wrong with the internet"def http_error(status): if status == 400: return "Bad request" elif status == 404: return "Not found" elif status == 418: return "I'm a teapot" else: return "Something's wrong with the internet"
Enter fullscreen mode Exit fullscreen mode
New way
def http_error(status):match status:case 400:return "Bad request"case 404:return "Not found"case 418:return "I'm a teapot"case _: # This is a wildcard operatorreturn "Something's wrong with the internet"def http_error(status): match status: case 400: return "Bad request" case 404: return "Not found" case 418: return "I'm a teapot" case _: # This is a wildcard operator return "Something's wrong with the internet"def http_error(status): match status: case 400: return "Bad request" case 404: return "Not found" case 418: return "I'm a teapot" case _: # This is a wildcard operator return "Something's wrong with the internet"
Enter fullscreen mode Exit fullscreen mode
3. Merging Dictionaries
Python3.9 introduced a new way of merging or updating dictionaries. It complements the existing dict.update and {**d1, **d2} methods of merging dictionaries.
Old way
x = {"key1": "value1 from x", "key2": "value2 from x"}y = {"key2": "value2 from y", "key3": "value3 from y"}print({ **x,** y})print({ **y,** x}) # Note here keys of y would be given preference over keys of xx = {"key1": "value1 from x", "key2": "value2 from x"} y = {"key2": "value2 from y", "key3": "value3 from y"} print({ **x,** y}) print({ **y,** x}) # Note here keys of y would be given preference over keys of xx = {"key1": "value1 from x", "key2": "value2 from x"} y = {"key2": "value2 from y", "key3": "value3 from y"} print({ **x,** y}) print({ **y,** x}) # Note here keys of y would be given preference over keys of x
Enter fullscreen mode Exit fullscreen mode
New way
x = {"key1": "value1 from x", "key2": "value2 from x"}y = {"key2": "value2 from y", "key3": "value3 from y"}# Note the usage of OR (|) operatorprint(x | y)prin(y | x) # Note here keys of y would be given preference over keys of xx = {"key1": "value1 from x", "key2": "value2 from x"} y = {"key2": "value2 from y", "key3": "value3 from y"} # Note the usage of OR (|) operator print(x | y) prin(y | x) # Note here keys of y would be given preference over keys of xx = {"key1": "value1 from x", "key2": "value2 from x"} y = {"key2": "value2 from y", "key3": "value3 from y"} # Note the usage of OR (|) operator print(x | y) prin(y | x) # Note here keys of y would be given preference over keys of x
Enter fullscreen mode Exit fullscreen mode
4. Removing Prefix or Suffix
Python3.9 introduced dedicated methods to get rid of prefixes or suffixes in strings.
Old way
x = "prefixstring"y = "stringsuffix"print(x.split("prefix")[-1])print(y.split("suffix")[0])x = "prefixstring" y = "stringsuffix" print(x.split("prefix")[-1]) print(y.split("suffix")[0])x = "prefixstring" y = "stringsuffix" print(x.split("prefix")[-1]) print(y.split("suffix")[0])
Enter fullscreen mode Exit fullscreen mode
New way
x = "prefixstring"y = "stringsuffix"print(x.removeprefix("prefix"))print(y.removesuffix("suffix"))x = "prefixstring" y = "stringsuffix" print(x.removeprefix("prefix")) print(y.removesuffix("suffix"))x = "prefixstring" y = "stringsuffix" print(x.removeprefix("prefix")) print(y.removesuffix("suffix"))
Enter fullscreen mode Exit fullscreen mode
5. Positional Only Parameters
Python provides to define positional and keyword arguments for a function. But you can pass them interchangeably. Python3.8 introduced a way to enforce that you cannot pass positional arguments as keyword arguments.
Old way
def fun(pos_arg1, pos_arg2, key_arg_1 = None):print("positional arguments: ", pos_arg1, pos_arg2)print("Keyword arguments: ", key_arg_1)# It will workfun(1, 2, 3) # Passing keyword argument as positional argument# It will also workfun(pos_arg1=1, pos_arg2=2, key_arg_1=3) # Passing positional argument as keyword argumentdef fun(pos_arg1, pos_arg2, key_arg_1 = None): print("positional arguments: ", pos_arg1, pos_arg2) print("Keyword arguments: ", key_arg_1) # It will work fun(1, 2, 3) # Passing keyword argument as positional argument # It will also work fun(pos_arg1=1, pos_arg2=2, key_arg_1=3) # Passing positional argument as keyword argumentdef fun(pos_arg1, pos_arg2, key_arg_1 = None): print("positional arguments: ", pos_arg1, pos_arg2) print("Keyword arguments: ", key_arg_1) # It will work fun(1, 2, 3) # Passing keyword argument as positional argument # It will also work fun(pos_arg1=1, pos_arg2=2, key_arg_1=3) # Passing positional argument as keyword argument
Enter fullscreen mode Exit fullscreen mode
New way
def fun(pos_arg1, pos_arg2, /, key_arg_1 = None):print("positional arguments: ", pos_arg1, pos_arg2)print("Keyword arguments: ", key_arg_1)# It will workfun(1, 2, 3) # Passing keyword argument as positional argument# It won't workfun(1, pos_arg2=2, key_arg_1=3) # Passing one positional argument as keyword argumentfun(pos_arg1=1, pos_arg2=2, key_arg_1=3) # Passing both positional arguments as keyword argumentdef fun(pos_arg1, pos_arg2, /, key_arg_1 = None): print("positional arguments: ", pos_arg1, pos_arg2) print("Keyword arguments: ", key_arg_1) # It will work fun(1, 2, 3) # Passing keyword argument as positional argument # It won't work fun(1, pos_arg2=2, key_arg_1=3) # Passing one positional argument as keyword argument fun(pos_arg1=1, pos_arg2=2, key_arg_1=3) # Passing both positional arguments as keyword argumentdef fun(pos_arg1, pos_arg2, /, key_arg_1 = None): print("positional arguments: ", pos_arg1, pos_arg2) print("Keyword arguments: ", key_arg_1) # It will work fun(1, 2, 3) # Passing keyword argument as positional argument # It won't work fun(1, pos_arg2=2, key_arg_1=3) # Passing one positional argument as keyword argument fun(pos_arg1=1, pos_arg2=2, key_arg_1=3) # Passing both positional arguments as keyword argument
Enter fullscreen mode Exit fullscreen mode
6. Time with nanoseconds Precision
Did you ever need nanosecond precision in getting time to compare code performances? Python3.7 introduced new methods in the time library.
Old way
import timestart = time.time()end = time.time()print(end - start)# Provides sub-second precision, though that precision varies by platform# >> 2.7179718017578125e-05 Notice the precision is in e-05import time start = time.time() end = time.time() print(end - start) # Provides sub-second precision, though that precision varies by platform # >> 2.7179718017578125e-05 Notice the precision is in e-05import time start = time.time() end = time.time() print(end - start) # Provides sub-second precision, though that precision varies by platform # >> 2.7179718017578125e-05 Notice the precision is in e-05
Enter fullscreen mode Exit fullscreen mode
New way
import timestart = time.time_ns()end = time.time_ns()print(end - start)# Provides nanosecond precision# >> 47000 Notice the figure is in nanosecondsimport time start = time.time_ns() end = time.time_ns() print(end - start) # Provides nanosecond precision # >> 47000 Notice the figure is in nanosecondsimport time start = time.time_ns() end = time.time_ns() print(end - start) # Provides nanosecond precision # >> 47000 Notice the figure is in nanoseconds
Enter fullscreen mode Exit fullscreen mode
7. f-strings
Python3.6 introduced f-strings to provide an easy way of formatting strings.
Old way
a = 1print("Value of a = {}".format(a))a = 1 print("Value of a = {}".format(a))a = 1 print("Value of a = {}".format(a))
Enter fullscreen mode Exit fullscreen mode
New way
a = 1print(f"Value of a = {a}")a = 1 print(f"Value of a = {a}")a = 1 print(f"Value of a = {a}")
Enter fullscreen mode Exit fullscreen mode
8. Underscores in Numeric Literals
Python 3.6 introduced allowing adding underscores in numeric literals for better readability.
Old way
a = 1000000000000000 # try counting the number of 0'sprint(type(a))# >> <class 'int'>a = 1000000000000000 # try counting the number of 0's print(type(a)) # >> <class 'int'>a = 1000000000000000 # try counting the number of 0's print(type(a)) # >> <class 'int'>
Enter fullscreen mode Exit fullscreen mode
New way
a = 1_000_000_000_000_000print(type(a))# >> <class 'int'>a = 1_000_000_000_000_000 print(type(a)) # >> <class 'int'>a = 1_000_000_000_000_000 print(type(a)) # >> <class 'int'>
Enter fullscreen mode Exit fullscreen mode
9. Matrix Multiplication Operator
Python3.5 introduced a dedicated @ infix operator for matrix multiplication.
Old way
import numpyx = numpy.ones(3)m = numpy.eye(3)print(x.dot(m))import numpy x = numpy.ones(3) m = numpy.eye(3) print(x.dot(m))import numpy x = numpy.ones(3) m = numpy.eye(3) print(x.dot(m))
Enter fullscreen mode Exit fullscreen mode
New way
import numpy # NumPy 1.10 has support for the new operator:x = numpy.ones(3)m = numpy.eye(3)print(x @ m) # @ is the new matrix matrix-multiplication operatorimport numpy # NumPy 1.10 has support for the new operator: x = numpy.ones(3) m = numpy.eye(3) print(x @ m) # @ is the new matrix matrix-multiplication operatorimport numpy # NumPy 1.10 has support for the new operator: x = numpy.ones(3) m = numpy.eye(3) print(x @ m) # @ is the new matrix matrix-multiplication operator
Enter fullscreen mode Exit fullscreen mode
10. Approximate Equality
Python3.5 introduced dedicated methods to tell whether two values are approximately equal or “close” to each other.
Old way
a = 5.0b = 4.99998print(abs(a - b) <= 1e-5)a = 5.0 b = 4.99998 print(abs(a - b) <= 1e-5)a = 5.0 b = 4.99998 print(abs(a - b) <= 1e-5)
Enter fullscreen mode Exit fullscreen mode
New way
import matha = 5.0b = 4.99998print(math.isclose(a, b, rel_tol=1e-5))import math a = 5.0 b = 4.99998 print(math.isclose(a, b, rel_tol=1e-5))import math a = 5.0 b = 4.99998 print(math.isclose(a, b, rel_tol=1e-5))
Enter fullscreen mode Exit fullscreen mode
暂无评论内容