スコープと名前空間について
名前空間
ポイント
- 名前空間とは、名前とオブジェクトの対応付けの事
- 名前空間に紐づく属性は
obj.attribute
のようにドットでつなぐ
スコープ
ポイント
- スコープとは、スコープある名前空間から直接アクセスできるプログラムテキスト上の範囲の事
- スコープの参照順は以下
- ローカル名の入ったスコープ
- ↑を取り囲む関数がある場合、その関数のスコープ
- 今いるモジュールのグローバルなスコープ
- 最も外側のスコープはビルトイン名の入ったスコープ
- global文が使用されていない限り、代入や削除は常に最も内側のスコープにおいて行われる
- global文を使うと、その変数がグローバルスコープにあることを示す
- nonlocal分は、その変数が現在のスコープを取り囲むスコープにあることを示す
スコープと名前空間の例
def scope_test():
def do_local():
spam = "local spam"
def do_nonlocal():
nonlocal spam
spam = "nonlocal spam"
def do_global():
global spam
spam = "global spam"
spam = "test spam"
do_local()
print("After local assignment:", spam)
# After local assignment: test spam
do_nonlocal()
print("After nonlocal assignment:", spam)
# After nonlocal assignment: nonlocal spam
do_global()
print("After global assignment:", spam)
# After global assignment: nonlocal spam
scope_test()
print("In global scope:", spam)
# In global scope: global spam
クラス定義
クラス定義の構文
クラス定義
class className:
<文1>
.
.
.
<文N>
ポイント
- クラス定義に入ると新しい名前空間が生成され、
ローカルスコープとして使われる(ローカル変数、ローカル関数はこのスコープ) - クラス定義の実行が終わるとクラスオブジェクトが生成される(基本的には名前空間を包むラッパーに過ぎない)
クラスオブジェクト
ポイント
- クラスオブジェクトは、属性参照、インスタンス化の2種の操作をサポートする
- 属性参照は
obj.name
の形で行われる - 属性参照はクラスの名前空間に存在するすべての名前(以下のような場合、
MyClass.i
とMyClass.f
が有効)
属性参照
class MyClass:
"""A simple example class"""
i = 12345
def f(self):
return 'hello world'
- クラスのインスタンス化は関数のように呼び出す
クラスのインスタンス化
x = MyClass()
- この時、
__init__()
メソッドが定義してあると、新規作成したインスタンスに対し自動的に__init()__
がコールされる
__init__()関数
def __init__(self):
self.data = []
-
__init__()
関数には引数を与えることができる - 引数はクラスのインスタンス化の時に与えた引数が使用される
クラスのインスタンス化(引数あり)
class Human:
def __init__(self, height, weight):
self.height = height
self.weight = weight
x = Human(165, 55)
print('height:', x.height, 'weight:', x.weight)
# height: 165 weight: 55
インスタンスオブジェクト
ポイント
- インスタンスには「データ属性」と「メソッド」の2つの属性参照がある
データ属性とメソッドの参照
class MyClass:
def __init__(self):
pass
def f(self, word):
print(word)
x = MyClass()
# データ属性の追加・参照
x.counter = 1 # データ属性は事前に宣言する必要はない
while x.counter < 10:
x.counter = x.counter * 2
print(x.counter) # 16
del x.counter
# メソッドの参照
x.f("hello kawauso")
メソッドオブジェクト
- メソッドは変数に格納できる
- メソッドの特徴は第1引数にインスタンスのオブジェクトが渡されること
- x.f()のコールはMyClass.f(x)と等価である
メソッドオブジェクトを変数に格納
xf = x.f
xf('hello kawauso again') # 出力: hello kawauso again
クラス変数とインスタンス変数
ポイント
- クラスで共通のクラス変数とインスタンス毎に固有のインスタンス変数がある
- インスタンス毎に値の変更が必要なものはクラス変数にはしないこと
クラス変数とインスタンス変数の例
class Dog:
kind = '柴' # クラス変数
def __init__(self, name):
self.name = name # インスタンス変数
taro = Dog('太郎')
jiro = Dog('次郎')
# クラス変数の参照
print(taro.kind) # 柴
print(jiro.kind) # 柴
# インスタンス変数の参照
print(taro.name) # 太郎
print(jiro.name) # 次郎
その他いろいろ
ポイント
- データ属性は同名のメソッドをオーバーライドしてしまうので名前の重複は命名規則などで避ける
- データ属性はどこからでも操作できる(クラスによる隠ぺいがされない)
- メソッドの第1引数は慣習的に
self
とする(予約語ではないがこの方がみんな慣れているから) - メソッドのグローバルスコープはそのメソッドが定義されたモジュール(クラスがグローバルスコープとして使われることはない)
- グローバルスコープにインポートされた関数やモジュールももちろん使用可能
- メソッドのクラスそのものもグローバルスコープに定義されているため参照可能
継承
クラスの継承
ポイント
- 継承はクラスの引数に基底クラス名を渡す
- 別モジュールからも渡すことができる
継承のサンプル
class base():
def a(self):
print('私はbase.aです.base.bをコールします')
self.b()
def b(self):
print('私はbase.bです.der.bでオーバーライドされます')
class der(base):
def b(self):
print('私はder.bです.')
b = base()
d = der()
b.a()
# 私はbase.aです.base.bをコールします
# 私はbase.bです.der.bでオーバーライドされます
d.a()
# 私はbase.aです.base.bをコールします
# 私はder.bです.
- インスタンスの型をチェックするにはビルトイン関数、
isinstance(obj, int)
を使用する - クラス継承のチェックには
issubclass()
を使用する
多重継承
ポイント
- Pythonでは複数の基底クラスを持つクラス定義が可能
※ボリュームありそうなので、これについては別でまとめる
プライベート変数
ポイント
- Pythonにはオブジェクト内部からしかアクセスできないプライベートなインスタンス変数は存在しない
- 慣習として
_
が前置された名前はAPIの非公開部分とする - クラスプライベートメンバについては、名前マンダリングというサブクラスとの名前衝突を防ぐ仕組みがある
- これは
__spam
のような形の識別子をすべて_classname__spam
の形に置き換えるというもの
名前マンダリングの例
class Mapping:
def __init__(self, iterable):
self.items_list = []
self.__update(iterable)
def update(self, iterable):
for item in iterable:
self.items_list.append(item)
__update = update # ↑のupdate()メソッドのプライベートコピー
class MappingSubclass(Mapping):
# update()の新しいシグネチャを提供しつつ
# 既存の__init__()は破壊せずに利用できる
def update(self, iterable):
for item in zip(keys, values):
self.items_list.append(item)
その他のあれこれ
- 名前の付いたデータアイテムを埋めていく場合は空のクラス定義を使うと便利
class Employee:
pass
john = Employee()
john.name = 'Jhon Doe'
john.dept = 'computer lab'
john.salary = 1000
- 特定のデータ型を想定したPythonコードに、そのデータ型のメソッドをエミュレートしたクラスを渡すことはよくある。例えば、文字列バッファからデータを得るクラスに
read()
またはreadline()
メソッドを定義すると、ファイルオブジェクトからデータを受け取って整形する関数に、引数として渡せるようになる - インスタンスメソッドオブジェクトにも属性がある。メソッド
m()
に対して、インスタンスオブジェクトはm.__self__
であり、メソッドに対応した関数オブジェクトはm.__func__
である
例外もクラス
ポイント
- ユーザー定義例外もクラスとされるため、これを利用して拡張可能な階層型の例外体系が作れる
反復子(iterator)
ポイント
- for文では内部的に、コンテナオブジェクトに対して、
iter()
をコールするようになっている - 反復子オブジェクトには
__next__()
が備わっており、要素が尽きるとStopIteration
例外を送出し、forループはこれを受けて終了する - 自作のクラスに反復子のふるまいを追加するためにはこの仕組みを利用して以下のように定義すればよい
__iter__()と__next__()
# __next__()メソッドの付いたオブジェクトを返す__iter__()メソッドを定義する
# すでに__next()__が定義してあるクラスでは__iter__()はselfを返すだけでよい
class Reverse:
def __init__(self, data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
def __next__(self):
if self.index == 0:
raise StopIteration
self.index = self.index - 1
return self.data[self.index]
rev = Reverse('spam')
iter(rev)
for char in rev:
print(char)
# m
# a
# p
# s
ジェネレータ
ポイント
- ジェネレータはシンプルに反復子を作るためのツール
- 普通の関数と同じように書くが、データを返す部分で
yield
を使う - next()がコールされるたびにジェネレータは前回抜けたところに戻る
- ジェネレータは
__iter__()
と__next__()
のメソッドを自動的に生成するため、簡単に反復子を生成する - ジェネレータはローカル変数と実行状態をコール間で自動保存する
- ジェネレータは終了時に、自動的にStopIterationを送出する
ジェネレータの例
def reverse(data):
for index in range(len(data)-1, -1, -1):
yield data[index]
for char in reverse('golf'):
print(char)
# f
# l
# o
# g
ジェネレータ式
ポイント
- 簡単なジェネレータであれば
()
を使って簡単にかける
ジェネレータ式の例
sum(i*i for i in range(10)) # 2乗して合計
xvec = [10, 20, 30]
yvec = [7, 5, 3]
sum(x*y for x,y in zip(xvec, yvec)) # 内積
from math import pi, sin
# sin表
sine_table = {x: sin(x*pi/180) for x in range(0, 91)}
# ページの中の重複しない単語
unique_words = set(word for line in page for word in line.split())
# 卒業生総代
valedictorian = max((student.gpa, student.name) for student in graduates)
data = 'golf'
list(data[i] for i in rage(len(data)-1, 1, -1))