←PC版は別ページ

≪いっしょにPython≫ プログラマーの実験ノート

 このページは,プログラム言語Pythonをこれから学ぼうと考えている筆者の備忘録です.
 そこそこ調べて書いていますが,仕様を精密に調べたものではありません.どちらかと言えば,実装されたPython 3を使ってみてどうなったかということを中心に書いたものです.いわば,実験ノートの記録です.(最終更新年月日:2019.4.24 / by Python 3.7.3)
Pythonの練習には,
(1) IDLEというメニューから入って,>>>というプロンプトが表示される状態で使う場合
• 画面の上端がPython 3.7.x Shellとなっていて,2行目がFile, Edit, Shell, Debug, ...となっている
• 1行入力するたびに,エラーを確かめながら処理を進める,対話型インタプリタの形になっている
(2) IDLEというメニューから入って,左上端のFileをクリック→New Fileと進む場合
• 画面の上端がファイル名(初めはUntitled)となっていて,2行目がFile, Edit, format, Run, ...となっている
(3) Python 3.7.x という真っ黒なコマンドプロンプトの画面(ターミナルウィンドウ)から入るもの
の3通りが使えるが,以下は(2)のNew Fileから,テキストエディタを使って,プログラムを書く場合を考える.

Pythonの組み込み関数(ビルドイン関数)

• Pythonでは,よく使われる関数が組み込み関数(ビルドイン関数)になっており,何らかの外部モジュールをインポートしなくても,どの場面でも使えるようになっている.

1. abs()関数

【書式】
abs(数値)
• 正(または0)の整数,正(または0)の浮動小数点数が引数のときは,そのままの値を返し,負の整数,負の浮動小数点数が引数のときは,符号を変えたものを返します.(いわゆる絶対値)
• 複素数が引数のときは,複素数の絶対値(実部と虚部の2乗の和の(正の)平方根)を返します.
【例 1】
IDLE → New File → Save as .. → Run
a1 = abs(-3)
a2 = abs(7.1)
a3 = abs(3+4j)
a4 = abs((3+4j)/(1-2j))
print(a1,a2,a3,a4)
3 7.1 5.0 2.23606797749979


• 数学でiと書く虚数単位は,Pythonでは電磁気学での書き方と同様にjで表す.


であるが,Pythonのビルドイン関数だけでは,2.2360679...(富士山麓オーム鳴く)は累乗根表示に直らない.(sympyをインストールして,solve()で方程式の解として求めると,累乗根表示にできる)

2. all()関数,any()関数

all()関数
【書式】
all(イテラブル)
• リストやタプルなどのイテラブルのすべての要素が真であるとき,Trueを返します.
• ただし,イテラブル自体が空のときはTrueになります.(要注意)
(*) 各要素は,False, None, 0, 0.0, 複素数の零 0+0j, 空文字 '', 空のリスト[], 空の辞書 {}, 空の集合 set(),空のタプル()であるとき,偽(False)になり,それ以外はすべて真(True)となります.
any()関数
【書式】
any(イテラブル)
• リストやタプルなどのイテラブルの少なくとも1つの要素が真であるとき,Trueを返します.
• 各要素の真偽は,all()の場合と同様.
要素1要素2
要素n-1要素nall()any()
TrueTrueTrueTrueTrueTrue
TrueTrueTrueFalseFalseTrue
TrueTrueFalseFalseFalseTrue
TrueFalseFalseFalseFalseTrue
FalseFalseFalseFalseFalseFalse
⇒ 全部が真か?と尋ねているのがall(),1つでも真があるか?と尋ねているのがany()
【例 2.1】
IDLE → New File → Save as .. → Run
L1 = []
D1 = {}
S1 = set()
T1 = ()
str1 = ''
B1 = all(L1)
B2 = all(D1)
B3 = all(S1)
B4 = all(T1)
B5 = all(str1)
print(B1, B2, B3, B4, B5)
True True True True True
(要注意)空のリスト,空の辞書,空の集合,空のタプル,空文字自体ををall()関数に渡すと,Trueが返される.(なお,{}は空の辞書を表すので,空集合は{}でなく,set()と書く.)

