乙Py先生のプログラミング教室
初学者のためのプログラミング学習サイト

python

                 33.global変数とスコープ

関数などを使う際に必ず付いてくるのが、スコープという概念です。
これは宣言した変数が使える範囲を示す概念のことです。

スコープには
グローバルとローカルの2種類があります。

ざっくり説明すると、一番外にあるものがグローバル
関数のブロックの中がローカルということになります。

ではその違いについて見て見ましょう。
まずはグローバルで使える変数を用意します。

In [1]:
global_var = 'global'

次に関数を定義して、その中で使える変数を用意します。

In [2]:
def local_def():
    # ローカル用の変数
    local_var = 'local'
    return local_var

グローバルの変数の呼び出しは

In [3]:
print(global_var)
global

ローカル変数の呼び出しは

In [4]:
print(local_var)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-4-38c61bb47a8e> in <module>()
----> 1 print(local_var)

NameError: name 'local_var' is not defined

変数が定義されていないと、エラーが出てしまいます。

ローカルで定義したものは、
そのローカルのブロックの中でしか使えないからです。

なのでこのlocal変数が使えるのは、
定義した関数の中だけになります。

次に関数をいじって、グローバル変数を関数の中で読んでみましょう。

In [5]:
def local_def():
    # グローバル変数の参照
    print(global_var)

local_def()
global

グローバルで宣言した変数はどこでも使用することができます。

ローカルで宣言したものを
グローバルで使うにはどうすればいいでしょうか?

関数であれば戻り値として
グローバルの方に戻すことで使い回すことができます。

In [6]:
def local_def():
    local_var = 'local'
    # ローカルで定義した変数を戻す
    return local_var

g2 = local_def()
print(g2)
local

for文の方も見て見ましょう。

In [7]:
global_var = 'g'

for i in range(5):
    j = global_var*i
    
# ローカルの変数をそのままプリントする
print(i,j)
4 gggg

結果は最終的に代入された結果のみが反映されることになります。

for文のブロックで使用していた変数などを
気づかずに再利用してしまうケースなどがありえると思いますが、
変数に格納した値によってはプログラムの結果に大きく影響が出てきます。

最後に、グローバルとローカルで変数名がかぶってしまった場合どうなるか

In [8]:
sp = 'global'

def local_def():
    sp = 'local'
    print(sp)

local_def()
print(sp)
local
global

一旦ローカルで同じ変数名で定義していますが
関数を呼び出した際はその関数で定義した変数名で
上書きされる形になるのでローカルで代入した値が表示されます。

その後グローバルの変数を呼び出す際には
元の代入された値が表示されるという仕組みです。

global という予約語を用いると、
このグローバルの変数をローカル側で操作することできます。

In [9]:
sp = 'global'

def local_def():
    # ローカルでグローバル変数として定義
    global sp 
    sp= 'local'
    print(sp)

local_def()
print(sp)
local
local

一旦ローカルで変数の前にglobalをつけて宣言し、
その後に変数に代入するとグローバルの変数の操作をすることができます。

グローバルと、ローカルで同じ変数を用いると、
不慮のバグの元になりやすいです。

なので慣れないうちは変数名はなるべく被らないような
名称にすることをお勧めします。

                 32.関数

プログラミング言語では
一定の処理を一まとめにしたものを「関数」と読んでいます。

関数の作り方

def 関数名(引数):
    return 戻り値

関数を定義して見ましょう。

In [1]:
# 関数の定義
def abc(aaa=0):
    print(aaa)
    return 1

# 関数の実行
print(abc())
print(abc(2))
0
1
2
1

引数の変数名の後にイコールをつけて値を代入しておくと、
引数が指定されなかった場合に、初期値として使われる値になります。

戻り値は関数の実行結果です。
変数に格納したりする事もできます。

In [2]:
# 関数の実行結果が変数に格納される
a = abc(3)
print(a)
3
1

戻り値や引数は設定しなくても関数として定義できます。

