yano3nora
7/2/2017 - 1:38 PM

[python: note] Python - modern server side script language. #python

[python: note] Python - modern server side script language. #python

OVERVIEW

python.org
docs.python.org/ja/3

1991 年に登場したプログラミング言語。OSS・クロスプラットフォーム・インタラクティブシェル・科学演算や機械学習ライブラリの充実性などがウリ。Google App Engine でも利用可能な言語として Java などと共に採用されており、Youtube や Dropbox も Python で記述されている。現在 2 / 3 系があるが、2 系のサポート終了が 2020 年のため 3 系を利用するのが一般的。

  • 予約語が少ない
  • インデントでブロックを表す ( インデントを強制 )
    • 公式スタイルガイド PEP 8 では 4 を推奨
  • 構文エラー以外は殆ど例外にしてくれる
  • 珍しい「多重継承」をサポートする
  • オブジェクト指向対応言語 ( 純粋ではない )
  • ライブラリ・パッケージが豊富

いわゆる「 Python っぽい」書き方は Python のリファクタリングでイケてないコードを別に美しいオブジェクト指向設計ではない普通のコードにする方法 を見るとわかりやすい。namedtuple については Built-in API 参照。

Refs

pyenv

pyenv/pyenv - github.com

rbenv みたいな、よくある言語ランタイムのバージョン管理ツール。使い方は他の env ツールと同様。

最近の OS なら python は最初から入っていることが多いが、python は 2 系と 3 系で仕様が大きく違ったり、3 系は python3pip3 コマンドで呼び出したりとややこしいので pyenv で一元管理したほうが楽。

追記: 最近言語ランタイムのバージョン管理は anyenv 経由で pyenv を入れて環境構築する方向に統一した

PACKAGES

PyPI

PyPI – the Python Package Index - pypi.org Python Package Index ( 略称 PyPI ) は、プログラミング言語Pythonの、サードパーティーソフトウェアリポジトリである。すべてのオープンソースなPythonパッケージの包括的なカタログととらえることができる - wiki

PyPI にない GitHub 上のライブラリを依存に含める

pip install #git
GitHub のリポジトリを requirements.txt に含める

Issue にあがって PR が master にマージされてるけどパッケージリリースがまだで待てないよお!みたいなときのやつ。

$ pip install -e git+https://github.com/Hanaasagi/uvicorn.git@fix/ssl-context-pickle#egg=uvicorn

pip

pip - pypi.org
pip - wikipedia.org
Python モジュールのインストール

ruby の gem みたいな標準パッケージ管理ツール。

$ vi requirements.txt
> Django
> uwsgi

$ pip install -r requirements.txt

# Freeze
$ pip freeze > requirements.txt
> Django==2.2.1
> uWSGI==2.0.18

$ pip install -r requirements.txt

pip-tools

jazzband/pip-tools - github.com
pip-toolsでrequirements.txtのパッケージバージョン番号を管理しよう
Python アプリ依存パッケージ管理 ベストプラクティス集

pip だけだと機能不足なので、こっちと合わせて使うと良さそう。

# requirements.in というファイルに入れたいパッケージ書く
# requirements.txt と同じ書式で書けるみたい
$ vi requirements.in

# pip-compile で in の内容から依存パッケージや version など補完した
# requirements.txt 作ってくれる
$ pip-compile requirements.in

# --dry-run があるので requirements.txt が既にあるときは
# 更新時に内容差分をチェックするのもありかも
$ pip-compile --dry-run requirements.in | diff requirements.txt -

# 生成された requirements.txt は普通に pip install でもよいし
# pip-sync で「不足分を install 」「不要になったものを uninstall 」みたいなことも可能
$ pip-sync

venv 仮想環境

venv 仮想環境 - python.jp

  • プロジェクト毎に Python の仮想実行環境を構築できる Python の標準機能 (Python 3.3~)
  • .venv ディレクトリを作成し、中にこのプロジェクト専用の Python ランタイムを置く感じ
  • pyenv と組み合わせれば、ローカルでプロジェクト毎の Python バージョン・依存パッケージを管理可能
    • 「ローカルでエディタの linter 効かす」とか「依存をローカルにインストールして実行する」とか標準機能でシンプルに実現できる
    • Pipenv とか Poetry とかも試したが 正直使いづらい & Docker 連携しづらい ので venv で良くね?って思う
