Python's "I can't reach the itch ..." feature

Here are some Python techniques, tricks, and tricks that are useful for everyday programming.

We have confirmed the operation with Python 3.8.0.

Comparison of 3 or more values

1 == 2 == 3  # -> False
1 < 2 < 3 < 4  # -> True

Time (date time / date) comparison

Flexible comparisons are possible thanks to the magic method.

Reference: [Python] Allow comparison operations with your own class

from datetime import date

feb1 = date(2020, 2, 1)
feb2 = date(2020, 2, 2)
feb3 = date(2020, 2, 3)

feb1 < feb2 <= feb2 < feb3  # -> True

Maximum / minimum of time

from datetime import date

#I intentionally reverse the order
dates = [
    date(2020, 2, 3),
    date(2020, 2, 2),
    date(2020, 2, 1),
]

min(dates)  # -> datetime.date(2020, 2, 1)
max(dates)  # -> datetime.date(2020, 2, 3)

Time calculation

# Input
from datetime import datetime

start = datetime(2020, 2, 1, 10)
goal = datetime(2020, 2, 3, 12)

t = goal - start
print(f'Your record is{t.days}With the day{t.seconds}Seconds')

# Output
'Your record is 2 days and 7200 seconds'

Make time the key to an associative array

This is useful when performing date and time-based aggregation.

# Input
from datetime import date

counts = {
    date(2020, 2, 1): 0,
    date(2020, 3, 1): 0,
}
counts[date(2020, 2, 1)] += 1
counts[date(2020, 2, 1)] += 1
counts[date(2020, 3, 1)] += 1

print(counts)

# Output
{datetime.date(2020, 2, 1): 2, datetime.date(2020, 3, 1): 1}

Is the key included in the associative array?

d = {
    'foo': 1,
    'bar': 2,
    'baz': 3,
}

print('foo' in d)  # -> True

Extract keys from associative array

d = {
    'foo': 1,
    'bar': 2,
    'baz': 3,
}
print(list(d))  # -> ['foo', 'bar', 'baz']

Extract values from associative array

d = {
    'foo': 1,
    'bar': 2,
    'baz': 3,
}
print(list(d.values()))  # -> [1, 2, 3]

Extract key / value pairs from an associative array

d = {
    'foo': 1,
    'bar': 2,
    'baz': 3,
}

for key, value in d.items():
    print(key, value)

# Output
foo 1
bar 2
baz 3

Convert a double array to an associative array

# Input
l = [
    ['Yamada', 'baseball'],
    ['Tanaka', 'soccer'],
    ['Sato', 'tennis'],
]
dict(l)

# Output
{'Yamada': 'baseball', 'Tanaka': 'soccer', 'Sato': 'tennis'}

Multiple array loop

# Input
rows = [
    ['yamada', 20],
    ['tanala', 18],
    ['sato', 18],
]