In [3]:
def defg():
    print('dd')

defg()
dd

特に値を戻す必要がなければ
return 無しで戻り値をなくしても大丈夫です。

関数の結果を用いて次に処理を行いたい場合は
return を付けて値を戻すようにします。

引数は何個でもつけることができますが、
あまり多すぎると使うときに困ると思います。

なので5-6個くらいに納めた方が良いかと思います。
それ以上になるようであれば処理を改めたほうが良いでしょう。

関数を定義するときに引数を定義したら、
使うときもその引数の分だけ
指定することが必要になりますが、
関数によっては引数で処理を
変えたりしたいというニーズもあったりします。

そんな時は引数に  **をつけると
引数をタプル型や辞書型で受け取ることができます。

In [4]:
def aaa1(**karg):
    print(karg)

# 関数の引数に辞書型を渡す
aaa1(aws=[1,2])
{'aws': [1, 2]}
In [5]:
def aaa2(arg1, arg2, arg3):
    print(arg1)
    print(arg2)
    print(arg3)
    
lis = ['one', 'two', 'three']
# 関数の実行にリストをタプル型にして渡す。
aaa2(*lis)
one
two
three

ただしこの場合は引数の数と合わなければなりません。

In [6]:
lis2 = ['one', 'two', 'three','four']
# 引数の数と渡す数が合わ無いとエラーになる。
aaa2(*lis2)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-6-545521add84a> in <module>()
      1 lis2 = ['one', 'two', 'three','four']
      2 # 引数の数と渡す数が合わ無いとエラーになる。
----> 3 aaa2(*lis2)

TypeError: aaa2() takes 3 positional arguments but 4 were given

関数の使い方としては、複数行にまたがる処理が
2回以上行われる場合は、関数化していった方が
コードがスッキリして見やすく、
メンテナンスもしやすくなります。

同じような処理を2度書くくらいであれば、全体を見直して
簡略化、省力化ができる部分を書き直してゆく、という感じで
関数を作っていくのが良いと思います。

関数は別名メソッドとも読んでいます。
プログラミングでは必須になる概念のため
必ず覚えておいてください。

                31.基礎演習2

復習をしながら問題を解いてきましょう。!!

ここまででやったこととしては・・
制御文や内包表記、各種組み込み関数やリストなどのデータ型についてです。

もしわからなければ、今までの講義の動画をもう一度見てみましょう。

In [ ]:
 

基礎演習問題1:

リスト型の変数を宣言してみよう(要素は任意で3つ以上)

In [ ]:
 

基礎演習問題2:

文字列型のキーと数値の値を持つ
辞書型の変数を作成してみよう(要素は任意で3つ以上)。

In [ ]:
 

基礎演習問題3:

辞書型の変数を作成し、要素(キー:値)を追加してみよう

In [ ]:
 

基礎演習問題4:

IF - ELSE文を用いた処理を作成しよう
条件は整数型の変数aの値が
偶数の場合 : 「偶数」とプリント
奇数の場合 : 「奇数」とプリント

In [ ]:
 

基礎演習問題5:

for 文で enumerate関数とrange関数を用いて
その2つの値を表示(print)させよう。
range関数の引数は10とする。

In [ ]:
 

基礎演習問題6:

リスト型の変数に3つの整数値を入れて、その最大値を出力する。

In [ ]:
 

基礎演習問題7:

次の文章
「浦和、南浦和、北浦和、東浦和、西浦和、武蔵浦和、中浦和、浦和」

これを「、」で分割してキーとして辞書型の変数に格納したい。
値は空白「」とする。

In [ ]:
 

基礎演習問題8:

「無駄」を10個要素にもつリスト型を作成したい。
内包表記を使ってうまく書けないだろうか?

In [ ]:
 

基礎演習問題9:

九九は1から9までの数字同士を掛け合わせた結果になる。
これをfor文を用いて表示してみよう。
なお、文字として表示させることとし、桁も揃えよう。

