私がPythonで詰まるところとか抜けるとこアレコレその1
はじめに
就職先でプロダクトに触れつつある中、やはり未経験故のスキルの無さに直面したのと本格的に Python と向き合う大義名分も得たので、定期的に基本からそれ以降のことまで個人的に忘れがちだったりすることを PU していけたらと思います。
サクッと仮想環境化
python -m venv myenv
source myenv/bin/activate # windows以外
source myenv/Scripts/activate # windows
deactivate # 仮想環境を閉じる
スクリプト実行はディレクトリ合わせてpython ファイル名.py
。
f-string
'...'.format()
の略式、プロダクトのソースコードによく出てきてたやつ。
x = 20
y = 50
s1 = f'The Value of x is {x}' # The Value of x is 20
s2 = f'The Value of xy is {x*y}' # The Value of xy is 1000
型ヒント
変数の型を定義するというのは TypeScript に触れるようになってから意識するようになりましたが、Python でも関数の引数及び返り値に対して変数の型を指定できるのを業務でプロダクトのソースコードに触れるようになって知りました。
独学とかだとなかなか思い至らないですが、プロダクトではバグ対応、エラーチェック、テスト……等々行う上でどこに問題があるのかを出来得る限りはっきりさせておかないと、仮に問題が起きた場合対処が大変なのでこういうところもしっかりしておく必要があるんだなと。
あとまあ、型ヒントあると見慣れないうちは戸惑いますが、慣れてくると逆に引数と返り値に何が入るのかわかりやすくなるので。
# 引数
def type_hint(x: str, y: int):
print (f' {X} number is {y}')
# 返り値
def type_hint_return(x: int) -> int:
return x + 1
比較
def check1(x, y, data):
# and
if 0 <= x < 10:
return x * 2
# or
elif x > 0 or y > 0:
return x * y
# in
elif x < y:
if y in data:
print(f'{data} contains {y}')
# オブジェクトが等価か評価
def check2(x, y):
if x is y:
print('x is y == True')
def main():
# x,yは以下の場合保持している値は等価だが、オブジェクト(インスタンス)としては等価ではない
x = [1,2,3]
y = [1,2,3]
# つまり以下のような実行結果となる
z = x
v = y
check2(y,z) # False
check2(x,z) # True
check2(x,y) # False
check2(v, y) # True
check2(v, z) # False
*args、**kwargs
引数が不特定多数になる場合に使う。
*args
はタプルとして、**kwargs
は辞書として引数を受け取る。
def my_sum(*args):
print('args: ', args)
print('type: ', type(args))
print('sum: ', sum(args))
print(my_sum(1, 2, 4, 6))
# args: (1, 2, 4, 6)
# type: <class 'tuple'>
# sum: 13
def my_sum2(x, y, *args):
print('args: ', args)
print('type: ', type(args))
print('sum: ', sum(args))
print(x * y * sum(args))
print(my_sum(1, 2, 4, 6, 9))
# args: (4, 6, 9)
# type: <class 'tuple'>
# sum: 19
# 38
def func_kwargs(**kwargs):
print('kwargs: ', kwargs)
print('type :', type(kwargs))
func_kwargs(x=1, y=2, z=10)
# kwargs: {'x': 1, 'y': 2, 'z': 10}
# type : <class 'dict'>
def func_kwargs(a, b, **kwargs):
print(a)
print(b)
print('Unit: ', kwargs)
print('type :', type(kwargs))
func_kwargs(a='Gundam Development Project', b='ThiS is Forbidden Documents', key1='ZEPHYRANTHES', key2='PHYSALIS',
key3='STAMEN', key4='GERBERA')
# Gundam Development Project
# ThiS is Forbidden Documents
# Unit: {'key1': 'ZEPHYRANTHES', 'key2': 'PHYSALIS', 'key3': 'STAMEN', 'key4': 'GERBERA'}
# type : <class 'dict'>
集合
x = {0,1,2,3,4,5,6,7,8,9,10}
# リスト・タプルから集合を作る
x = set([1,1,2,2,3,4,5,5,6,6,77]) # {1, 2, 3, 4, 5, 6, 77}
y = set(([1,2,4])) # {1, 2, 4}
# ページには以下の例で紹介されてたけどTypeError: unhashable type: 'list'なのでダメ
y = set(('x', 'x', 3.14, [1, 2], [1, 2])) # TypeError: unhashable type: 'list'
モジュール周り
__all__ = ["モジュール名1", "モジュール名2", "モジュール名3", ....]
つまり
greet
│--__init__.py
│--hello.py
│--goodbye.py
となっていた場合
from greet import *
としたときに import されるのはhello.py
ということになる。
リスト内包表記
プロジェクトで頻出なのとそうでなくてもほぼこの書き方は多いのに個人的には苦手な部分。
# ループの基本かつよく使う用法
x = []
for i in range(50):
x.append(i * 2)
# リスト内包表記
x = [i * 2 for i in range(50)]
順序がこんがらかってわからなくなるのが私の常ですが、つまり
iterableに対してループで適用したい処理 for ループ変数 in iterable
と覚えればいい。
また、単純にリストや辞書を作るだけじゃなくて別の型をキャストしてリストや辞書を新しく作るという用法もよくある。
x = {'a': 10, 'b': 20, 'c': 30}
y = [(key, value) for key, val in x.items()] # [('a', 10), ('b', 20), ('c', 30)]
DF だとこんな感じで使われたりする
import pandas as pd
# DF生成
df_origin = [["taro", "Tokyo", "12"], ["karen",
"Kanagawa", "22"], ["Yamato", "Nagoya", "33"]]
df = pd.DataFrame(df_origin, index=['row1', 'row2', 'row3'], columns=[
'name', 'live', 'age'])
print(df)
""" name live age
row1 taro Tokyo 12
row2 karen Kanagawa 22
row3 Yamato Nagoya 33 """
# 首都圏に住んでいるかどうか判定
def judge_syutoken(col):
syutoken = ["Tokyo", "Kanagawa", "Chiba", "Saitama"]
if col in syutoken:
return "lived"
else:
return "Never"
# リスト内包表記で新しいカラムを作る
df["live_syutoken"] = [judge_syutoken(col) for col in df['live']]
print(df)
# name live age live_syutoken
# row1 taro Tokyo 12 lived
# row2 karen Kanagawa 22 lived
# row3 Yamato Nagoya 33 Never
ラムダ式
いつまでも見慣れないのでこの機会にちゃんと覚える。
# これは
lambda x: x * x
# 以下の関数と同義
def sample(x):
return x * x
つまり単純に引数: 引数を使って行う処理
という図式で覚えればいい。
a = [1, 2, 3, 4, 5]
result = list(map(lambda x: x * x, a))
print(result) # [1, 4, 9, 16, 25]
result = list(map(lambda x: x * x * 4, a))
print(result) # [4, 16, 36, 64, 100]
result = list(map(lambda x: (x, x * x), a))
print(result) # [(1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]
スコープ
アクセス指定子はない。
命名規則で表現する。
メソッドでも変数でも規則は同じでアンダースコアをいくつつけるかで表現する。
- public…… method()
- protected(子クラスからアクセスはできるが、生成したオブジェクトからアクセスができない)…… _method()
- private(クラス内のみでしかアクセスできない)…… __method()
あれってなるやつ
init()
前述の通り、オブジェクト(インスタンス)生成時に最初に呼び出されるメソッド。
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
Object = Point(20,30) # self.x = 20, self.y = 30
getattribute
クラスのメンバやメソッドを参照してるよというお知らせ
getattr
クラスのメンバの参照に失敗しているよというお知らせ。
大抵未定義のものにアクセスするとこれが出てくる。
setattr
メンバへの代入が行われましたよというお知らせ。
当然、クラス変数が Protected 以上だった場合、これは行えない。
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
Object = Point(20,30) # self.x = 20, self.y = 30
Object.x = 30 # self.x = 30, self.y = 30
str
クラスオブジェクト(インスタンス)に対して文字列キャストが行われたり、それが必要と判断された場合と呼び出される。
書式が適切ではないので自分で定義するほうがよい。
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f'<Point(x={self.x}, y={self.y})>'
Object = Point(20, 30) # self.x = 20, self.y = 30
print(Object) # < Point(x=20, y=30) >
プロパティ
import math
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
@property
def distance(self):
return math.sqrt(self.x * self.x + self.y * self.y)
@property のデコレータは関数を変数のように参照するデコレータ。
これがつくと以下の挙動になる。
# これが正常
point = Point(10, 20)
point.distance # 22.360679775
# こっちはエラーになる、つまりクラスメソッド扱いにならない
Point.distance()
もっと役割がわかるように書くと以下の通り。
import math
class Point:
def __init__(self, x, y):
# _ をつけて隠蔽していることを示す
self._x = x
self._y = y
@property
def x(self):
return self._x
@property
def y(self):
return self._y
@property
def distance(self):
return math.sqrt(self.x * self.x + self.y * self.y)
def __init__
は初期化のためのメソッドなので内部のクラス変数にはアクセスさせたくない。
そこで_x
として private にする。
しかし、参照はしたいという場面もある。
そこで、self._x
等を返す関数を作成してその関数に対して@property
のデコレータを適用すればクラス変数への代入を防ぎつつ、参照が可能となる。
point = Point(10, 20)
print(point.x, point.y) # _x, _y の参照は可能。x = 10, y =20
point.x = 30 # _x, _y への代入はできない error
この場合どうしてもクラス変数への代入を試みたい場合は以下のようにする。
@property
def x(self):
return self._x # getter
@x.setter
def x(self, value):
self._x = value # setter
point.x = 30 # OK
with()って何?
with のスコープ内だけでオブジェクトを使用したい、あるいはファイルの読み書きを行うが事が済んだら元に戻したいといった場合に使う。
具体的にはopen()
でファイルを読み込んだ場合はclose()
を行ってファイルを閉じないといけないが、最初からwith()
を使っていれば閉じ忘れもなくなるといった感じで使われる。
# これは
with open("hello.txt") as hello:
print(hello.read())
# これに同じ
hello =-open("hello.txt")
try:
print(hello.read())
finally:
print(hello.close())
ファイルが複数の場合は以下の通り。
with open("file1") as file1:
with open("file2") as file2:
with open("file3") as file3:
print(file1.read(), file2.read(), file3.read())
# ネストは嫌だ
with open("file1") as file1, \
open("file2") as file2, \
open("file3") as file3:
print(file1.read(), file2.read(), file3.read())
# python3.3~
from contextlib import ExitStack
with ExitStack() as stack:
file1 = stack.enter_context(open("file1"))
file2 = stack.enter_context(open("file2"))
file3 = stack.enter_context(open("file3"))
# python3.9~
with (open("file1") as file1,
open("file2") as file2,
open("file3") as file3):
print(file1.read(), file2.read(), file3.read())
Discussion