# Python バージョンの仮想化はしてくれないので
# 予め pyenv などでバージョンは揃えておく
#
$ pyenv local 3.7.9
$ exec $SHELL -l

# 仮想環境を初期化
#
# この時点でエディタや IDE が venv に対応してれば
# 仮想環境を activate しなくても Python ランタイムとして
# .venv 内の仮想環境を参照する設定をするか確認してくれる
# (VSCode なら Python Extension 入れれば OK)
#
$ python -m venv .venv

# 大体はテンプレに書いてあるけど一応確認したほうがいい
$ echo '.venv' >> .gitignore
$ echo '.venv' >> .dockerignore

# 仮想環境内のシェルに入る
#
# VSCode の設定ができてれば統合ターミナル起動時に
# activate は自動で実行してくれるみたい
#
$ source .venv/bin/activate          # mac
$ .venv\Scripts\activate.bat  # win

# 仮想環境内にパッケージをインストール
$ pip install --upgrade pip   # 念の為 pip 最新化してから
$ pip install -r requirements.txt

# activate してる仮想環境シェルを終了
$ deactivate

SYNTAX

Encoding

# coding: utf-8
# デフォルトエンコーディングを利用しない場合の設定

print('こんにちは、世界。')

Comment

# pythonは # でコメントアウト

"""
複数行はダブルクォート 3 つで囲う
シングルクォートも可
"""

Docstring

PEP 257 -- Docstring Conventions - www.python.org
NumPyスタイルPython Docstringsの例
GoogleスタイルのPython Docstringsの例
Google Python Style Guide

Python の Docstring は他言語と結構違うので注意。

# Oneline

"""See: http://bit.ly/2XZVpk9 ."""
from asgiref.sync import async_to_sync

def create_list(a, b):
    """Create list from args."""

# Multiline
class MyKlass():
    """Example class for docstring.  # クラスは平叙文で

    Longer class information ...

    Attributes:
        x (float): The X coordinate.
        y (float): The Y coordinate.
    """

    def add_numbers(x, y):
        """Add numbers.  # 関数/メソッドは命令文で

        Args:
            x (type): Description of parameter `x`.
            y: Description of parameter `y` (with type not specified)
        Returns:
            int: Description of anonymous integer return value.
        Raises:
            LinAlgException: If the matrix is not numerically invertible.
        Example:
            >>> MyKlass.add_numbers(1, 2)
            3
        """

None

print(a)  # None

"""
Python では一般的に Null と呼ばれる変数の状態
または未定義の変数について None と表現する
"""

Print / Stdout

print('hoge')                 # hoge
print('hogehoge', end='<BR>') # hogehoge<br>

# ヒアドキュメントのようなやつ
print('''
hoge
  hogehoge
     hogehogehoge
''')

Literal String Interpolation

PEP 498 -- Literal String Interpolation
Python 3.6の新機能f-strings(フォーマット文字列リテラル)について

例文面白すぎる。

import datetime

yukari = '田村ゆかり'
age = 40
anniversary = datetime.date(1976, 2, 27)

print(f'{yukari}{age-23}歳: Anniv. {anniversary: %A, %B, %d, %Y}')
# 田村ゆかり17歳: Anniv.  Friday, February, 27, 1976

# ちなみに .format() で上記を書き換えると以下のような感じ

print('{yukari}{age}歳: Anniv. {anniv: %A, %B, %d, %Y}'.format(yukari=yukari, age=age-23, anniv=anniversary))

# 順番通りに出力するだけならプレースホルダなしでもよいみたい

print('{}{}歳: Anniv. {:%A, %B, %d, %Y}'.format(yukari, age-23, anniversary))

Variable

# 動的型付けのため型宣言不要
# 定数がないので全大文字を定数ぽく利用する慣習有り
player = 'ゆうしゃ'
print(player + 'は目の前が真っ暗になった')

# 異なるデータ型連結エラー
enemyNum = 10
print('スライムが' + enemyNum + '匹あらわれた')

# 以下のように str() int() などでキャストすべし
print('スライムが' + str(enemyNum) + '匹あらわれた')

String