• Python3のドキュメント(https://docs.python.org/ja/3/index.html)には,all()関数は次のコードと等価であると紹介されている.
 これによれば,iterableの要素を順に調べて行って,elementに1つでも偽の要素があれば,not elementが真になるので,関数はFalseを返す.偽の要素が見つからないまま,forループが終わりまで来たら,Trueを返すという想定になっている.
 ところが,iterableの要素が1つもないときは,forループが始まらないまま,return trueに行くので,Trueが返される.
[ビルドイン関数のall()]
def all(iterable): for element in iterable: if not element: return False return True
 自分が使うときに,この約束に従うのは不合理だと考えるときは,そのプログラムでだけall()関数を次のように上書きして使えば,空データの場合にFalseが返される.
def all(iterable):
    if len(iterable) == 0:
        return False
    for element in iterable:
        if not element:
            return False
    return True
 これに対して,any()関数はつぎのコードに等しく,空データの場合にFalseを返すので,ビルドイン関数のままで常識感覚と合う.
[ビルドイン関数のany()]
def any(iterable): for element in iterable: if element: return True return False
【例 2.2】
IDLE → New File → Save as .. → Run
L1 = [[], {}, set(), (), '']
B1 = any(L1)
print(B1)
False
• 空のリスト,空の辞書,空の集合,空のタプル,空文字を要素とするリストをall()関数に渡すと,(*)で述べたように各要素がFalseを返すから,all()はFalseを返す.any()でも同じ.
【例 2.3】
IDLE → New File → Save as .. → Run
L1 = [ 2 > 1, -1, 3.1, 1+2j, '0', '',1 == 1.0]
B1 = all(L1)
print(B1)
True
• 2 > 1 という式は真と評価される.
• (1だけでなく)0以外の整数の真偽値は,すべて真.−1も真になる.
• 0.0以外の浮動小数点数の真偽値は,すべて真
• 0+0j以外の複素数の真偽値は,すべて真
• 0の真偽値は偽であるが,'0'という文字の真偽値は真
• 空文字''の真偽値は偽であるが,半角スペース' 'の真偽値は真
• int型とfloat型の数値の比較では,float型に型変換されて比較が行われるので,1==1.0の真偽値は真になる
※コンピュータで小数計算を行う場合には,有効桁数の限界があって,通常のソフトでは,1を3で割ってから3を掛けても,1には戻らない(0.9999999999999999)が,Pythonの場合は,1/3*3 == 1や1/7*7 == 1は真になる.mathモジュールをインポートして,円周率πや自然対数の底eで割ってから掛けても,同様である.どういう仕組みで計算が行われているのかは分からないが,この結論は頭の隅に置いておきたい.
• リストのリスト(二重のリスト),タプルのリストなどのように,イテラブルの各要素がイテラブルになっている場合,(*) にまとめたように個々の要素が, 空のリスト[], 空の辞書 {}, 空の集合 set(),空のタプル()に該当する場合はFalse,それ以外はTrueと考えれば返される結果を説明できる.
【例 2.4】
IDLE → New File → Save as .. → Run
L1 = [[], [0],[0,0]]
B1 = all(L1)
B2 = any(L1)
print(B1, B2)
False True
• []は空のリストだからFalse,[0]は空でないからTrue,[0,0]も空でないからTrue
 したがって,all()はFalseを返し,any()はTrueを返す.
L1 = [[[]], [(),()]]
B1 = all(L1)
B2 = any(L1)
print(B1, B2)
True True
• [[]]は[]でないからTrue,[(),()]も[]でないからTrue
 したがって,all()はTrueを返し,any()もTrueを返す.

〜危険な落とし穴〜
• Pythonの論理演算子and, orは,他の言語と違うところがあり,真偽だけでなく式(数値,文字列,リスト,タプルなど)も扱う.
• 例えば,L1= A and Bは,A,Bが真偽値の場合には,all(L1)と等しくなるが,A,Bが式の場合は,その式の値を返す.

(1) 比較演算子

• <, >, <=,>=, ==, !=, is, is not, in, not inは演算の結果を真偽で返すので,次のB1〜B9の式に対して,andで結んだものはall(リスト)と同じになり,orで結んだものは,any(リスト)と同じになる.
【例 2.5】
IDLE → New File → Save as .. → Run
B1 = (2 > 3)
B2 = (2 <= 4)
B3 = (3 > 0)
B4 = (1/3*3 == 1)
B5 = (1/3 != 0.333)
B6 = (1/2 is 0.5)
B7 = (1/2 is not 0.4)
B8 = (2 in range(3))
B9 = (5 not in range(5))
print(B1 and B2 and B3 and B4 and B5 and
 B6 and B7 and B8 and B9)
L1 = [B1,B2,B3,B4,B5,B6,B7,B8,B9]
print(all(L1))
print(B1 or B2 or B3 or B4 or B5 or B6 or
 B7 or B8 or B9)
print(any(L1))

False
False
True
True
• B1だけ偽で他は真だから,all(L1)はFalse,any(L1)はTrueになる

(2) not演算子

• 論理演算子のうちで,not演算子は後に来るものが真であればFalseを返し,偽であればTrueを返す.
(*) に述べたように,False, None, 0, 0.0, 複素数の零 0+0j, 空文字 '', 空のリスト[], 空の辞書 {}, 空の集合 set(),空のタプル()の真偽値は偽になるから,B1 = (not False),B2 = (not None),B3 = (not 0),B4 = (not 0.0),B5 = (not 0+0j),B6 = (not ''),B7 = (not []),B8 = (not {}),B9 = (not set()),B10 = (not ())などはTrueになる.
【例 2.6】
IDLE → New File → Save as .. → Run
B1 = (not True)
B2 = (not None)
B3 = (not 0)
B4 = (not 0.0)
B5 = (not 0+0j)
B6 = (not '')
B7 = (not [])
B8 = (not {})
B9 = (not set())
B10 = (not ())
print(B1 and B2 and B3 and B4 and B5 and 
B6 and B7 and B8 and B9 and B10)
L1 = [B1,B2,B3,B4,B5,B6,B7,B8,B9,B10]
print(all(L1))
print(B1 or B2 or B3 or B4 or B5 or B6 or 
B7 or B8 or B9 or B10)
print(any(L1))

False
False
True
True
• 先頭のB1のみFalseで他はTrueであるから,all(L1)はFalse,any(L1)はTrueになる.

(3) and演算子,or演算子

【ショートサーキット】
• and演算子は,A and Bとなっているときに,AもBも真であるとき,真を返し,少なくとも一方が偽であるとき,偽を返す.
• ただし,比較演算子やnot演算子とは異なり,AやBが真偽値でない場合,その値を返す仕様になっている.
• 例えば,Aが0, 0.0, 空文字 '', 空のリスト[]など真偽値が偽であるものであれば,Bを調べるまでもなく,A and Bは偽になる.この場合において,Bを調べる作業を省略することをショートサーキットといい,この場合には,真偽値ではなくAの値が返される.このようにしてA and B and C and ...となっているとき,先頭から調べて最初に偽となる値が登場すればその値が返され,全部真であれば最後の値が返される.
• 同様にして,Aが「0, 0.0, 空文字 '', 空のリスト[]など真偽値が偽であるもの」以外であれば,Bを調べるまでもなく,A or Bは真になるから,Bを調べるまでもなくAの値が返される.このようにしてA or B or C or ...となっているとき,先頭から調べて最初に真となる値が登場すればその値が返され,全部偽であれば最後の値が返される.
• ビルドイン関数のall()やany()は,上に示したように,True, Falseだけを返すように定義されているから,式の値を返すand, or演算子と同じではない.
【例 2.7】
IDLE → New File → Save as .. → Run
print(0 and 2.1)
print(1.3 and [] and 'a')
print(-1 and 'x' and () and 1.5)

0 → 0 and 2.1では,2.1を調べるまでもなく0が偽であるから,「その値」の0を返す.2.1は調べない.
[] → 1.3 and [] ..では,[]が偽であるから,「その値」の[]を返す.'a'は調べない.
()→ -1 and 'x' and () ..では,()が偽であるから,「その値」の()を返す.1.5は調べない.
【例 2.8】
IDLE → New File → Save as .. → Run
print(2.1 or 0)
print([] or 3 or 'a')
print(() or '' or {})

2.1 → 偽でない値2.1が登場したから,その値を返す.Trueを返すわけではない.0は調べない.
3 → 偽でない値3が登場したから,その値を返す.Trueを返すわけではない.'a'は調べない.
{}→ 偽でない値が登場しないので,最後まで調べる.最後の要素は空辞書で真偽値は偽であるが,この式の値{}を返す.」

3. bin(), oct(), hex(), int()関数

【書式】
bin(10進数の整数値)
oct(10進数の整数値)
hex(10進数の整数値)
• bin()関数は,10進数の整数値を2進数の文字列(0bで始まる表記になる…先頭は0[零],アルファベット大文字のOではない.以下,oct(),hex()でも同様)に変換する.oct()関数は,8進数の文字列(0oで始める表記になる)に変換する.hex()関数は,16進数の文字列(0xで始まる表記になる)に変換する.
• いずれも,ビルドイン関数のまま使う場合は,10進数の整数値のみ引数として渡すことができ,小数は渡せない.
• ただし,負の整数は単純にマイナスの符号を付ければよい.(アセンブリ言語などで用いられる補数表示で負の数をするのではなく,単純にマイナスを付ける.)
【書式】
int(整数を表す文字列, 基数)
• int()関数は,例えば '0b11' のように2進数を表す文字列が与えられたときに,それを10進数に変換する.
• 基数には,第1引数が何進数の数字であるかを2,8,16などと書く.(変換元の基数を書く.変換先ではない!)
• 例えば,2進数は0,1の2種類の数字だけを使って書き,2,3,..などは使わない.整数を表す文字列と基数に矛盾があればエラーになる.
• 例えば,変換元が16進数のように10以上の基数であるとき,各位の数はa,b,c,...のように小文字で書いても,A,B,C,...のように大文字で書いてもよい.
• int()関数に,文字列ではなく,浮動小数点数を渡した場合(基数なし),小数点以下を切り捨てた整数値を返す.
【例 3.1】
IDLE → New File → Save as .. → Run
n1 = 14
n2 = -17
print(bin(n1),oct(n1),hex(n1))
print(bin(n2),oct(n2),hex(n2))

0b1110 0o16 0xe
-0b10001 -0o21 -0x11
だから,bin(14)→ 0b1110
だから,oct(14)→ 0o16
だから,oct(14)→ 0xe

だから,bin(-17)→ -0b10001
だから,oct(-17)→ -0o21
だから,oct(-17)→ -0x11
【例 3.2】
IDLE → New File → Save as .. → Run
b1 = '0b1101'
o1 = '-0o107'
x1 = '0xab'
print(int(b1,2), int(o1,8),int(x1,16))
print(int('21',3),int('43',5), int('61',7))

13 -71 171
7 23 43
だから,int('0b1101',2)→ 13
だから,int('0o107',8)→ -71
 実際には,上記の例で,'0b', '0o', '0x'の接頭辞がなくても,第2引数の基数と矛盾なければ,変換は行われる.次の例のように,3進数,5進数,7進数から10進数への変換も行える.
だから,int('21',3)→ 7
だから,int('43',5)→ 23
だから,int('61',7)→ 43
【例 3.3】
IDLE → New File → Save as .. → Run
print(int(3.14), int(-2.1),int(3/2))

3 -2 1
• 3.14の小数部分を切り捨てると→ 3
• 負の小数x=−2.1の小数部分αとは何かという問題について,高校数学ではx=n+α (0≦α<1)となるように選ぶ(x=−3+0.9)が,この関数int()では,単純に−0.1を切り捨てて,整数部分を−2とする約束になっている.※数学の常識とは違うことに注意
• 3/2=1.5だから int(3/2)=1

• ビルドイン関数だけでは,n進数の小数を変換することはできない.次の関数は,n進数で与えられた小数を10進数に直す関数の試作品.ただし,コードを簡単にするため,小数点がない場合などのエラー処理はしていない.
n_to_decと書くべきところを,n2decと略した.(youをu,toを2などと略するのは,英語圏でよくある)
【試作品】
def n2dec(n_str, base):
    t1 = n_str.rpartition('.')
    n1 = int(t1[0], base)
    d1 = int(t1[2], base)/base**len(t1[2])
    return (n1+d1)
print(n2dec('123.1', 4))

27.25
• baseがn進数で与えられた小数,例えば4進数の小数123.1を10進数に直すには,
の整数部分は27,小数部分は0.25だから→ 27.25
• 文字列 '123.1' に対して,rpartition('.')メソッドを適用すると,区切り文字の左にある文字列,区切り文字,区切り文字の右にある文字列から成るタプル ('123', '.', '1') に分けられる.
• 整数部分はそのまま10進数に変換して使う.小数部分は,base**桁数により整数になるので,これを10進数に変換し,base**桁数で割ると小数に戻る.
• 一般に,m進数の整数からn進数に変換すると必ず整数になるが,m進数の小数をn進数に直すと,ほとんどの場合,無限小数になる.n進数の小数を10進数に変換したときに,有限小数になるのは,m=2,4,8,16, 5, 10,25,...など,基数がとなる場合に限られる.

4. divmod()関数

【書式】
divmod(割られる数, 割る数)
• 割られる数(被除数)a,割る数(除数)bとも10進数で書き,割り算の結果(a//b, a%b)を,(商, 余り)のタプルで返す.
• たとえば,14÷4 = 3余り2の場合,divmod(14,4) → (3, 2)の形になる.
• divmod(a, b)において,a, bが浮動小数点数の場合,商a/bは3.0のように整数値に.0を付けた形で返され,余りa%bは近似値になる.
【例 4.1】
IDLE → New File → Save as .. → Run
print(divmod(14,4))
print(divmod(-2.3, 2))
print(divmod(2.5, 1.2))

(3, 2)
(-2.0, 1.7000000000000002)
(2.0, 0.10000000000000009)
• 14÷4 = 3余り2だからdivmod(14,4) → (3, 2)になる.
• この計算で,負の数を正の数で割る場合,-2.3=2×(-2)+1.7のように,余りを正の数として求める.余りは正確に1.7になるはずであるが,小数第16位を丸めて表示される.
• 2.5=2.0×1.2+0.1すなわち,2.5÷1.2 = 2.0余り0.1になるはずであるが,小数第16位を丸めて表示される.
(参考)
• a÷b=q…rのとき,(q,r)すなわち(a//b, a%b)のタプルを返すのが divmod(a,b) である.
• ところで,2数の最大公約数を求める方法の1つとして,ユークリッドの互除法という計算方法があって,(a,b)の代わりに(b,r)を次々と求めて行って,rが0になったときのbが2数a,bの最大公約数になる.
• この方法で最大公約数を求めるには,divmod(a,b)のように関数の形で途中計算を行ってもよいが,単にタプル(b, a%b)を返してもよい.次のgcd1(a,b)は,2数a,bの最大公約数を再帰型で求めるもの,gcd2(a,b)はループ型で求めるものである.(「C言語によるアルゴリズム事典」奥村晴彦著/技術評論社P.81参照)
【例 4.2】
IDLE → New File → Save as .. → Run
def gcd1(a1, b1):
    if b1 == 0:
        return a1
    else:
        return gcd1(b1, a1% b1)
def gcd2(a1, b1):
    while b1 != 0:
        (a1, b1) = (b1, a1 % b1)#(*)
    return a1

12=22×3, 18=2×32だから,12と18の最大公約数は6になる.これを,上記のプログラムで確かめるには,print(gcd1(18,12))とすればよい.
もっと大きな数字では,
例  1763=41×43 , 1927=41×47 → G=41
例  152963=1013×151 , 783049=1013×773 → G=1013
例  6171373=532×133 , 1513733=53×134
    → G=53×133=116441
などで確かめることができる.
#(*)のところは,タプルを代入すると2つの値を同時に変えられることを使う.( )がなくてもタプルだから,a1, b1 = b1, a1 % b1と書いてもよい.

5. float()関数

【書式】
float(10進数と見なせる数値,または文字列)
整数や文字列で与えられた引数を浮動小数点数に変換することが主な働き.
• 引数として渡せる記号は,原則として,10進数を表すための「+, -の符号,0〜9までの整数,小数点」の組み合わせだけ.
を5.203e3と書く,0.000071を7.1e-5と書くように指数表示を表すeまたはEの文字は,この形式に沿っていれば使える.
• [意外なもの]++3.1, ---5.2, 1.2++++3.5のように数値の前(または間)に符号が何個あってもよい.
• [ダメなもの]a,b,..,x,y,zのように,10進数に登場しない記号を含むものは使えない.小数点が2個以上含まれる表現,カンマ(,),コロン(:),セミコロン(;),リスト,タプルなどを含むものはダメ.
• [例外的なもの」無限大inf, infinity(∞)や非数nan(Not A Number)など数値に関係する記号も使える.
【例 5.1】
IDLE → New File → Save as .. → Run
f1 = float(5)
f2 = float(-3.1)
print(f1,f2)
5.0 整数は小数に変換される
-3.1 小数はそのまま
【例 5.2】
IDLE → New File → Save as .. → Run
f5 = float(3+1.2)+float(4.1-5.4)
f6 = float(2/7)
print(f5,f6)
2.8999999999999995 float()の結果は数値だから,結果を足したり引いたりできる(2.9)
0.2857142857142857 分数は小数に直る(ただし,近似値)
【例 5.3】
IDLE → New File → Save as .. → Run
f8 = float('12.3')
f9 = float('-23.5')
f10 = float('4.1e3')
f11 = float('0.3e-3')
print(f8, f9, f10, f11)
12.3 10進数と見なせる文字列は小数になる
-23.5 負の小数
4100.0 
0.0003 理科や数学では,有効数字×指数表示で書くときは,1≦有効数字<10を使うが,float()の途中経過では,この書き方も許される..ただし,有効数字を省略したfloat('e-3')はダメ
【例 5.4】
IDLE → New File → Save as .. → Run
f3 = float(++2.1)
f4 = float(4.3---3.1)
print(f3,f4)
2.1 符号の+は何個書いても,値を変化させない
1.1999999999999997 1.2ということ.4.3の後にある1つのマイナスは引き算の演算で,他はマイナスが付くたびに符号が変わる:4.3-(-(-3.1))=4.3-3.1=1.2
【例 5.5】
次の式はいずれもエラーとなる
IDLE → New File → Save as .. → Run
f12 = float('1,200')
f13 = float('1:2')
f14 = float('2;3')
f15 = float('3=2')
f16 = float([2,3])
f17 = float((2,3))

【例 5.5】
IDLE → New File → Save as .. → Run
f1 = float('inf')
f2 = float('-inf')
f3 = float('infinity')
f4 = float('nan')
print(f1,f2,f3,f4)
print(1/f1, 1/f2, 1/f3, 1/f4)
print(f1*2)
inf -inf inf nan 無限は'inf','infinity'のいずれも可
0.0 -0.0 0.0 nan などは,形式的に満たされる.
inf などは,形式的に満たされる.
ただし,少し込みいった計算になると対応していない:はnan

6. globals(), locals()関数

【書式】
globals()
• 引数なしで呼び出す.グローバル空間にある変数の名前と値,関数の名前とメモリアドレスの辞書を返す.
locals()
• 引数なしで呼び出す.ローカル空間にある変数の名前と値,関数の名前とメモリアドレスの辞書を返す.
※ これらの関数とよく似た働きをする関数として,vars(オブジェクト), dir(オブジェクト)があるが,ここでは取り上げない.
• まず,グローバル空間とローカル空間を単純化すれば,左図のようになるでしょう.
• グローバル空間では,グローバル変数の読み書きと関数の呼び出しはできるが,ローカル空間にある変数は読むことも書くこともできない.
• ローカル空間からはグローバル空間にある変数は読めるが,原則として(global宣言などしてない限り)変更はできない.
※しかし,詳しく見ると,もう少し込み入っています.(以下の例の解説の通り)
【例 6.1】
IDLE → New File → Save as .. → Run
str1 = 'abc'
n1 = 12
L1 = [1,2,3]
#1
def fun1(a1, b1):
    x1 = a1
    y1 = b1
f1 = fun1(3,4)
#2
n2 = 13
#3
• globals()が表す名前空間の辞書{変数名:値, ...}は,その時点で読み込まれたものを示し,#1においてglobals()を呼び出すと,システムが使用している __name__ の形のダブルアンダースコアで挟まれたオブジェクトの他,'str1': 'abc', 'n1': 12, 'L1': [1, 2, 3]の3つのグローバル変数がある.#1で読み書き可能な変数は,これだけであることが分かる.
• これに対して,#2でglobals()を呼び出すと,#1のときと比べて,'fun1': 'lt;function fun1 at 0x*******>という{関数名:そのメモリアドレス},および 'f1': Noneという{名前:値}が増える(関数fun1(a1, b1)には戻り値を設定していないので,f1の値はNoneになる).
• #3でglobals()を呼び出すと,さらに,'n2': 13という要素が辞書に追加される.

≪要点1≫
 globals()は,global空間に存在する変数や関数の全部を表しているのではなく,プログラムを前から順に読み込んでいったときに,そのglobals()までに読み込まれた変数や関数の{名前:値(関数定義の場合はそのメモリアドレス)}を表す.
【例 6.2】
IDLE → New File → Save as .. → Run
n1 = 1
if 1+1 == 3:
    n2 = 5
print(globals())
• 変数n2はglobals()の呼び出しよりも前に記述されているが,1+1は3でないから,実際には呼び出されておらず,globals()を呼び出すと,システムが使用している __name__ の形のダブルアンダースコアで挟まれたオブジェクトの他には,{'n1':1}だけが表示される.
• 上記の要点1は実行時に言えることであるが,次の例6.3は,IDLEの中で文法チェックに引っかかり,そもそも実行できない.
【例 6.3】
IDLE → New File → Save as .. → Run
str1 = 'abc'
n1 = 12
L1 = [1,2,3]
def fun1(a1, b1):
    x1 = a1
    y1 = b1
n2 = 5
global n1

• global空間に書かれる変数は,黙っていてもglobal変数として扱われる.
• global変数に,global宣言を付けても矛盾はないが,同じ空間の中で(=同一のコードブロックの中で)global宣言よりも前に,使用することはできない.
※上記の例6.1,6.2において,glovals()関数の代わりにlocals()関数を使っても,全く同じ結果が得られる.
 すなわち,locals()が示すものは,「そのlocals()関数g呼び出された空間」ということなので,locals()関数がグローバル空間で呼び出されたら,グローバル空間の変数と関数の辞書が示される.
【例 6.4】
IDLE → New File → Save as .. → Run
str1 = 'abc'
n1 = 56
def fun1(a1, b1):
    print(locals())#1
    x1 = a1
    y1 = b1
    print(locals())#2
fun1(3,4)

{'a1': 3, 'b1': 4}
{'a1': 3, 'b1': 4, 'x1': 3, 'y1': 4}
• 関数は定義されただけでは実行されず,この例ではfun1(3,4)のように呼び出された所で,実行される.
• fun1(3,4)を実行するときに,#1の箇所では,まだローカル変数x1, y1は登場しておらず,仮引数a1,b1のみがローカル変数として示される.
• #2の箇所では,a1,b1,x1,y1がローカル変数として示される.

≪要点2≫
• 関数の中では,仮引数もローカル変数になる.
【例 6.5】
IDLE → New File → Save as .. → Run
str1 = 'abc'
n1 = 56
def fun1(a1, b1):
    x1 = a1
    y1 = b1
    print(globals())#3
fun1(3,4)

#3の箇所で,globals()関数を呼び出すと,グローバル変数str1, n1の{変数名:値}の辞書,およびグローバルな関数fun1:そのメモリアドレスが示される.
'str1': 'abc', 'n1': 56, 'fun1': <function fun1 at 0x*******>}

7. 自由変数,束縛変数

• [原則]
 関数の中で定義された変数は,ローカル変数と呼ばれ,その関数の中だけで参照でき,その関数の外からは参照できない.
 関数の外部にあってプログラムのメインになる部分(トップレべルのコーディングブロック)で定義された変数は,グローバル変数と呼ばれ,そのモジュール(Pythonファイル)のどこからでも参照できる.
 このように,グローバル変数,ローカル変数の区別は,変数が定義された場所(空間)を表しており,「どこから見たら」というような相対的な関係ではない…ここで,相対的な関係とは,例えばAに2人の子供B,Cがいて,Bの子供がDであるとき,AはCの親,DはAの孫,BとCは兄弟姉妹というように見る位置によって変わる関係をいう.
• [自由変数]
 それぞれの関数の内部から利用できるローカル変数のうちで,その関数の外部で定義されたものを自由変数という.(グローバル変数は,どの関数の内部からでも利用できるが,これは自由変数とは言わない.また,それぞれの関数の内部で定義されたローカル変数は,その関数の内部で利用できるが,これは束縛変数と呼ばれる.)
 関数の定義が入れ子になっている場合(関数の中で関数が定義されている場合)に,自由変数ができる.(次の例参照)
【例 7.1】
IDLE → New File → Save as .. → Run
n0 = 0
def fun1():
    n1 = 1
    print(n0,n1)#1
    def fun2():
        n2 = 2
        print(n0,n1,n2)#2

0 1
0 1 2
#1では,n0はグローバル変数で参照可能,n1は束縛変数で参照可能
#2では,n0はグローバル変数で参照可能,n1は自由変数で参照可能,n2は束縛変数で参照可能
このように,n1はローカル変数であるということは言えるが,n1が自由変数であるか,束縛変数であるかは「見る場所によって変わる」
≪要点3≫
外部→親関数→子関数→孫関数のように入れ子になっている場合
== 読み ==
•(1) 下位の関数の中から外部および(直系の)上位に属するすべてのローカル変数が読める.
•(2) 外部または上位の関数の中から下位に属するローカル変数は読めない.
== 書き ==
•(3) 各関数の中でglobal宣言を行えば,global変数を書き換え(代入)できる.グローバルに定義されていない変数でも,関数の中からglobal宣言すれば,グローバル変数を作ることができる.
•(4) 各関数の中でnonlocal宣言を行えば,(直系の)1つ上位の関数に属するローカル変数を書き換え(代入)できる.上位の関数にない変数を下位の関数の中からnonlocal宣言によって作ることはできない.
== 何もできない ==
•(5) ある関数から見て,上位であっても直系の上位でない関数のローカル変数は,読みも書きもできない.
•(6) ある関数から見て,兄弟姉妹(直系の親の別の子)である関数のローカル変数は,読みも書きもできない.
(解説)
•(1)は上記の例7.1の通り
•(2)は次の例7.2の通り
【例 7.2】
IDLE → New File → Save as .. → Run
n0 = 0
def fun1():
    n1 = 1
    def fun2():
        n2 = 2
        return
    #1
    return
#0
 関数fun1()の外部#0において,変数n1を読もうとするとエラーになる.(is not defined)
 関数fun1()の内部かつ関数fun2()の外部#1において,変数n2を読もうとするとエラーになる.(is not defined)
•(3) 次の#1において,global n0と宣言した後,n0 = 2のように代入すれば,グローバル変数n0の値を変更できる.
 グローバル宣言をせずに,n0 = 2のように代入すれば,新たなローカル変数n0が定義されたことになり,グローバル変数n0の値0は変化しない.
 #1において,n0のグローバル宣言をしなくても,print(n0)のようにn0を読むと,グローバル変数を読み出すことができる.(1)
次にそのn1にn0 = 2のように代入すると,n0はグローバル変数なのかローカル変数なのか,矛盾を生ずる.実際には「後でn0 =2 と書いてあれば,先のprint(n0)において,ローカル変数のとり扱いになり,未定義の変数を使ったというエラーになる」.関数の中でのglobal宣言は,変数の読み出しや代入などが行われる前に行われなければならない.読み書きを行ってから,global宣言を行うとエラーになる.
• #2において,global n1と宣言しても,fun1()を飛び越えてグローバル変数を読み書きできる.
• #1,#2において,本来のグローバル変数に定義されていないn2, n3のような変数を新たにglobal宣言して代入操作を行うと,以後,どこからでもそのグローバル変数を読み書きすることができる.(グローバルの空間,関数内部のローカルな空間のいずれにおいてもglobal宣言を行うことはできるが,宣言だけして代入操作が行われなければ,名前空間に残らない)
【例 7.3】
IDLE → New File → Save as .. → Run
n0 = 0
n1 = 1
def fun1():
    #1
    def fun2():
        #2
        return
    fun2()
    return
fun1()

•(4) 次の例において,fun2()の中でn1のnonlocal宣言がなされているから,fun1()のローカル変数n1はfun2()の自由変数になり,書き換えることができる.
【例 7.4】
IDLE → New File → Save as .. → Run
n0 = 0
def fun1():
    n1 = 1
    def fun2():
        nonlocal n1
        n1 = 2
    fun2()
    print(n1)
    return
fun1()

8. id()関数

• id(オブジェクト)は,変数,関数などのオブジェクトに固有の識別値(ID値)を10進数の整数で返す.ローカル変数のようにスコープ(有効範囲,寿命)が限られているオブジェクトは,それが有効である範囲でid()関数が使える.
• ID値は,そのオブジェクトが格納されているメモリアドレス(通常16進数で表される)を10進数で表したものになっている.
• 比較演算子(==)は,2つのオブジェクトの型(type)と値が同じであればTrueを返すのに対して,is演算子は2つのオブジェクトが同じものであるときTrueを返す.したがって,
id(オブジェクト1) == id(オブジェクト2)
オブジェクト1 is オブジェクト2
とは真偽が一致する.
【例 8.1】
IDLE → New File → Save as .. → Run
x1 = 3
str1 = 'abc'
def fun1():
    y1 = 5
    return y1
f1 = fun1()
print(id(x1),id(str1),id(fun1),id(f1))
→ この形で,それぞれの変数,関数のID値(その時の実行環境で割り当てられたメモリアドレス)が分かる.全く同じプログラムでも,1秒後にもう一度実行したとき,同じID値になるとは限らない.
【例 8.2】
IDLE → New File → Save as .. → Run
x1 = 3
x2 = 4
print(id(x1),id(x2))#1
x2 = x1
print(id(x1), id(x2))#1
→ この形で行うと,#1ではx2のID値がx1よりも16だけ後ろになる.(int型は一連の場所に配置されるようだ)
• #2では同じID値になる.
 x2 = x1とは,変数x2に変数x1の値を代入するということだから,値が等しくなるのは納得できるが,ID値は「それぞれのオブジェクトに固有の識別子」ではなかったのか?識別子が同じになったら,区別できないのではないのか?
 次のイメージ図で考える.
 変数x1,x2には,参照先オブジェクトのアドレスが書かれている.
 代入文x2 = x1を実行すると,x2の参照先が変わる.
 これにより,変数x1,x2は同じオブジェクトを参照することになる.
 「それぞれのオブジェクトには固有の識別子がある」ということには,間違いはなく,それを参照する側は,同じオブジェクトを参照することもある.
【例 8.3】
IDLE → New File → Save as .. → Run
x1 = 3
def fun1():
    y1 = 4
    return y1
f1 = fun1()
print(id(x1),id(fun1), id(f1))
print(globals())
print(hex(id(fun1)))
• x1はint型,fun1()の戻り値f1もint型だから,id(x1), id(f1)は連番(16だけ大きい).
• globals()によって,関数定義fun1はメモリアドレスが示される.このメモリアドレスは16進数で書かれている.そこで,id(fun1)を16進数に直せばhex(id(fun1))globals()の示すメモリアドレスと一致することが分かる.

9. type()関数

• type(オブジェクト)のように書くことにより,そのオブジェクトの型が<class 'int'>などの形で返される.(Python 3)
【例 8.2】
IDLE → New File → Save as .. → Run
def fun1():
    pass
print(type(3), type('ab'),type([4,5]),type((6,7)),type({1:'a'}))
print(type(fun1))
<class 'int'> <class 'str'> <class 'list'> <class 'tuple'> <class 'dict'>
<class 'function'>
○== メニューに戻る ==