Python学習【365日チャレンジ!】63日目のマスターU(@Udemy11)です。
LINEのスタンプが自分で簡単に作れることを知って、iPhoneのアプリでいろいろと試しているのですが、なかなか時間がかかってしまいます。
スタンプだけじゃなくて、絵文字も作れるのですが、絵文字はスタンプと違って、iPhoneのアプリ【LINE CREATORS STUDIO】では作れないので、LINE Creators Marketから操作する必要があります。
作ってはみたものの、審査はきちんとされているみたいなので、通るかどうかは微妙なところですが、できるのならちょっとLINEスタンプづくりにハマってしまいそうです。
それでは、今日のPythonを学習していきましょう!
昨日の復習
昨日は、ジェネレーター内包表記を学習しました。
def g():
for i in range(10):
yield(i)
g = g()
print(next(g))
print(next(g))
#上記のコードが次のように書ける
g = (i for i in range(10))
print(next(g))
print(next(g))
出力結果
0
1
ジェネレーターの内包表記は、内包表記を使用しない場合と比べるとかなり短く書けるイメージですね。
ただ、Parenthesesの丸括弧なので、タプルと間違わないようにしておきましょう。
タプルに値を入れたい場合は、()
の前にtuple
を記述する必要がありました。
g = tuple(i for i in range(10))
print(g)
出力結果
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
それでは、名前空間とスコープを学習していきましょう!
名前空間とスコープとは
名前空間は、関数や変数が所属している場所のことで、スコープはその場所から見える範囲(有効範囲)を示す言葉です。
この2つは、どうやら微妙に違うものなのですが、まー同じものと理解しておいても問題はないでしょう。
例えば、現実世界では、1年A組の鈴木花子さんも、3年A組の鈴木丸子さんも○○学校の鈴木さんには間違いありませんので、
○○学校の鈴木さんがね〜
という話を聞くと、受け取り手によって、1年A組の鈴木花子さんのことだと思う人と、3年A組の鈴木丸子さんのことだと思う人がいますが、人の会話では、別にどちらでも問題はありません。
同じ話をしていても、人によって全く受け取り方が違うので、人それぞれ解釈が違うわけです。
しかし、Pythonのようなプログラムでは、それがどちらの鈴木さんなのかわかっていないと処理をすすめることができずにエラーを返してしまいます。
例えば、変数を定義して関数の出力をする場合、関数の中と外では名前空間が違いますし、名前空間からのスコープ(有効範囲)が違います。
参照できる範囲が決め手
実際にコードでみていきましょう。
gadget = 'Mac'
def f():
print(gadget)
f()
出力結果
Mac
関数の外で定義した変数が関数の中で使われていますが、関数の外のスコープは関数の内側を含むので、関数の外で定義した関数を利用することができます。
一方で、関数の中のスコープは、関数内だけになるので、関数内で定義したものは、関数の外で利用することはできません。
gadget = 'Mac'
def f():
print(gadget)
tv = 'sony'
f()
print(tv)
このコードの場合は、関数f
でMac
と出力されたあと、次のようなエラーが起こります。
NameError: name ‘tv’ is not defined
これは、関数の内側(Global)のスコープが関数内なので、関数の外側ではtv
が定義されていないと判断しているからです。
例えば、変数を同じgadget
として考えてみるとよく分かるのですが、関数の中で、変数gadget
を出力したあと、変数gadget
を他の値に変更して、変更した値を出力しようとするとエラーが起こります。
gadget = 'Mac'
def f():
print(gadget)
gadget = 'iPhone'
print('Next:', gadget)
f()
わたしなら、普通にMac
が出力されたあと、変数gadget
がiPhone
に変更されて、Next:iPhone
と出力されるような気がするのですが、このコードの場合、下記のエラーが返されます。
UnboundLocalError: local variable ‘gadget’ referenced before assignment
変数gadget
が定義される前に出力しようとしてますよというエラーですが、私ならここでもまだ、「gadget
は前で定義しているのにな〜」なんて思っちゃいます。
関数の外で定義されているgadget
と同じ名前の変数が関数の中に定義されているので、関数内では、中で定義されたgadget
が優先されます。
関数の中のgadget
と外のgadget
は別物として認識されるので、関数の中では、定義されていないものを出力しようとして起こるエラーなので、Pythonさんの仰せのとおり、4行目のgadget = 'iPhone'
を3行目に持ってくればエラーは起こりません。
ただ、この場合は、出力される値が変わってきます。
gadget = 'Mac'
def f():
gadget = 'iPhone'
print(gadget)
print('Next:', gadget)
f()
出力結果
iPhone
Next: iPhone
関数の中で定義された変数gadget
はiPhone
から変更されていないので、出力は2回ともiPhone
になっています。
では、関数の外側の変数gadget
はどうなっているのかというと、最初に定義されたMac
が入ったままなので、外側(インデントを上げて)で出力するとMac
が出力されます。
gadget = 'Mac'
def f():
gadget = 'iPhone'
print(gadget)
f()
print(gadget)
出力結果
iPhone
Mac
関数外のスコープは関数内も含むけど、同じ名前があれば中で定義されたものが優先されるということです。
GlobalとLocal
中と外という表現をしてきましたが、Pythonでは、グローバル(Global)とローカル(Local)という言葉が使われています。
Pythonは、インデントを揃えることで名前空間を把握していますが、インデントなしで記載されているエリアがグローバルで、インデントされているエリアがローカルです。
これまで説明したことをこれらの言葉を使って解説すると、
グローバル変数のスコープはローカルを含んで、ローカル変数のスコープはローカル内で有効。ただし、ローカルエリアのローカル変数にグローバル変数と同じ名前が使われている場合は、ローカル変数が優先される。
という感じでしょうか?
ちなみに、グローバルとローカルで同じ名前の変数を使っている場合でも、次の方法を使えばローカル内の関数でグローバル変数を利用することが可能です。
gadget = 'iPad'
def f():
global gadget
gadget = 'Macmini'
print(gadget)
print(gadget)
f()
出力結果
iPad
Macmini
global
文でグローバル変数を指定することで、ローカルエリア内でもグローバル変数を扱うことができます。
上記の例では、ローカルエリア内でグローバル変数gadget
をMacmini
に変更したあと出力する関数を定義し、関数f
を実行する前にグローバル変数gadget
を出力し、最後に関数f
を実行しているので、出力がiPad
とMacmini
になっています。
変数の参照
グローバルエリアとローカルエリアで宣言されている変数を参照したいときは、globals()
メソッドやlocals()
メソッドを使います。
gadget = 'Apple Watch'
def f():
gadget = 'iMac'
print(gadget)
print(locals())
f()
print(globals())
出力結果
iMac
{'gadget': 'iMac'}
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x104e65860>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/Users/bigmacpro/PycharmProjects/python_programming/lesson.py', '__cached__': None, 'gadget': 'Apple Watch', 'f': <function f at 0x104de6400>}
locals()
メソッドとglobals()
メソッドを使えば、それぞれのエリアで宣言されている変数が辞書型で参照することができます。
ローカル変数は、一つだけですが、グローバルには、Python自体が宣言している変数がたくさんあるということですね。__name__
が__main__
と定義されているものから始まって、定義した関数もきちんと記載されています。
理解に時間がかかる
かなりプログラミングっぱい感じの内容にになってきたので、内容の理解にかなり時間がかかっています。
年齢のせいで、頭が固くなってしまって、理解度が低くなっていることもあり、何度も何度も講義を見直したり、Webサイトで検索したりしながら理解していますが、なかなか腑に落ちないんです。
とはいえ、きちんと理解していないと、どんどんわからないことが出てくるので、時間をかけてでも理解するように努力しています。
腑に落ちたときの快感がたまらないので、しっかりと理解できるように突き詰めていこうと思います。
ではでは、明日もGood Python!