一般的な文字列操作 - docs.python.org
Python文字列操作マスター

# sprintf
#
# %s 文字列として展開
# %d 整数として展開
# %f 小数点数として展開

c = 'World'
print('Hello, %s!' % c)  # Hello, World!

a = 'Python'
b = 'a programming language'
print('%s is %s' % (a, b))  # Python is a programming language

v = dict(first='Michael', family='Jackson')
print('He is %(first)s %(family)s.' % v)  # He is Michael Jackson

Raw String

# Raw string.
# エスケープ \ など特殊文字を無効化して文字列として扱う
# 文字列リテラルで正規表現を作るときとかに便利
#
path  = 'C:\\Windows\\system32\\cmd.exe'  # \ を \ でエスケープしてる
rpath = r'C:\Windows\system32\cmd.exe'    # r をつけたのでエスケープ不要
path == rpath  # True

s_r = repr(s)  # 変数を Raw 化するなら repr() を利用
print(s_r)     # 'a\tb\nA\tB'

Byte String / Unicode String

Python2で文字列を処理する際の心掛け Python 3 系ではデフォルトが Unicode なので使うことはほぼない。2 系では日本語などマルチバイト表現時に利用していた。

# Python
#
 'あ'  # バイト文字列 '\xe3\x81\x82'
u'あ'  # Unicode 文字列 u'\u3042'

'あ'.decode('utf-8')   # u'\u3042'

u'あ'.encode('utf-8')  # '\xe3\x81\x82'

List

配列。

list   = ['hoge', 'fuga', 'bar']
sample = list[0]

list.append("piyo")     # append
removed  = list.pop(2)  # pop
list_num = len(list)    # length

List x Splat Operator

# Unpacking list.
li1 = [1, 2, 3, 4, 5]
li2 = [99, *li1]       # [99, 1, 2, 3, 4, 5]

Dictionary

ハッシュ。ドットアクセスはできないのでやりたい場合は cdgriffith/Box とか使うよろし。

dic = {
    'id':   'i-hogehoge',
    'type': '4xlarge',
    'child': {
        'id': 'i-piyopiyo',
        'type': '2xmid',
    },
}

print(dic['id'])       # i-hogehoge
print(dic['num'])      # KeyError Exception
print(dic.get('num'))  # None

# Nesting get
dic.get('child', {}).get('id')     # i-piyopiyo
dic.get('child', {}).get('child')  # None

len(dic)  # length

dic['num'] = 100   # add
dic.append('num')  # add

del dic['num']          # delete
type = dic.pop('type')  # pop

Tuple

イミュータブルな配列。

# タプル、イミュータブルなリスト
# Dictionary のキーとしても使える
# https://www.lifewithpython.com/2017/12/python-tuple-list-difference.html
#
my_tuple = ('hoge',)  # 要素1つの場合末尾の , が必要
print(my_tuple[0])    # hoge

# タプルを += すると両者を足した新規タプル生成
t1  = ('みかん', 'りんご')
t1 += ('バナナ', 'パイナップル')
print(t1)  # ('みかん', 'りんご', 'バナナ', 'パイナップル')

Set

集合。

# 集合、一意なデータ集合を表現するリスト
set_ = {'abc'}  # set() でも OK

# インデックスがないため順序の概念がない
for s in set_:
    print(s)  # c,a,b ...

# in を使った参照がリストより高速
numbers = {1, 2, 3}
print(1 in numbers)  # True
print(4 in numbers)  # False

# add, remove, clear, pop ...
numbers.add(4)
numbers.remove(1)
numbers.pop(2)
numbers.clear()

# 集合演算が可能
# https://goo.gl/YNqGNS
#
# 和集合: union(), |
# 積集合: intersection(), &
# 差集合: difference(), -
# 対称差集合: symmetric_difference(), ^
# 部分集合判定: issubset(), <=
# 上位集合か判定: issuperset(), >=
# 互いに素か判定: isdisjoint()

If

type = 't2.micro'

if type == 'm4.xlarge':
    print("NOOOOOOOOOOOO!!!!!")
elif type == 't2.micro': 
    print('OKKKKKKKKKK!!!!!!!')
else:
    print(type)

a = None

if a is None:
    print('a is None')