for name, age in rows:
    print(f'{name}Is{age}I'm old')
else:
    print('End of introduction')

# Output
'yamada is 20 years old'
'tanala is 18 years old'
'sato is 18 years old'
'End of introduction'

Loop while extracting only the necessary elements from the multiple array

# Input
l = [
    ['Yamada', 'Taro', 20, 'baseball'],
    ['Tanaka', 'Jiro', 18, 'circle'],
]

#Extract the beginning
for last_name, *others in l:
    print(last_name, others)
print()

#Extract the end
for *others, circle in l:
    print(circle, others)
print()
    
#Extract the first two elements
# (If you don't need other elements, it's Python style to specify a double underscore.)
for last_name, first_name, *__ in l:
    print(last_name, first_name)

# Output
Yamada ['Taro', 20, 'baseball']
Tanaka ['Jiro', 18, 'circle']

baseball ['Yamada', 'Taro', 20]
circle ['Tanaka', 'Jiro', 18]

Yamada Taro
Tanaka Jiro

You can also do something like this.

# Input
l = [
    ['a', 'b', 'c', ['d', 'e', 'f']],
]

for one, *__, (*__, two) in l:
    print(one, two)

# Output
a f

Loop with counter

# Input
rows = [
    ['Yamada', 20],
    ['Tanaka', 18],
    ['Sato', 16],
]

for i, (name, age) in enumerate(rows, start=1):
    print(f'{i}Line:Full name={name},age={age}')

# Output
'The first line:Full name=Yamada,age=20'
'2nd line:Full name=Tanaka,age=18'
'3rd line:Full name=Sato,age=16'

Prevent keyless errors in associative arrays

You don't have to write ʻif'key' in dict` every time.

d = {
    'Yamada': 20,
    'Tanaka': 18
}
d.get('Yamada')  # -> 20
d.get('Sato')  # -> None
d.get('Sato', 'No age')  # -> No age

Expand array / associative array and pass it to function

# Input
def func(a, b, c=None, d=None):
    print(a)
    print(b)
    print(c)
    print(d)


l = ['aaa', 'bbb']
d = {'c': 'ccc', 'd': 'ddd'}

func(*l, **d)

# Output
aaa
bbb
ccc
ddd

all function

If all the elements of the array can be evaluated as True, it will return True.

l = [
    True,
    1,
    "foo",
]
all(l)  # -> True

l = [
    True,
    1,
    "",
]
all(l)  # -> False

any function

If any one of the elements of the array can be evaluated as True, it will return True.

l = [
    False,
    0,
    "foo",
]
any(l)  # -> True

l = [
    False,
    0,
    "",
]
any(l)  # -> False

Notes on any function

In the example below, the computationally expensive process (heavy_task) is wasted three times.

# Input
def heavy_task(ret):
    print(ret)
    return ret


if any([heavy_task(True), heavy_task(False), heavy_task(False)]):
    print('Complete')

# Output
True
False
False
Complete

In this case it is more efficient to use if.

# Input
def heavy_task(ret):
    print(ret)
    return ret


if heavy_task(True) or heavy_task(False) or heavy_task(False):
    print('Complete')

# Output
True
Complete

Set operation (Set type)

Remove duplicate elements

# Input
l = ['yamada', 'tanaka', 'yamada', 'sato']
print(set(l))

# Output
{'sato', 'tanaka', 'yamada'}

Extract duplicate elements (intersection)

# Input
l1 = ['yamada', 'tanaka', 'yamada', 'sato']
l2 = ['tanaka']
print(set(l1) & set(l2))

# Output
{'tanaka'}

Combine and remove duplicates (union)

# Input
l1 = ['yamada', 'tanaka']
l2 = ['yamada', 'sato']
print(set(l1) | set(l2))

# Output
{'sato', 'tanaka', 'yamada'}

Delete the elements contained in it from the other (difference set)

# Input
l1 = ['yamada', 'tanaka']
l2 = ['tanaka']
print(set(l1) - set(l2))

# Output
{'yamada'}

collections / itertools package

The collections and ʻitertools` packages are useful when you say" This loop processing can be done if you write it honestly, but it is troublesome ... ".

collections.Counter It counts the number of occurrences of elements and sorts them in descending order.

# Input
import collections

l = ['a', 'b', 'c', 'a', 'a', 'c']
c = collections.Counter(l)
print(c.most_common())

# Output
[('a', 3), ('c', 2), ('b', 1)]

collections.defaultdict You can execute processing on an associative array with or without a key.

I don't know what you're talking about, so check out the code example.

# Input
import json
import collections

# defaultdict()Function in the argument of(callable)You can give anything
groups = collections.defaultdict(list)

#If it is an ordinary associative array"There is no key called baseball"Error occurs
groups['baseball'].append('yamada')
groups['tennis'].append('tanaka')
groups['baseball'].append('sato')

print(json.dumps(groups))

# Output
{"baseball": ["yamada", "sato"], "tennis": ["tanaka"]}

The defaultdict is especially useful when creating some sort of aggregation process. The process is simple because you don't have to check the existence of each key.

# Input
import collections

products = ['Note', 'Pencil', 'eraser']

sales = [
    ['Note', 1],
    ['eraser', 2],
    ['eraser', 1],
]

aggregates = collections.defaultdict(int)

for product, cnt in sales:
    aggregates[product] += cnt

print('---Today's sales performance---')

for product in products:
    print('%s : %d pieces' % (product, aggregates[product]))

# Output
---Today's sales performance---
Note:1 piece
Pencil:0 pieces
eraser:Three

Multiple associative arrays can also be implemented simply by taking advantage of the property that any function (callable) can be passed.

# Input
import json
from collections import defaultdict

nested = defaultdict(lambda: defaultdict(int))

nested['a']['a'] += 1
nested['a']['a'] += 1
nested['a']['b'] += 1
nested['b']['c'] += 1

print(json.dumps(nested))

# Output
{"a": {"a": 2, "b": 1}, "b": {"c": 1}}

itertools.product It comprehensively generates "combinations" from two or more arrays.

# Input
import itertools

a = ['a1', 'a2']
b = ['b1', 'b2', 'b3']
c = ['c1']

list(itertools.product(a, b, c))

# Output
[('a1', 'b1', 'c1'),
 ('a1', 'b2', 'c1'),
 ('a1', 'b3', 'c1'),
 ('a2', 'b1', 'c1'),
 ('a2', 'b2', 'c1'),
 ('a2', 'b3', 'c1')]

itertools.chain.from_iterable It transforms a 2D array into a 1D array (flatten).

# Input
import itertools

l = [
    ['a1', 'a2'],
    ['b1', 'b2', 'b3'],
]

list(itertools.chain.from_iterable(l))

# Output
['a1', 'a2', 'b1', 'b2', 'b3']

io package

It provides a virtual object that can be treated like a file.

Useful when writing tests.

# Input
import io


def writer(f, text):
    f.write(text)


def printer(f):
    print(f.read())


sio = io.StringIO()

writer(sio, 'foo\n')
writer(sio, 'bar\n')
writer(sio, 'baz\n')

sio.seek(0)
printer(sio)

# Output
foo
bar
baz

Tuple

Tuples are similar to arrays, but they are immutable and cannot be reassigned.

Because it is immutable, it can be used as an associative array key.

yamada = ('Yamada', 'Taro')  #This is a tuple
tanaka = ('Tanaka', 'Jiro')  #This is also a tuple

print(yamada[0])  # -> Yamada

yamada[0] = 'Tanaka'  # TypeError: 'tuple' object does not support item assignment

ages = {
    yamada: 20,
    tanaka: 18,
}

print(ages[tanaka])  # -> 18

This characteristic can be used to efficiently group similar data.

# Input
from collections import defaultdict

data = [
    {'circle': 'baseball', 'name': 'yamada', 'age': 10},
    {'circle': 'baseball', 'name': 'sato', 'age': 10},
    {'circle': 'baseball', 'name': 'suzuki', 'age': 11},
    {'circle': 'tennis', 'name': 'tanaka', 'age': 10},
]

per_circle_age = defaultdict(list)

for v in data:
    k = (v['circle'], v['age'])  # (Circle name,age)Generate a tuple called
    per_circle_age[k].append(v['name'])  #Aggregate using tuples as a key

for (circle, age), members in per_circle_age.items():
    print(f'{circle}Belonging to{age}Year-old member:{members}')

# Output
"10-year-old member of baseball:['yamada', 'sato']"
"11-year-old member of baseball:['suzuki']"
"10-year-old member of tennis:['tanaka']"

dataclasses package

This is a function that can be used from Python 3.7.

It is convenient because you do not have to write assignment processing such as self.name = name in the constructor.

import dataclasses


# frozen=True is invariant(immutable)Can be treated as an object
@dataclasses.dataclass(frozen=True)
class User:
    last_name: str
    first_name: str
        
    def full_name(self):
        return f'{self.last_name} {self.first_name}'
        
        
yamada = User(last_name='Yamada', first_name='Taro')
tanaka = User(last_name='Tanaka', first_name='Jiro')

yamada.full_name()  # -> Yamada Taro

#Can be easily converted to an associative array
dataclasses.asdict(yamada)  # -> {'last_name': 'Yamada', 'first_name': 'Taro'}

#Comparison is possible
yamada2 = User(last_name='Yamada', first_name='Taro')
yamada == yamada2  # -> True
yamada == tanaka  # -> False
yamada in [yamada2]  # -> True

# "frozen=True"If you do, you cannot reassign the value.
yamada.last_name = 'Sato'  # -> FrozenInstanceError: cannot assign to field 'last_name'

#Since it is immutable, it can be used as an associative array key.
d = {yamada: 'foo', tanaka: 'bar'}

#Set arithmetic(Set type)Is also possible
{yamada, tanaka} & {yamada2}  # -> {User(last_name='Yamada', first_name='Taro')}

Recommended Posts

Python's "I can't reach the itch ..." feature
I can't get the element in Selenium!
I can't install the package with pip.
I can't enter characters in the text area! ?? !! ?? !! !! ??
I can't click the Selenium checkbox Python VBA
I tried the Pepper NAOqi OS 2.5.5 SLAM feature
[Note] I can't call the installed module in jupyter
I can't use the darknet command in Google Colaboratory!
I can't install Anaconda!
I counted the grains
I can't log in to the admin page with Django3