In [ ]:
 

基礎演習問題10:

第9問、これを内包表記で1行でうまく書くことはできないだろうか?
色々な書き方があると思うので試してみよう。

In [ ]:
 

すぐに答えが出ない人は、動画を止めて考えてみましょう。

コツとしては何を入力したら、どう計算されて、どう出力されるのか
それを考えながら書いてみましょう。

答えはこの下に





回答

基礎演習問題1:

リスト型の変数を宣言してみよう(要素は任意で3つ以上)

In [1]:
# リストは[]囲んで定義する
a = [1,2,3,4,5]

基礎演習問題2:

文字列型のキーと数値の値を持つ
辞書型の変数を作成してみよう(要素は任意で3つ以上)。

In [2]:
# 辞書型は{} 波カッコで定義する
d = {'a':1 , 'b':2 , 'c':3}

基礎演習問題3:

辞書型の変数を作成し、要素(キー:値)を追加してみよう

In [3]:
d = {'a':1 , 'b':2 , 'c':3}
# 辞書の要素の追加は変数名[キー] = 値
d['d'] = 4
print(d)
{'c': 3, 'd': 4, 'a': 1, 'b': 2}

基礎演習問題4:

IF - ELSE文を用いた処理を作成しよう
条件は整数型の変数aの値が
偶数の場合 : 「偶数」とプリント
奇数の場合 : 「奇数」とプリント

In [4]:
a = 10
if a%2==0:
    print('偶数')
else:
    print('奇数')
偶数

基礎演習問題5:

for 文で enumerate関数とrange関数を用いて
その2つの値を表示(print)させよう。
range関数の引数は10とする。

In [5]:
# enumerate の結果を1つの変数に格納する。
for i in enumerate(range(10)):
    print(i)
(0, 0)
(1, 1)
(2, 2)
(3, 3)
(4, 4)
(5, 5)
(6, 6)
(7, 7)
(8, 8)
(9, 9)
In [6]:
# enumerate の結果を2つの変数に格納する。
for i,r in enumerate(range(10)):
    print(i , r)
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9

基礎演習問題6:

リスト型の変数に3つの整数値を入れて、その最大値を出力する。

In [7]:
a = [10,20,30]
print(max(a))
30

基礎演習問題7:

次の文章
「浦和、南浦和、北浦和、東浦和、西浦和、武蔵浦和、中浦和、浦和」

これを「、」で分割してキーとして辞書型の変数に格納したい。
値は空白「」とする。

In [8]:
a = '浦和、南浦和、北浦和、東浦和、西浦和、武蔵浦和、中浦和、浦和'
# split関数で 、 で区切る。
b = a.split('、')
# 内包表記で辞書型にする
d = {k:'' for k in b}
print(d)
{'武蔵浦和': '', '中浦和': '', '東浦和': '', '浦和': '', '南浦和': '', '北浦和': '', '西浦和': ''}
In [9]:
a = '浦和、南浦和、北浦和、東浦和、西浦和、武蔵浦和、中浦和、浦和'
# split関数で 、 で区切る。
b = a.split('、')
# 内包表記を使わ無い場合
d = {}
for k in b:
    d[k]=''
print(d)
{'武蔵浦和': '', '中浦和': '', '東浦和': '', '浦和': '', '南浦和': '', '北浦和': '', '西浦和': ''}

基礎演習問題8:

「無駄」を10個要素にもつリスト型を作成したい。
内包表記を使ってうまく書けないだろうか?

In [10]:
muda = ['無駄' for i in range(10)]
print(muda)
['無駄', '無駄', '無駄', '無駄', '無駄', '無駄', '無駄', '無駄', '無駄', '無駄']

基礎演習問題9:

九九は1から9までの数字同士を掛け合わせた結果になる。
これをfor文を用いて表示してみよう。
なお、文字として表示させることとし、桁も揃えよう。