# elif について:https://goo.gl/YNqGNS
# ちなみに python には switch ありません

Loop

list = ['hoge', 'fuga', 'bar']

# for in
for x in list:
    print x
    # hogefugabar

for i in range(10):
    print i
    # 0123456789

for i in range(6, 10):
    print i
    # 6789

# while
import random

hp = 30
while hp > 0:
    hit = random.randint(1, 10)
    print('スライムに' + str(hit) + 'のダメージを与えた')
    hp -= hit

print('スライムを倒した!!')

Comparison Operators

# == Object Equality を比較 ( 型も見る )
str1 = 'hoge'
str2 = 'piyo'

if str1 == str2:
    print('not equal.')

# Object Identity を比較 ( 同一オブジェクトか見る )
a = None

if a is None:
    print('Good')

Import

# 外部ライブラリを import して利用
import random

number = random.randint(1,100)
print('スライムが' + str(number) + '匹あらわれた')  # 1 ~ 100匹

import boto3
ec2 = boto3.resource('ec2')

Namespace of Modules / Packages

Python におけるモジュールとパッケージは「名前空間」

"""
polls/models/
 ├─ __init__.py ... 名前空間参照時にロードされる初期処理
 ├─ question.py ... Question モデルクラスの定義
 ├─ choice.py   ... Choice モデルクラスの定義
 └─ utils.py    ... 内部利用するユーティティ
"""

# polls/models/__init__.py
#
from .question import Question
from .choice import Choice
# ( utils は内部でしか使わないので書かない )

# 外部でモデルクラスを参照するとき ...
# 分割前 ( polls/models.py ) と同じ import でよくなる
#
from polls.models import Question, Choice
question = Question()
choice = Choice()

Function

def lambda_handler(event, context):
    kansu_kekka = kansu1(hoge, hoge)
    print(kansu_kekka)

def kansu1(hikisu1, hikisu2):
    moji_1 = hikisu1
    moji_2 = hikisu2
    kekka = moji_1 + moji_2
    return kekka

# タイプヒンティング ( python 3.5 以降 )
def to_int(s: str) -> Optional[int]:
    return int(s)

Args - *args / **kwargs

python3x: parameterとして出てくるargsと*kwargsって何を意味するの?
Python 関数のキーワード引数

# Python は引数定義時にキーワードが設定され
# Call 時に順番にかかわらず指定可能
#
def f(option1=1, option2=2): 
    pass

f(option1=10, option2=20)
f(option2=20, option1=10)  # 順番は構わない

# 但しデフォルト値がない引数をキーワードで呼ばない
# = 位置指定した際は順番を守るべし ( 当たり前か
#
def print_message(message, id=1):
    print(message)
    print('id is ' + str(id))

print_message('めっせーじ')        # OK
print_message('めっせーじ', 9)     # OK
print_message('めっせーじ', id=9)  # OK
print_message(                    # OK
    id=9,
    message='めっせーじ',
)
print_message(                    # NG!!
    9,
    message='めっせーじ',
)


# *args: 可変長引数は
# 値が Tuple で格納される
#
def foo(*args):
    for a in args:
        print a

foo(1) #1
foo(1,2,3) #1\n2\n3\n


# **kwargs: キーワード可変長引数は
# 値が Dict で格納される
#
def bar(**kwargs):
    for a in kwargs:
        print(a, kwargs[a])

bar(name="your mom", age=12, hotness=1) #hotness 1\nage 12\nname your mom


# Python 3 では * Required 引数を利用して
# 固定引数の後続のキーワード引数を誤って
# 位置引数としてコール不可能にすることも可能
#
def fc1(a, b, option1=1, option2=2): 
    pass

def fc2(a, b, *, option1=1, option2=2): 
    pass

fc1(1, 2, 3)  # 最後の 3 は「位置的になんとなく」 option1 になっちゃう
fc2(1, 2, 3)  # 固定引数は 2 個までやで ... ってエラー吐いてくれる

Lambda function

myfunc = lambda x: x ** 2 
myfunc(5)  # 25

# sorted() との組み合わせ
# 与えられたリストをソートしてくれる非破壊関数
list1 = [(7, 2), (3, 4), (5, 5), (10, 3)]
list2 = sorted(list1, key=lambda x: x[1])
print list2  # [(7, 2), (10, 3), (3, 4), (5, 5)]

