Python Tips
Posted on Wednesday, Jul 01, 2020 in programming
A collection of small Python scripts and tips.
This post is based on a Twitter thread I started in April 2020 and works as a centralized way to read all the tips in an easier format than Twitter's 280 characters.
Both will be updated frequently.
List Flatten without explicit loops
- Using Itertools' chain
1 2 3 4 5 6 | import itertools
test = [[-1, -2], [30, 40], [25, 35]]
list(itertools.chain.from_iterable(test))
>> [-1, -2, 30, 40, 25, 35]
|
- Using map and strings (Suggested by @Romestantc )
1 2 3 4 | test = [[-1, -2], [30, 40], [25, 35]]
map(int, ''.join(c for c in test.__str__() if c not in '[]').split(',') )
>> [-1, -2, 30, 40, 25, 35]
|
Not pretty in my opinion ;)
Count individual items of any iterable
1 2 3 4 5 6 7 8 | from collections import Counter
count = Counter(['a', 'b', 'c', 'a', 'a', 'b', 'd'])
print(count)
>> Counter({'a': 3, 'b': 2, 'c': 1, 'd': 1})
count['a']
>> 3
|
Repeat a series of values from any iterable
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import itertools as it
data = it.cycle([1, 2])
for i in range(10):
print(next(data))
>> 1
2
1
2
1
2
1
2
1
2
|
Name slices to reuse them
1 2 3 4 5 6 7 8 | # slice(start, end, step)
STEPTWO = slice(None, None, 2)
integer_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
integer_list[STEPTWO]
>> [0, 2, 4, 6, 8]
|
This is the same as:
1 | integer_list[::2]
|
Reverse any "indexable" collection that supports slices
1 2 3 4 5 6 | # slice(None, None, -1) or [::-1]
integer_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
integer_list[::-1]
>> [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
|
Use array to keep homogeneous type of objects in your lists
1 2 3 4 5 6 | from array import array
integer_list = array('i', [1, 2, 3])
integer_list = array('i', [1, 2, "a"])
>> TypeError: an integer is required (got type str)
|
The constructor of array includes a type code parameter and an optional initial list.
Swap dictionary key-values using zip
Using zip:
1 2 3 4 5 6 7 8 9 | data = { 'a': 1, 'b': 2, 'c': 3, 'd': 4 }
data.items()
>> dict_items([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
z = zip(data.values(), data.keys())
dict(z)
>> { 1: 'a', 2: 'b', 3: 'c', 4: 'd' }
|
Using dictionary comprehensions:
1 2 3 4 | data = { 'a': 1, 'b': 2, 'c': 3, 'd': 4 }
{ v:k for k, v in data.items() }
>> { 1: 'a', 2: 'b', 3: 'c', 4: 'd' }
|
If there are repeated values, the result will be overwritten, so be careful.
Namedtuples as lightweight, inmutable, record-like objects
1 2 3 4 5 6 7 8 9 10 | from collections import namedtuple
Person = namedtuple("Person", "name age gender")
bob = Person("Bob", 30, "male")
bob.age
>> 30
bob[1]
>> 30
|
Something similar can be achieved using Dataclasses (for Python 3.8+)
Built-ins set and frozenset for unordered collections of hashable objects
1 2 3 4 5 6 7 8 9 10 11 | A = set([1, 2, 3])
B = set([3, 4, 5])
A.union(B)
>> {1, 2, 3, 4, 5}
A.intersection(B)
>> {3}
A.symmetric_difference(B)
>> {1, 2, 4, 5}
|
frozenset has the same interface but returns an inmutable set object.
Itertools' dropwhile and takewhile to filter from an iterator
1 2 3 4 5 6 7 8 | from itertools import dropwhile
numbers = [-2, -1, 0, 1, 2]
f = lambda x: x < 1
list ( dropwhile (f, numbers) )
>> [1, 2]
|
takewhile takes the items if True. The difference with built-in function filter is that the iteration stops whenever the test-function is false.
1 2 3 4 | numbers = [-2, -1, 0, 1, 2, -3, -4]
list(dropwhile(f, numbers))
>> [1, 2, -3, -4]
|
As @jgomo3 Observes, one way to think about those functions is that they simply divide a sequence in halves.
takewhile gives you the first part. dropwhile the last part.
Traspose a matrix with List Comprehensions
1 2 3 4 5 6 7 8 | M = [[1,2,3],
[4,5,6],
[7,8,9]]
MT = [[row[i] for row in M] for i in range(len(M))]
print(MT)
>> [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
|
Also, NumPy provides methods to for easier matrix manipulation.
Datetime to UTC
To create timezone aware datetimes in Python:
1 2 3 4 5 6 7 | from datetime import datetime, timezone, timedelta
tz = timezone(timedelta(hours=-6)) # UTC-6
local = datetime(2020, 4, 16, 13, 40, 0, 0, tzinfo=tz)
local.isoformat()
>> '2020-04-16T13:40:00-06:00'
|
1 2 3 4 5 6 7 8 9 10 11 | from datetime import datetime, timezone
# assuming `local` is a datetime object
print(local.isoformat() )
>> '2020-04-16T13:40:00-06:00'
local_in_utc = local.astimezone(timezone.utc)
print(local_in_utc.isoformat())
>> '2020-04-16T19:40:00+00:00'
|
You can always use pytz to handle timezones.
1 2 3 4 5 6 7 | from datetime import datetime
from pytz import timezone
dt = datetime(2020, 4, 16, 13, 40, 0, tzinfo=pytz.utc)
print(dt.isoformat())
>> '2020-04-16T13:40:00+00:00'
|
functools.singledispatch to achieve parametric polymorphism in Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | from functools import singledispatch
@singledispatch
def process(num=None):
raise NotImplementedError("Implement process function.")
@process.register(int)
def sub_process(num):
# processing interger
return f"Integer {num} has been processed successfully!"
@process.register(float)
def sub_process(num):
# processing float
return f"Float {num} has been processed successfully!"
# use the function
print(process(12.0))
print(process(1))
|