In [11]:
for i in range(1,10):
    for j in range(1,10):
        # 2桁表記にフォーマットする , 改行コードをなくす
        print('{:02}\t'.format(i*j), end='')
    print() # 改行のみを行う
01	02	03	04	05	06	07	08	09	
02	04	06	08	10	12	14	16	18	
03	06	09	12	15	18	21	24	27	
04	08	12	16	20	24	28	32	36	
05	10	15	20	25	30	35	40	45	
06	12	18	24	30	36	42	48	54	
07	14	21	28	35	42	49	56	63	
08	16	24	32	40	48	56	64	72	
09	18	27	36	45	54	63	72	81	

基礎演習問題10:

第9問、これを内包表記で1行でうまく書くことはできないだろうか?
色々な書き方があると思うので試してみよう。

In [12]:
# 1段分の文字列を9つ持つリスト , 再度 join関数を使って リストを文字に、改行で連結
print('\n'.join([''.join(['{:02}\t'.format(i*j) for i in range(1,10) ])for j in range(1,10)]))
01	02	03	04	05	06	07	08	09	
02	04	06	08	10	12	14	16	18	
03	06	09	12	15	18	21	24	27	
04	08	12	16	20	24	28	32	36	
05	10	15	20	25	30	35	40	45	
06	12	18	24	30	36	42	48	54	
07	14	21	28	35	42	49	56	63	
08	16	24	32	40	48	56	64	72	
09	18	27	36	45	54	63	72	81	

さて基礎演習はいかがだったでしょうか?
処理を1つ1つ追いながらコードを書くことで、問題が解けるように
なっていくのではないかと思います。

仕事などで九九を使うことはないと思いますが
プログラムを組み立ててゆくことを覚えるのには
最適な題材です。

上記で紹介した解法以外にも書き方はあると思いますので
色々な解き方を試して見てください。

分からなかった方は、文字列やフォーマットの回、繰り返しの回などを
参考にしていただいて、理解を深めていただければ次第に解けるようになるかと
思います。

                 30.内包表記

内包表記はpython独特の書き方で
一定の処理を簡素に書くための手法となります。

内包表記の書き方

リスト:[counter for counter in iterator] 
辞書 :{key:value for key,value in iterator} 
セット:{counter for counter in iterator}

内包表記を使ってリストを作る

In [1]:
# 内包表記で変数にリスト型を格納する
lis1 = [i for i in range(10)]
print(lis1)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

内包表記を使わない場合は

In [2]:
lis2 = []
for i in range(10):
    lis2.append(i)
print(lis2)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

内包表記は複数行にわたる処理を1行にまとめる事ができる。

内包表記を使って辞書を作る

In [3]:
# 内包表記でキーをenumerate , 値をrangeの結果として辞書型を作成
num_dict = {k:v for k,v in enumerate(range(2,30,3))}
print(num_dict)
{0: 2, 1: 5, 2: 8, 3: 11, 4: 14, 5: 17, 6: 20, 7: 23, 8: 26, 9: 29}

keyとvalueに該当するものが有れば辞書型を作成可能。

内包表記はIF文を用いることもできます。

内包表記 + IF文

リスト:[value for counter in iterator if conditions]

辞書 :{key:value if conditions for key,value in iterator}

内包表記 + IF , else文

リスト:[value if conditions else value for counter in iterator]

辞書:{k1 if conditions else k2 : v1 if conditions else v2 for key:value in iterator}

In [4]:
# リスト内包表記 + IF文
num_list = [i for i in range(15) if i%2==0]
print(num_list)
[0, 2, 4, 6, 8, 10, 12, 14]
In [5]:
# リスト内包表記 + IF , else文
num_list = [i if i%2==0 else 0 for i in range(15) ]
print(num_list)
[0, 0, 2, 0, 4, 0, 6, 0, 8, 0, 10, 0, 12, 0, 14]
In [6]:
# 辞書内包表記 + IF文
num_dict = {k:v for k,v in enumerate(range(2,30,3)) if k%2==0}
print(num_dict)
{0: 2, 8: 26, 2: 8, 4: 14, 6: 20}
In [7]:
# 辞書内包表記 + IF , else文
num_dict = {k if k%2==0 else 0 : v if k%2==0 else 0  for k,v in enumerate(range(2,30,3)) }
print(num_dict)
{0: 0, 8: 26, 2: 8, 4: 14, 6: 20}