# map() との組み合わせ
# リスト等の各要素に同じ処理をして返してくれる非破壊関数
list1 = [1, 3, 5]
list2 = map(lambda x: x ** 2, list1)
print list2  # [1, 9, 25]

# filter() との組み合わせ
# 与えられたリストから条件に合うもののみを抽出して返す非破壊関数
class Album(object):
    def __init__(self, title, artist):
        self.title = title
        self.artist = artist

a1 = Album("A Hard Day's Night",'The Beatles')
a2 = Album('The Rolling Stones','The Rolling Stones')
a3 = Album("Abbey Road",'The Beatles')
albums = [a1, a2, a3]

# このリストalbumsの中から、artistが「The Beatles」のもののみを抽出したい
albums_beatles = filter(lambda album: album.artist == 'The Beatles', albums)

for a in albums_beatles:
    print a.artist, '-', a.title
    # The Beatles - A Hard Day's Night
    # The Beatles - Abbey Road

Exception

try:
    example_dict = {
        'hoge': 'fuga',
        'piyo': {'foo': 'bar'},
    }
    example_dict['piyo']['baz']
except KeyError:
    pass

try:
    f = open(file_name,'w')
    data = dict_input['data']
    f.write(data)
    f.close()
    # 例外投げは raise を使う → raise KeyError('HiThere') 
except KeyError:
    print('キーが見つかりませんでした')
except (FileNotFoundError, TypeError) :
    print('ファイルが開けませんでした')
except:
    print('何らかのエラーが発生')
else:
    print('正常終了しました')
finally:
    print('エラーでも正常終了でも来ます')
"""
異常終了以外の全例外をキャッチしたいなら
基底例外クラスを except するとかも可能

但し FW などでこれをやると後続のエラー制御に
大きく影響するため取り扱い注意

最低でも処理を挟み込んだのち再 raise しておくべき
"""

def divide_exception(a, b):
    try:
        print(a / b)
    except Exception as e:
        print(str(e))
        #
        # テスト以外ではここで再スローしとくのが無難
        # raise e にするより以下でスタックトレースに
        # 再スローの跡を残さずに投げる方がいいみたい
        #
        # raise

divide_exception(1, 0)      # division by zero
divide_exception('a', 'b')  # unsupported operand type(s) for /: 'str' and 'str'

With

with statement - docs.python.org
ブロックの実行を、コンテキストマネージャによって定義されたメソッドでラップするために使われます (with文とコンテキストマネージャ セクションを参照してください)。これにより、よくある try...except...finally 利用パターンをカプセル化して便利に再利用することができます。

with構文とは何なのか
with構文は、ある機能の利用者が、より安全、簡潔にその機能を使えるようにする構文です。既知の定形終了処理であれば、機能作成側でそれをあらかじめ定義し、利用者はwith構文を使うだけで、安全に機能を使うことが出来ます。

Pythonのwith構文の使い方

# try で囲うような例外の起きそうなコードブロックについて with でラップできる
# 以下ケースでは open メソッドが予め用意している例外ハンドリングの定型句を利用して
# try except finally を省略しつつ安全に処理を行えるようにしている
#
with open('sample.txt', 'r') as f:
    print(f.read())


# with でラップせずに try を自分で組んだらきっとこう
#
f = None
try:
    f = open('sample.txt', 'r')
    try:
        print(f.read())
    except:
        raise
finally:
    if f:
        f.close()

Class

class Spam:

    # プロパティ
    val = 100

    # メソッド
    # 定義時は必ず第一引数に self を記述し
    # 他言語の this みたいな擬似変数として使う
    # コール時にはこいつは不要
    #
    def ham(self):
        self.egg('call method')

    def egg(self, msg):
        print('{0}'.format(msg))
        print(('{0}'.format(self.val)))

    # Static なクラスメソッド
    # インスタンス化しないで使うやつね
    #
    @staticmethod
    def hello():
        print('hello')

spam = Spam()  # new
spam.ham()     # 100

# メソッドの第一引数 self は this 
# Pythonではメソッドは最低 1 つの引数を持つ
# この最初の引数は必ず self という名前にする慣例があり
# self にすることでオブジェクト自身の変数を取得したり
# メソッドを呼び出すことが出来る

