Python
を勉強しようと思ったことはこれまでに何度もあるのですが、仕事などで、ちょっとした処理を行うときなどは、つい使い慣れたRuby
で書いてしまうので、なかなかPython
の技術が上達せずにここまできました。
2022年の目標としてPython
をしっかり学んでみようと思います。
以下は、『Python実践レシピ』を読みながら気になることをメモしたものです。
Chapter 1 Pythonの環境
仮想環境について、
1. 仮想環境の作成
python -m venv dir_name
2. 仮想環境の有効化
source dir_name/bin/activate
3. 仮想環境の無効化
deactivate
Chapter 2 コーディング規約
Pythonには、PEP 8というスタイルガイドがあり、Pythonの開発者の間ではPEP 8に従うことが一般的
Chapter 3 Pythonの言語仕様
with文
with
文に渡すオブジェクトをコンテキストマネージャーという。with文が実行されると、
__enter__()
メソッドの処理が実行される。
with
ブロックが終了したあとに、__exit__()
が実行される。
@contextlib.contextmanager
というデコレータを使用してジェネレータ関数を記述することで、コンテキストマネージャーを定義できる。
ジェネレーター
>>> def multiplier():
... num = 1
... while True:
... yield num
... num *= 2
...
>>> gen = multiplier()
>>> dir(gen)
['__class__', '__del__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__name__', '__ne__', '__new__', '__next__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running', 'gi_yieldfrom', 'send', 'throw']
>>> next(gen)
1
>>> next(gen)
2
>>> next(gen)
4
デフォルト値付引数でリストオブジェクトを使用するときの注意点
>>> def sample_func(a, b, c=[]):
... c.append(a + b)
... return c
...
>>> sample_func(1, 2)
[3]
>>> sample_func(3, 4)
[3, 7]
>>> sample_func(5, 6)
[3, 7, 11]
実行結果は、[3], [7], [11]
となって欲しいのですが、原因は、デフォルト値のリストオブジェクトは関数作成時に一度だけ作成され、そのあともこのリストオブジェクトが再利用されるためだそうです。ちょっと直感と動作が違うような気がします。
解決策は、以下の通りです。
>>> def sample_func(a, b, c=None):
... if c is None:
... c = []
... c.append(a + b)
... return c
...
>>> sample_func(1, 2)
[3]
>>> sample_func(3, 4)
[7]
>>> sample_func(5, 6)
[11]
ちなみに、Rubyだと以下のように動作します。
irb(main):005:1* def sample_func(a, b, c = [])
irb(main):006:1* c.append(a + b)
irb(main):007:1* c
irb(main):008:0> end
=> :sample_func
irb(main):009:0> sample_func(1, 2)
=> [3]
irb(main):010:0> sample_func(3, 4)
=> [7]
irb(main):011:0> sample_func(5, 6)
=> [11]
デコレーター
def my_decorator(func):
def wrap_function():
"""wrap_functionのドキュメントです"""
func()
print(f'function: {func.__name__} called')
return wrap_function
def greeting():
"""greetingのドキュメントです"""
print('こんにちは')
greeting = my_decorator(greeting)
print(greeting.__name__)
print(greeting.__doc__)
greeting()
実行すると、
wrap_function
wrap_functionのドキュメントです
こんにちは
function: greeting called
となり、もとの関数名やdocstring
が失われる。
def my_decorator(func):
def wrap_function():
"""wrap_functionのドキュメントです"""
func()
print(f'function: {func.__name__} called')
return wrap_function
@my_decorator
def greeting():
"""greetingのドキュメントです"""
print('こんにちは')
print(greeting.__name__)
print(greeting.__doc__)
greeting()
この方法でも、デコレータ関数を作成し代入文を使用する例と同様の実行結果となる。
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrap_function():
"""wrap_functionのドキュメントです"""
func()
print(f'function: {func.__name__} called')
return wrap_function
@my_decorator
def greeting():
"""greetingのドキュメントです"""
print('こんにちは')
print(greeting.__name__)
print(greeting.__doc__)
greeting()
実行すると、以下のようになり、名前やdocstring
をもとのデコレート対象の関数のものに設定してくれる。通常、デコレーターを作成するときは、functools.wraps
を使用するとよい。
greeting
greetingのドキュメントです
こんにちは
function: greeting called
Chapter 4 Pythonのクラス
from dataclasses import dataclass
@dataclass
class User:
name: str
age: int
address: str
user = User('mae', 20, 'Miyazaki')
print(user)
Chapter 5 型ヒント
def say_hello(name: str) -> str:
return f'Hello, {name}'
print(say_hello('Python'))
print(say_hello(0))
を実行すると、以下のようになり、実行することはできる。
Hello, Python
Hello, 0
mypyによる静的型チェックを行うと、以下のように出力され、say_hello
にint
が渡されているとエラーが出力される。
type_hint_prac.py:5: error: Argument 1 to "say_hello" has incompatible type "int"; expected "str"
Found 1 error in 1 file (checked 1 source file)