辞書型はenumerateを用いずにzip関数でリスト2つを用いても作る事ができる。

In [8]:
lis_a = [1,2,3,4,5]
lis_b = ['a','b','c','d','e']
d2 = {k:v for k,v in zip(lis_a,lis_b)}
print(d2)
{1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e'}

リスト内包表記から辞書を作成する事もできます。

In [9]:
# リスト内包表記から辞書化
num_dict = dict((str(i),i) if i%2==0 else (i,str(i)) for i in range(10))
print(num_dict)
{9: '9', 1: '1', 3: '3', '8': 8, 5: '5', '0': 0, '2': 2, '4': 4, '6': 6, 7: '7'}

for文は複数書くことができます。

In [10]:
# 2重の内包表記
print([i*j for i in range(1,5) for j in range(1,4) ])
[1, 2, 3, 2, 4, 6, 3, 6, 9, 4, 8, 12]

これを通常のfor文で書いて見ましょう。

In [11]:
a5 = []
for i in range(1,5):
    for j in range(1,4):
        a5.append(i * j)
print(a5)
[1, 2, 3, 2, 4, 6, 3, 6, 9, 4, 8, 12]

後に書いた方が内側のfor文に該当します。

このように複数行に渡るものも1行でスッキリ書くことができるのが
内包表記の良いところです。

まずは通常の処理を正しく書くことから始め、
それを簡略化できる部分があれば、内包表記に変えてゆく
といった風にすると、上手に覚えることができると思います。

                 29.ソート

ソートはデータを並べ替えを行います。

プログラムの中ではかなり多く用いられる処理です。

文字列型の並び替え

インデックスを用いて文字列を逆さまにすることができます。

In [1]:
a1 = 'abcdeアイウエオ123幹事!'
# [::-1]で逆さまにする
print(a1[::-1])
!事幹321オエウイアedcba

文字列内で使われている文字を並び替えるには
一旦リスト型に変換してから並び替えし、文字列に戻すことで
文字列の並び替えを実現できます。

sorted関数を使うと、文字列を分割して並び替えを行います。
第二引数のreverseにTrueを指定すれば降順
Falseを指定するか第二引数なしの場合は昇順となります。

In [2]:
a2 = 'abcdeアイウエオ123幹事!'
# sorted関数でリストに変換して昇順ソート
print(sorted(a2,reverse=False))

# sorted関数でリストに変換して降順ソート
print(sorted(a2,reverse=True))
['!', '1', '2', '3', 'a', 'b', 'c', 'd', 'e', 'ア', 'イ', 'ウ', 'エ', 'オ', '事', '幹']
['幹', '事', 'オ', 'エ', 'ウ', 'イ', 'ア', 'e', 'd', 'c', 'b', 'a', '3', '2', '1', '!']

join関数を用いるとリスト型のものを1つづつ連結して
文字列にすることができます。

In [3]:
a3 = 'abcdeアイウエオ123幹事!'

print(''.join(sorted(a3 , reverse=True)))
幹事オエウイアedcba321!

あまり文字列だけでの並び替えは
使う機会がないかもしれませんが、いざという時に役立つ手法です。

リスト型のソート

リスト型の関数であるsort関数を使う方法

昇順:
変数名.sort()

降順:
変数名.sort(reverse=True)

sorted関数を用いる方法

昇順:
sorted(変数名)

降順:
sorted(変数名,reverse=True)

※sorted関数は元のリストの並び順に影響しないが
sort関数はリストの並び替えを行い、リスト型の並び順が変わる。

In [4]:
lis1= [3,1,2,4,5,7,6]
print(lis1)

# 昇順ソート
print(sorted(lis1))

# sorted関数の後に再度呼び出しても順番は変わら無い
print(lis1)
[3, 1, 2, 4, 5, 7, 6]
[1, 2, 3, 4, 5, 6, 7]
[3, 1, 2, 4, 5, 7, 6]
In [5]:
lis2= [3,1,2,4,5,7,6]
print(lis2)

# 降順にソート
lis2.sort(reverse=True)
# sort関数の後に呼び出すと順番は変わる
print(lis2)
[3, 1, 2, 4, 5, 7, 6]
[7, 6, 5, 4, 3, 2, 1]

順番がプログラムに大きな影響を与える場合は注意が必要

元の形を保持したままにするのか、並び替えてから使うのか
処理のさせ方を考えて使い分けをします。

辞書型のソート

辞書型の並び替えは
キーの昇順と降順
値での昇順と降順
この4通りが存在します。

キー昇順:sorted(dct.items())

値の昇順:sorted(dct.items(), key=lambda x:x[1])

キー降順:sorted(dct.items(), reverse=True)

値の降順:sorted(dct.items(), key=lambda x:x[1],reverse=True)

In [6]:
# キー昇順
dct = { 2:3, 3:4, 1:2, 0:8, 4:2 }
for k, v in sorted(dct.items()):
    print(k,':',v)
0 : 8
1 : 2
2 : 3
3 : 4
4 : 2
In [7]:
# キー降順
dct = { 2:3, 3:4, 1:2, 0:8, 4:2 }
for k, v in sorted(dct.items(), reverse=True):
    print(k,':',v)
4 : 2
3 : 4
2 : 3
1 : 2
0 : 8
In [8]:
# Value昇順
dct = { 2:'3', 3:'4', 1:'2', 0:'8', 4:'2' }
for k, v in sorted(dct.items(), key=lambda x:x[1]):
    print(k,':',v)
1 : 2
4 : 2
2 : 3
3 : 4
0 : 8
In [9]:
# Value降順
dct = { 2:'3', 3:'4', 1:'2', 0:'8', 4:'2' }
for k, v in sorted(dct.items(), key=lambda x:x[1],reverse=True):
    print(k,':',v)
0 : 8
3 : 4
2 : 3
1 : 2
4 : 2

lambdaというのはラムダと読み、
pythonでは無名の関数を表す予約語になります。

ラムダについては後の講義で詳しくやって行きたいと思いますので、
ここでは割愛しますが、辞書の値で並び替えするには、
こういった書き方をしなくてはいけません。

辞書型そのものは並び順が大きな意味を持たないデータ型であるので
それを使うときにだけ並び替えを行うのが一般的です。

例えば、最終的に上位1番だけを出力する
といった時には降順で並び替えて出力する、ということを行います。

In [10]:
dct = { 2:'3', 3:'4', 1:'2', 0:'8', 4:'2' }

# まず辞書を値の降順ソートしてからenumrate関数に組み込む
for i , d  in enumerate(sorted(dct.items(), key=lambda x:x[1],reverse=True)):
    if i>=1:
        # 2回目で処理を抜ける
        break
    print(i,d[0],d[1])
0 0 8

繰り返しの処理を行う際にまず、辞書を値の降順でソートしておきます。
enumerate関数から返されるカウント数をみて繰り返しの処理を抜けます。

そうすることでデータが大量にある際に、上位何番まで出力するといった
ことも実現できるようになります。

注意点としてはenumerate関数と辞書型のitemsを使うと、返ってくるデータ型は
数値型とタプル型なので気をつけてください。

pythonではsorted関数を用いることで、
様々な形のデータの並び替えを行うことができます。

並びが重要なものを取り扱うプログラムを作るのに役立つと思います。

このページのトップヘ