Init as constructor

# __init__()という名前で定義 _ を前後に2つ。

class Spam:
    def __init__(self, ham, egg):
        self.ham = ham
        self.egg = egg
    def output(self):
        sum = self.ham + self.egg
        print('{0}'.format(sum))

spam = Spam(5, 10)
spam.output()

# __del__というデストラクタもあるがあんま使わない

Extends

class Base:
    basevalue = 'base'
    def spam(self):
        print(Base.spam())

    def ham(self):
        print('ham')

# class クラス名(基底クラス名): でサブクラス定義
class Derived(Base):
    def spam(self):
        print (Derived.spam())
        self.ham()

derived = Derived()
print('{0}'.format(derived.basevalue)) # base
derived.ham()  # ham

Multiple Extends

# class クラス名(基底クラス名1, 基底クラス名2, ...)
class A:
    pass # 何も持たないクラスの宣言

class B:
    pass

class C(A):
    pass

class D(B, C):
    pass

Abstract Base Class

Pythonの継承とAbstract Base Class

from abc import ABCMeta, abstractmethod

class Abstract(metaclass=ABCMeta):

    def __init__(self, name):
        self.name = name

    def explain(self):
        print(self.name)

    @abstractmethod
    def hello(self):
        raise NotImplementedError()


class Implement(Abstract):

    # 抽象メソッド hello を実装
    def hello(self):
        print('hello')

Access Modifier

# public / private しかない
# private にするにはプロパティ・メソッド名の前に __ をつける

class Spam:
    __attr = 100
    def __init__(self):
        self.__attr = 999
    def method(self):
        self.__method()
    def __method(self):
        print(self.__attr)

spam =Spam()
spam.method()   # OK
spam.__method() # NG
spam.__attr     # NG

Asynchronous

asyncio - docs.python.org
python3 の async/awaitを理解する

Python の async / await は並列に コルーチン Coroutin を起動する。Python 単体でマルチスレッド化したりするワケではないが「制御プロセスを手放して実行する」ということになる。JavaScript でいうと async / await よりかは Generator / yield の関係に近いみたい。

Coroutine - コルーチンは「特定のプロセス/スレッドに束縛されない、中断可能な計算インスタンス」のこと。 一般的に非同期処理で用いられ、 Thread よりも軽量で、実行途中で処理を「中断・再開」することができる。

async は「処理の中断・再開が可能な 返却関数」を定義する。これに対して await は以下 2 パターンの挙動をとれる yield みたいなものらしい。

  • await + Subroutine: 非同期でサブルーチンを実行し制御を手放す
  • await + Coroutine: 非同期でコルーチンを実行するが、処理結果の返却を待つ
import asyncio

async def main():
    print('hello')
    await asyncio.sleep(1)
    #
    # 上記で Coroutine な sleep を実行し結果を待つため
    # 結果的に 1 秒待って以下 world が出力される
    #
    print('world')

# 例によって await は async 内部でしか利用できない
# また async ( = Coroutine ) を await なしで実行する場合は
# main() ではなく asyncio.run() を使って起動してやる必要がある
#
asyncio.run(main())

MODULES

pprint - Pretty Print for Debug

from pprint import pprint

pprint(my_var)

# オブジェクトが保持する dict っぽいものを返す
# Django ORM のオブジェクトとか見たいときはこれ
#
pprint(vars(user_object))

# オブジェクトが保持する list っぽいものを返す
# クラスのプロパティ/メソッドの一覧が欲しいときはこれ
#
pprint(dir(user_object))

sys - 標準入力の処理

import sys

for line in sys.stdin.readlines():
    print(line.rstrip())

os - 環境変数

import os

os.environ['DOMAIN']  # $DOMAIN 環境変数を参照

datetime - 日付型

Python日付型

import datetime

print(datetime.date.today())     # 2017-11-08
print(datetime.datetime.today()) # 2017-11-08 23:58:55.230456

now = datetime.datetime(2017, 11, 12, 9, 55, 28)
print(now.year)   # 2017
print(now.month)  # 11
print(now.day)    # 12
print(now.hour)   # 9
print(now.minute) # 55
print(now.second) # 28

# 曜日は例のごとく 0: 月 ~ 6: 日曜日が返却
print(datetime.date(2017, 11, 11).weekday()) # 5

python3のdatetimeとtimezoneとpytzについて

# datetime はタイムゾーンを持たない native datetime オブジェクトなので
# フレームワークなどではタイムゾーンを持つ aware datetime へ変換が必要

import pytz
import datetime
now = datetime.datetime.now(pytz.timezone('UTC'))

# 上記nowを日本時間で扱いたければ
now.astimezone(pytz.timezone('Asia/Tokyo'))

# 特定の時間のdatetimeがほしければ
my_time = datetime.datetime(2018,6,30,12,0,0,0,pytz.timezone('Asia/Tokyo'))
# Django なら標準でユーティリティが用意されている

from django.utils import timezone

utc_now = timezone.now()         # utc
local_now = timezone.localtime() # local

timedelta - 日付/時刻計算

timedelta - docs.python.org

import datetime

calc_source = datetime.datetime(2017, 11, 12, 9, 55, 28)
print(calc_source + datetime.timedelta(weeks=3)) # 2017-12-03 09:55:28
# Django 

from django.utils import timezone

start_time = timezone.localtime()
end_time   = start_time + timezone.timedelta(minutes=10)

BUILT-IN CLASS / API

split / join

'A,B,C'.split(',')         # ['A', 'B', 'C']
','.join(['A', 'B', 'C'])  # 'A,B,C'

map / filter

map と filter ってなに?

JavaScript の非破壊配列操作メソッド Array.prototype.map と filter と大体おんなじような空気。内部的に Generator で実装されていて、渡されたイテラブルな要素と、渡された関数から新規 Generator を作成して、渡された関数を通過後した後 yield で要素が返却される ... みたいな挙動になっているっぽい。

当然配列メソッドではないため Generator からインデックスなどで要素を取得することはできないが、Generator が得意とする「メモリ上への要素の一括展開をせずに」順繰り巨大なイテラブルデータを扱う、みたいなことができる。

"""
map(function, iterable)
    @return MapObject
    
    指定関数を全要素実行して新規
"""
list = [1, 2, 3, 4, 5, 6, 7]

# 全要素二倍にしてリストで返す
list(map(lambda x: x * 2, list))


"""
filter(function, iterable)
    @return FilterObject
    
    条件に満たないものを削除
"""
list = [1, 2, 3, 4, 5, 6, 7, 8, 9]

# 偶数を抽出したリストで返す
# 存在しない場合は空の FilterObjcet から
# 空のリストが生成 ( キャスト ) される
#
list(filter(lambda x: x % 2 == 0, list))

# 偶数な最初の要素を返す
#
# next() で直接イテレータを終点まで進めるため
# 見つからなかった際は StopIteration 例外が発生
# 第二引数でデフォルト値を指定するのがよい
#
next(filter(lambda x: x % 2 == 0, list))

next(filter(lambda x: x == 10, list), None)  # None

namedtuple

FYI: namedtupleで美しいpythonを書く!(翻訳)

こう考えるとよいでしょう。namedtupleはメモリ効率がよいイミュータブルなクラスです。そして、クラス定義のショートカットになります。

これ便利すぎて涙出てくる ... 。

from collections import namedtuple

# 中で split されるので第二引数は 'color milage' でもいい
#
Car = namedtuple('Car' , ['color', 'milage'])

my_car = Car(color='red' , mileage=3812.4)
my_car.color    # 'red'
my_car.mileage  # 3812.4

# タプルなのでイミュータブル
my_car.color = 'blue'  # AttributeError: "can't set attribute"


"""
Python 3.6.1 からは typing.NamedTuple を使って
class 定義っぽくイミュータブルな構造体を定義できる
"""

from typing import NamedTuple
from typing import TypeVar  # こいつはクラスの型定義用

class User:
    # ...

U = TypeVar('User', bound=User)

class UserBirthday(NamedTuple):
    user: U
    year: int
    month: int
    day: int  # 初期値入れたいなら day: int = 14 とか

def user_birthday(user: U):
    # なんか色々処理して user の immutable な誕生日情報返す
    return UserBirthday(user, 1988, 8, 30)