[Python3 入門 8日目] 4章 Pyの皮：コード構造（4.1〜4.13）

#4.1 #によるコメント
- プログラムの中に含まれるテキストで"#"によるコメントはPythonインタープリンタから無視される。
- Pythonには複数行コメントはないため、コメント行、コメントセクションの冒頭には必ず#を入れなければならない。

#4.2 　\による行の継続
- 行末に\を置くとPythonはまだ行替えをしていないと思って動く。


```py

#小さい文字列から長い文字列を作りたければ次のように少しずつ作っていく方法がある。
>>> alphabet = ""
>>> alphabet +="abcdefg"
>>> alphabet +="hijklmnop"
>>> alphabet +="qrstuv"
>>> alphabet +="wxyz"

#継続文字を使えば見づらくせずワンステップで作ることもできる。
>>> alphabet = "abcdefg" + \
...  "hijklmnop" +\
...   "qrstuv"+\
...     "wxyz"

#行が複数行にまたがる場合にも行継続が必要になる。
>>> 1+2+
  File "<stdin>", line 1
    1+2+
       ^
SyntaxError: invalid syntax
>>> 1+2\
... 3
  File "<stdin>", line 2
    3
    ^
SyntaxError: invalid syntax
>>> 1+2+\
... 3
6

```

#4.3 if, elif,elseによる比較
- Pythonではセクション内ではコードが首尾一貫しているはずだと考えて動くため、**インデントは同じ大きさで左端が揃っていなければならない。**
- PEP8という推奨スタイルは4個のスペースを使っている。

```py

#disasterの内容をチェックして適切なコメントを表示するプログラム
>>> disaster = True
>>> if disaster:
...  print("Woe!")
... else:
...  print("Whee!")
...
Woe!

```

```py

>>> funny = True
>>> small = True
#if funnyがTrueならば if smallのテストに入る。
>>> if funny:
...     if small:
...      print(" It is a cat.")
...     else:
...      print(" It is a bear!")
... else:
...      if small:
...       print(" It is a skink!")
...      else:
...       print(" It is a human.Or a hairless bear.")
...
 It is a cat.

```

```py

#テストが3種類以上に分かれる場合はif,elif(else ifという意味),elseを使う。
>>> color ="puse"
>>> if color =="red":
...  print(" It is a tomato")
... elif color =="green":
...  print(" It is a green pepper")
... elif color =="bee purple":
...  print("I don't know what it is,but only bees can see it")
... else:
...  print("I've never heard of the color",color)
...
I've never heard of the color puse

```

```py

>>> x =2
#等価性のテスト
>>> x ==2
True
>>> x ==5
False
>>> 4 < x
False
>>> x<10
True
#ブール演算子は比較対象の要素よりも優先順位が低い。
#比較対象となる要素が先に計算されてからブール演算子が行われる。
>>> 1<x and x <10
True
>>> 1<x or x <10
True
>>> 1<x and x >10
False
>>> 1<x and not x >10
True
>>> 1<x<10
True
>>> 1<x<10<377
True

```

##4.3.1 Trueとは何か
- 以下に示すものは全てFalseと見なされる。
- その他のものは全てTrueと見なされる。

| Falseと見なされるもの      |         値|
|:-----------------|------------------:|:------------------:|
| ブール値       |False
| null           |None
| 整数のゼロ       |0
| floatのゼロ     |0.0
| 空文字列         |" "
| 空リスト         |[ ]
| 空タプル         |( )
| 空辞書          |{ }
| 空集合          |set()



```py

>>> some_list = []
>>> if some_list:
...  print("There's something in here")
... else:
...  print("Hey,it's empty!")
...
Hey,it's empty!

```


#4.4 whileによる反復処理
- 同じことを2回以上行いたいときはループを使う。

```py

#countの初期化
#countの値と5を比較
#countを1ずつインクリメント(加算)
>>> count =1
>>> while count <=5:
...  print(count)
...  count+=1
...
1
2
3
4
5

```

##4.4.1 ブレイクによるループの中止
- ループを中止させたい場合、breakを使う

```py

#無限ループ
#input()関数を使ってキーボードから入力行を読み出し、stuffに代入
#入力文字が"q":だった場合にループを抜ける。
#"q"以外は入力文字列の先頭文字を大文字にして表示する。

>>> while True:
...  stuff=input("String to capitalize[type q to quit]:")
...  if stuff == "q":
...     break
...  print(stuff.capitalize())
...
String to capitalize[type q to quit]:type
Type
String to capitalize[type q to quit]:untitarou
Untitarou
String to capitalize[type q to quit]:q

```

##4.4.2 continueによる次のイテレーションの開始
- **イテレーション**とは反復処理の1回分のことである。

```py

#input()関数を使ってキーボードから入力行を読み出し、valueに代入
#入力文字が"q":だった場合にループを抜ける。
#valueをint型にしてnumberへ代入
#偶数であれば次のイテレーションを開始

>>> while True:
...  value=input("Integer,please [q to quit]:")
...  if value == "q":
...     break
...  number =int(value)
...  if number %2==0:
...     continue
...  print(number,"squared is",number*number)
...
Integer,please [q to quit]:1
1 squared is 1
Integer,please [q to quit]:2
Integer,please [q to quit]:3
3 squared is 9
Integer,please [q to quit]:q

```


##4.4.3 elseによるbreakのチェック


```py

#変数positionの初期化
#偶数であれば"Found even numbers"とその数字を表示させbreakでwhile文を抜ける。
#else節はwhile分が終了したが探し物が見つからなかった場合に実行される。(breakチェッカー)

>>> number =[1,3,5]
>>> position =0
>>> while position <len(number):
...  numbers=number[position]
...  if numbers%2 ==0:
...   print("Found even numbers",numbers)
...   break
...  position+=1
... else:
...  print("No even number found")
...
No even number found

```

#4.5 forによる反復処理
Pythonは**イテレータ(イテレーションごとにリスト、辞書などから要素を一つずつ取り出して返すもの)**を頻繁に使う。
それはデータ構造を知らなくてもデータ構造の各要素を操作できるから。


```py

#while文
>>> rabbits =["a","b","c","d"]
>>> current=0
>>> while current<len(rabbits):
...  print(rabbits[current])
...  current +=1
...
a
b
c
d

#for文
>>> for rabbit in rabbits:
...  print(rabbit)
...
a
b
c
d

```

**リスト、文字列、タプル、辞書、集合、その他**とともにPythonの**イテラブル(イテレータに対応している)オブジェクト**である。

- タプルやリストをforで処理すると一度に一つずつの要素が取り出される。
- 文字列をforで処理すると以下に示すように一度に一つずつ文字が生成される。

```py

>>> word="cat"
>>> for letter in word:
...  print(letter)
...
c
a
t

```
- 辞書をforで処理するとキーが返される。

```py
>>> accusation ={"a":"ballroom","b":"weapon","c":"kowai"}
>>> for card in accusation:
...  print(card)
...
a
b
c

```

- 値を反復処理させたい場合は辞書のvalues()関数を使う。

```py
>>> for value in accusation.values():
...  print(value)
...
ballroom
weapon
kowai

```
- キーと値の両方をタプルの形で帰したい場合はitems()関数を使う。

```py
>>> for value in accusation.items():
...  print(value)
...
('a', 'ballroom')
('b', 'weapon')
('c', 'kowai')

```

- タプルの各要素を個別の変数に代入したい場合は、forの変数を2つ用意する。第一引数に"キー"、第二引数に"値"が代入される。

```py
>>> for card,contents in accusation.items():
...  print("Card",card,"has the contents",contents)
...
Card a has the contents ballroom
Card b has the contents weapon
Card c has the contents kowai

```

##4.5.1 breakによる中止
for文の中にbreak文を入れるとwhile分と同様にループを中止できる。

##4.5.2 continueによる次のイテレーションの開始
for文にcontinueを入れるとwhile分と同様に次のイテレーションにジャンプする。

##4.5.3 elseによるbreakのチェック
whileと同様にforは正常終了したかどうかをチェックするオプションのelseを持っている。breakが呼び出されなければ、else文が実行される。

```py

>>> cheeses=[]
>>> for cheese in cheeses:
...  print("This shop has some lovely",cheese)
...  break
... else:
...  print("This is not much of a cheese shop,is it?")
...
This is not much of a cheese shop,is it?

```

##4.5.4 zip()を使った複数のシーケンス処理
- zip関数を使えば複数のシーケンスを**並列的に処理**できる。
- zip()は最もサイズが小さいシーケンス要素を処理しつくし時に止まる。

```py

#リストの作成
#dessertsだけが他のリストより長い。他の要素を長くしない限りpuddingをもらえる人はいない。
>>> days =["Monday","Tuesday","Wednesday"]
>>> fruits=["coffee","tea","bear"]
>>> drinks=["coffee","tea","beer"]
>>> desserts=["tiamisu","ice cream","pie","pudding"]

#zip()を使い、複数のシーケンスをたどってオフセットが共通する要素からタプルを作ることができる。
>>> for day,fruit,drink,dessert in zip(days,fruits,drinks,desserts):
...     print(day,":drink",drink,"- eat",fruit,"-enjoy",dessert)
...
Monday :drink coffee - eat coffee -enjoy tiamisu
Tuesday :drink tea - eat tea -enjoy ice cream
Wednesday :drink beer - eat bear -enjoy pie

```

```py

#zip()から返される値自体はタプルやリストではなく、タプルやリストにできるイテラブルな値である。
>>> english="Monday","Tuesday","Wednesday"
>>> french="Lundi","Mardi","Mercredi"
#リスト化
>>> list(zip(english,french))
[('Monday', 'Lundi'), ('Tuesday', 'Mardi'), ('Wednesday', 'Mercredi')]
#辞書化
>>> dict(zip(english,french))
{'Monday': 'Lundi', 'Tuesday': 'Mardi', 'Wednesday': 'Mercredi'}

```
##4.5.5 range()による数値シーケンスの生成

- range()関数を使えば指定した範囲の数値ストリームを返すことができる。
- range()はrange(start: end:step)というスライスと似た形式で使う。
startは省略すると0が先頭になる。唯一の引数はendで、スライスと同様に作成される最後の値はstep-1の値である。
stepのデフォルト値は1だが、−1を指定して逆順にすることができる。
- zip()と同様にrange()はイテラブルなオブジェクトを返すので戻り値はfor...inで反復処理するか、リストなどのシーケンスに変換するが必要ある。

```py

#0から3までの範囲作成
>>> for x in range(0,3):
...   print(x)
...
0
1
2
#リスト化
>>> list(range(0,3))
[0, 1, 2]

#2から0までの範囲作成
>>> for x in range(2,-1,-1):
...   print(x)
...
2
1
0
#リスト化
>>> list(range(2,-1,-1))
[2, 1, 0]

#0から10までの偶数を取り出し
>>> list(range(0,11,2))
[0, 2, 4, 6, 8, 10]

```

#4.6 内包表記
**内包表記**は一つ以上のイテレータからPythonデータ構造をコンパクトに作れる形式。

##4.6.1 リスト内包表記

1から5までの整数のリストは以下のように一つずつ要素を追加しても作れる。

```py

#append()を使い末尾に追加している。
>>> number_list=[]
>>> number_list.append(1)
>>> number_list.append(2)
>>> number_list.append(3)
>>> number_list.append(4)
>>> number_list.append(5)
>>> number_list
[1, 2, 3, 4, 5]

#range()関数とforでも作れる。
>>> number_list=[]
>>> for number in range(1,6):
...     number_list.append(number)
...
>>> number_list
[1, 2, 3, 4, 5]

#range()の出力を直接リストに変換しても作れる。
>>> number_list = list(range(1,6))
>>> number_list
[1, 2, 3, 4, 5]

```

**リスト内包表記**を使ったコードを以下に示す。

```py

#[expression for item in iterable]の基本形式
#最初のnumber変数はループの実行結果をnumber_listに格納するためのもの。
#第二のnumberはfor文の一部
>>> number_list = [number for number in range(1,6)]
>>> number_list
[1, 2, 3, 4, 5]

#最初のnumberが式だとわかる。
>>> number_list = [number-1 for number in range(1,6)]
>>> number_list
[0, 1, 2, 3, 4]

```

```py

>>> a_list=[]
>>> for number in range (1,6):
...     if number%2 ==1:
...       a_list.append(number)
...
>>> a_list
[1, 3, 5]

#[expression for item in iterable if condition]の形式
>>> a_list = [number for number in range(1,6) if number %2 ==1]
>>> a_list
[1, 3, 5]

```

```py

>>> rows =range(1,4)
>>> cols=range(1,3)
>>> for row in rows:
...     for col in cols:
...        print(row,col)
...
1 1
1 2
2 1
2 2
3 1
3 2

#内包表記もネストできる。
#タプルで出力している。
>>> rows =range(1,4)
>>> cols=range(1,3)
>>> cells = [(row,col) for row in rows for col in cols]
>>> for cell in cells:
...     print(cell)
...
(1, 1)
(1, 2)
(2, 1)
(2, 2)
(3, 1)
(3, 2)

#cellsリストを反復処理しながらタプルからrow,colを引き抜く。
>>> for row,col in cells:
...     print(row,col)
...
1 1
1 2
2 1
2 2
3 1
3 2

```

##4.6.2 辞書包括表記
- 辞書にも内包表記がある。
- {key_item:value_item for item in iterable}の基本形式


```py

#"letters"から一つずつ文字を取り出し"letters"の中に何個含まれているかカウントし、キーとカウント回数をletter_countsに格納
>>> word ="letters"
>>> letter_counts={x:word.count(x)for x in word}
>>> letter_counts
{'l': 1, 'e': 2, 't': 2, 'r': 1, 's': 1}

#wordを集合としてみる。
>>> word ="letters"
>>> letter_counts={x:word.count(x)for x in set(word)}
>>> letter_counts
{'e': 2, 'l': 1, 's': 1, 't': 2, 'r': 1}

```

##4.6.3 集合内包表記

- {item for item in iterable}の基本形式
- 集合でも長いバージョンが使える。

```py
>>> a_set={number for number in range(1,6) if number %3 ==1}
>>> a_set
{1, 4}

```

##4.6.4 ジェネレータ内包表記

- タプルに内包表記はない
- 普通のかっこで内包表記を作るとジェネレータオブジェクトを返す
- ジェネレータとはイテレータにデータを供給する方法の一つ。

```py

#（）間のものはジェネレータ内包表記
>>> number_thing = (number for number  in range(1,6))
#ジェネレータオブジェクトを返す。
>>> type(number_thing)
<class'generator'>
#ジェネレータオブジェクトはfor文で処理できる。
>>> for number in number_thing:
...     print(number)
...
1
2
3
4
5

#ジェネレータ内包表記をlist()呼び出しでラップすればリスト内包表記リスト包括表記のように動作できる。
>>> number_list=list(number_thing)
>>> number_list
[1, 2, 3, 4, 5]

#ジェネレータは一度だけしか実行できない。ジェネレータは一度に一つずつその場で値を作り、イテレータに渡してしまうので作った値を覚えていない。そのためジェネレータをもう一度使ったりバックアップしたりすることはできない。
>>> number_list=list(number_thing)
>>>
>>> number_list
[]

```

#4.7 関数
プログラマーは関数に対して二つのことをできる。

- 関数の定義
- 関数の呼び出し

Python関数を定義するには defと入力し、関数名を書き、関数に対する入力引数をかっこに囲んでかき、最後に(:)を書く。

```py

#make_a_sound()関数を呼び出すとPythonが定義の中のコードを実行する。この場合は、1個の単語を出力して、メインプログラムに制御を返す。
>>> def make_a_sound():
...     print("quack")
...
>>> make_a_sound()
quack

#引数がないが値を返す関数
>>> def agree():
...     return True
...
>>> agree()
True

#ifを使って戻り値をテストする。
>>> if agree():
...     print("Splendid!")
... else:
...     print("That was unexpected.")
...
Splendid!

```

```py

>>> def echo(anything):
...     return anything+' '+anything
...
#echo()関数は"ss"という実引数とともに呼び出されている。
#この値はecho()内のanithingという仮引数にコピーされ、呼び出し元に返される。
>>> echo("ss")
'ss ss'

```

- 関数を呼び出すときに関数に渡される値も引数と呼ばれる。

- **実引数(argument)**を渡して関数を呼び出すとき、それらの値は関数内の対応する**仮引数(parameter)**にコピーされる。

```py
>>> def commtentary(color):
...     if color == "red":
...       return " It is a tomoato."
...     elif color =="green":
...       return " It is a green pepper"
...     elif color =="bee purple":
...       return "I don't know what it is,but only bees can see it."
...     else:
...       return "I've never heard of te color"+ color+"."
...
#"blue"という実引数とともにcommententary関数を呼び出す。
>>> comment=commtentary("blue")
>>> print(comment)
I've never heard of te colorblue.
```

- 関数が明示的にreturnを呼び出さなければ呼び出しもとはNoneを受け取る

```py

>>> def do_nothing():
...   pass
...
>>> print(do_nothing())
None
```
- Noneは何もいうべきことがない時に使われる。
- Noneはブール値として評価すると偽になるが、ブール値のFalseと同じではない。

```py

>>> def is_none(thing):
...     if thing is None:
...       print(" It is nothing")
...     elif thing:
...       print(" It is True")
...     else:
...       print(" It is False")
...


>>> is_none(None)
 It is nothing
>>> is_none(True)
 It is True
>>> is_none(False)
 It is False

#ゼロの整数とfloat、空文字列、空リスト、空タプル、空辞書、空集合はFalseだが、Noneとは等しくない。
>>> is_none(0)
 It is False
>>> is_none(0.0)
 It is False
>>> is_none(())
 It is False
>>> is_none([])
 It is False
>>> is_none({})
 It is False
>>> is_none(set())
 It is False

```

##4.7.1 位置引数
- 位置引数とは先頭から順に対応する位置の仮引数にコピーされる引数

```py
>>> def menu(x,y,z):
...     return{"wine":x,"entree":y,"dessert":z}
...
>>> menu("aaa","bbb","ccc")
{'wine': 'aaa', 'entree': 'bbb', 'dessert': 'ccc'}

```

##4.7.2 キーワード引数

- 対応する仮引数の名前を指定して実引数を指定すれば良い。
- 位置引数とキーワード引数の両方を使って関数を呼び出す場合は、まず先に位置引数を指定しなければならない。

```py

>>> def menu(x,y,z):
...     return{"wine":x,"entree":y,"dessert":z}
...
>>> menu("beef","bagel","bordeaux")
{'wine': 'beef', 'entree': 'bagel', 'dessert': 'bordeaux'}

#仮引数を指定して実引数を指定している。
>>> menu(y="beef",z="bagel",x="bordeaux")
{'wine': 'bordeaux', 'entree': 'beef', 'dessert': 'bagel'}
#位置引数とキーワード引数の両方を使う場合は位置引数を最初に指定する。
>>> menu("frontenac",z="flan",y="fish")
{'wine': 'frontenac', 'entree': 'fish', 'dessert': 'flan'}

```

##4.7.3 デフォルト引数値の指定

```py

#デフォルト引数値の指定
>>> def menu(x,y,z="unchi"):
...     return{"wine":x,"entree":y,"dessert":z}
...
#"dessert"のみ引数指定されていないためデフォルトの引数が入る。
>>> menu("dunkelfelder","chiken")
{'wine': 'dunkelfelder', 'entree': 'chiken', 'dessert': 'unchi'}
#引数を指定すればそれがデフォルト値の代わりに使われる。
>>> menu("dunkelfelder","duck","doughnut")
{'wine': 'dunkelfelder', 'entree': 'duck', 'dessert': 'doughnut'}

```

```py

>>> def buggy(arg,result=[]):
...     result.append(arg)
...     print(result)
...
>>> buggy("a")
['a']
#['ab']としたい...
>>> buggy("ab")
['a', 'ab']

```

```py
#buggy()関数呼びだしのたびにresultを[]にして初期化しておくおkとがポイント
>>> def buggy(arg):
...     result=[]
...     result.append(arg)
...     return result
...
>>> buggy("a")
['a']
#正しく動作した!!!!
>>> buggy("b")
['b']

```

```py

#result=[]としてif文を使い、resultを初期化している。
>>> def nonbuggy(arg,result=None):
...     if result is None:
...         result = []
...     result.append(arg)
...     print(result)
...
>>> nonbuggy("a")
['a']
>>> nonbuggy("b")
['b']

```

##4.7.4 *による位置引数のタプル化
- 関数定義の中で仮引数の一部として*を使うと、可変個の位置引数をタプルにまとめてその仮引数にセットできる。
- *を使うときにタプル仮引数をargsを使うのが慣習的。

```py

>>> def print_args(*args):
...     print("Positional argument tuple:",args)
...
>>> print_args()
Positional argument tuple: ()
#argsタプルとして表示される
>>> print_args(3,2,1,"wait!","uh...")
Positional argument tuple: (3, 2, 1, 'wait!', 'uh...')

#必須の引数がある場合には、位置引数の最後に*argsを書くと必須引数以外の全ての位置引数を一つにまとめることができる。
>>> def print_more(x1,x2,*args):
...     print("Need this one",x1)
...     print("Need this one too",x2)
...     print("All the rest",args)
...
>>> print_more("cap","gloves","scarf","monocle","mustache wax")
Need this one cap
Need this one too gloves
All the rest ('scarf', 'monocle', 'mustache wax')
```

##4.7.5 **によるキーワード引数の辞書化
- **を使えばキーワード引数を1個の辞書にまとめることができる。
- 引数の名前は辞書のキー、引数の値は辞書の値となる。
- 位置引数をまとめる*argsと**kwargsを併用する場合、この二つはこの順序でまとめなければならない。

```py
>>> def print_kwargs(**kwargs):
...     print("Keyward arguments:",kwargs)
...
>>> print_kwargs(x="xx",y="yy",z="zz")
Keyward arguments: {'x': 'xx', 'y': 'yy', 'z': 'zz'}

```

**4.7.6 docstring

- 関数本題の先頭に文字列を組み込めば、関数定義にドキュメントをつけることができる。これを関数のdocstringと呼ぶ。

```py

>>> def echo(anything):
...     'echoは与えられた入力引数をかえす'
...     return anything
...
#help()関数を呼び出すと関数のdocstringを表示する。
>>> help(echo)
Help on function echo in module __main__:

echo(anything)
    echoは与えられた入力引数をかえす
(END)

```

##4.7.7 一人前のオブジェクトとしての関数

- 関数もオブジェクトである。
- 関数はリスト、タプル、集合、辞書の要素として使うことができる。
- 関数はイミュータブルなので辞書のキーとしても使うことができる。

```py

>>> def answer():
...     print(43)
...
>>> answer()
43
>>> def run_something(func):
...      func()
...
#answer()ではなくanswerを渡すと他のデータ型と同様に関数をデータ型として使っていることになる。
>>> run_something(answer)
43

```

```py

>>> def add_args(x1,x2):
...     print(x1+x2)
...
>>> type(add_args)
<class'function'>

#funcをオブジェクトとしてrun_something_with_argsに渡している。
>>> def run_something_with_args(func,arg1,arg2):
...     func(arg1,arg2)
...
>>> run_something_with_args(add_args,5,9)
14

```

```py

#任意の数の位置引数をとり、sum()関数でそれらの合計を計算して返す。
>>> def sum_args(*args):
...     return sum(args)
...

```

```py

>>> def run_with_positional_args(func,*args):
...     return func(*args)
...
>>> run_with_positional_args(sum_args,1,2,3,4)
10

```

##4.7.8 関数内関数

- 関数を関数内で定義することができる。

```py

>>> def outer(a,b):
...     def inner(c,d):
...         return c+d
...     return inner(a,b)
...
>>> outer(4,7)
11
>>> def knights(s):
...     def inner(q):
...         return "%s" % q
...     return inner(s)
...
>>> knights("Ni!")
'Ni!'

```

##4.7.9 クロージャ

- 関数内関数はクロージャとして機能する。
- クロージャとは他の関数によって動的に生成される関数で、その関数の外で作られた変数の値を覚えていたり、変えたりすることができる。

```py

#inner2()は引数を要求せず、外側の関数に対する引数sを直接使う。
#knights2()はinner2を呼び出すのではなくその関数名を返す。

#inner2はクロージャとして機能している。
>>> def knights2(s):
...     def inner2():
...         return "%s" % s
...     return inner2
...
>>> a= knights2("Duck")
>>> b= knights2("Hasenpfeffer")
>>> type(a)
<class'function'>
>>> type(b)
<class'function'>

#これらは関数だが、クロージャでもある。
>>> a
<function knights2.<locals>.inner2 at 0x1006d6ef0>
>>> b
<function knights2.<locals>.inner2 at 0x1006d60e0>

#これらを呼び出すと二つのクロージャはknights2に自分たちが作られていたsの内容を覚えている。
>>> a()
'Duck'
>>> b()
'Hasenpfeffer'

```

##4.7.10 無名関数：ラムダ関数

- ラムダ関数は一つの文で表現される無名関数。
- 小さな関数をいくつも作って名前を覚えておかなければならないような場面で効果的。

```py

>>> def edit_story(words,func):
...     for word in words:
...         print(func(word))
...
>>> stairs = ["x","u","z","r"]
>>> def enliven(word):
...     return word.capitalize()+"!"
...
#リストから一文字ずつ取り出し、enlivenで処理している。
>>> edit_story(stairs,enliven)
X!
U!
Z!
R!

#enliven()関数はとても短いのでラムダに取り替える。
#このラムダは1個の引数 wordをとる。
#":"から末尾までの部分は全て関数定義である。
>>> edit_story(stairs,lambda word:word.capitalize()+"!")
X!
U!
Z!
R!

```
#4.8 ジェネレータ
- ジェネレータはPythonのシーケンスを作成するオブジェクトである。
- ジェネレータはイテレータのデータソースになることが多い。


```py

#ジェネレータ関数は値をreturnの代わりにyield文で返す。
>>> def my_range(first=0,last=10,step=1):
...     number=first
...     while number < last:
...         yield number
...         number +=step
...
#my_rangeは通常の関数である。
>>> my_range
<function my_range at 0x1009364d0>
>>> ranger=my_range(1,5)
#ジェネレータオブジェクトを返す。
>>> ranger
<generator object my_range at 0x100636ed0>
#ジェネレータオブジェクトを対象としてforによる反復処理をすることができる。
>>> for x in ranger:
...     print(x)
...
1
2
3
4

```

#4.9 デコレータ
- デコレータは入力として関数を一つとり、別の関数を返す関数。
- 以下のものを使ってデコレータを作成する。
  - *argsと**kwargs
  - 関数内関数
  - 引数としての関数
- 関数に対するデコレータは複数持てる。関数に最も近いデコレータが先に実行され、次にその上のデコレータが実行される。

```py

>>> def document_it(func):
...     def new_function(*args,**kwargs):
...         print("Running function:",func.__name__)
...         print("Positional arguments:",args)
...         print("Keyword arguments:",kwargs)
...         result =func(*args,**kwargs)
...         print("Result:",result)
...         return result
...     return new_function
... 
>>> def add_ints(a,b):
...     return a+b
... 
>>> add_ints(1,2)
3
>>> cooler_add_ints =document_it(add_ints)
>>> ccler_add_ints(1,2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'ccler_add_ints' is not defined
>>> cooler_add_ints(1,2)
Running function: add_ints
Positional arguments: (1, 2)
Keyword arguments: {}
Result: 3
3

#デコレートしたい関数の直前に@decorator_name形式で追加する。
>>> @document_it
... def add_ints(a,b):
...     return a+b
... 
>>> add_ints(1,2)
Running function: add_ints
Positional arguments: (1, 2)
Keyword arguments: {}
Result: 3
3

```

#4.10 名前空間とスコープ

- 名前空間とは特定の名前の意味が一意に決まり、他の名前空間と同じ名前とは無関係になる領域のこと。しかし、必要なら他の名前空間にアクセスできる。
- プログラムのメイン部分はグローバル名前空間を定義する。この名前空間の変数はグローバル変数と呼ばれる。
- pythonは名前空間にアクセスするために二つの関数を用意している。
 - locals()はローカル名前空間の内容を示す**辞書**を返す。
 - globals()はグローバル名前空間の内容を示す**辞書**を返す。


```py

#グローバル変数の値は関数内から参照できる。
>>> animal="Z"
>>> def print_global():
...     print("inside print_global:",animal)
...
>>> print("at the top level:",animal)
at the top level: Z
>>> print_global()
inside print_global: Z

#関数内でグローバル変数の値を取得し、書き換えようとするとエラーが起きる。
>>> def changed_and_print_global():
...     print("inside change_and_print_global:",animal)
...     animal="wombat"
...     print("after the change:",animal)
...
>>> changed_and_print_global()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in changed_and_print_global
UnboundLocalError: local variable 'animal' referenced before assignment

#change_local()関数もanimalという変数を持っているが、その変数は関数のローカル名前空間内の存在。
>>> animal="Z"
>>> def change_local():
...     animal = "W"
...     print("inside change_local:",animal,id(animal))
...

#change_local()の中のanimal変数がプログラム内のanimal変数とは別物であることがわかる。
>>> change_local()
inside change_local: W 4564475440
>>> animal
'Z'
>>> id(animal)
4564751344

#グローバル変数にアクセスするにはglobalキーワードを使ってそのことを明示しなければならない。
>>> animal="Z"
>>> def change_and_print_global():
...     global animal
...     animal = "wombat"
...     print("inside change_and_print_global:",animal)
...
>>> animal
'Z'
>>> change_and_print_global()
inside change_and_print_global: wombat
>>> animal
'wombat'

#関数内でglobalと書かなければ、Pythonはローカル名前空間を使い、animal変数はローカルになる。関数が終わったら、ローカル変数は消えて無くなる。
>>> animal="Z"
>>> def change_local():
...     animal = "wombat"
...     print("locals:",locals())
...
>>> animal
'Z'

#ローカル名前空間の内容を示す辞書を返す。
>>> change_local()
locals: {'animal': 'wombat'}

#グローバル名前空間の内容を示す辞書を返す。
>>> print("globals:",globals())
globals: {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class'_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'document_it': <function document_it at 0x110146290>, 'add_ints': 3, 'square_it': <function square_it at 0x110146440>, 'animal': 'Z', 'print_global': <function print_global at 0x1101464d0>, 'changed_and_print_global': <function changed_and_print_global at 0x1101465f0>, 'change_local': <function change_local at 0x110146950>, 'change_and_print_global': <function change_and_print_global at 0x110146830>}
>>> animal
'Z'

```

##4.10.1 名前の中の_と__
- 先頭と末尾が2個のアンダースコアになっている名前は、Pythonが使う変数として予約されている。

```py

>>> def amazing():
...     '''これは素晴らしい関数だ
...     もう1度見る'''
#関数の名前はシステム変数のfunction_name__(基本形式)
...     print("この関数の名前:",amazing.__name__)
#docstringはシステム変数のfunction_doc__(基本形式)
...     print("docstring:",amazing.__doc__)
...
>>> amazing()
この関数の名前: amazing
docstring: これは素晴らしい関数だ
    もう1度見る

```

#4.11 エラー処理とtry,except

- 例外が起きそうなところには全て例外処理を追加して、ユーザーに何が起きるか知らせておくのがグッドプラクティス。

```py

#プログラム強制終了
>>> short_list=[1,2,3]
>>> position = 5
>>> short_list[position]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

#tryを使って例外が起きそうな場所を囲み、exceptを使って例外処理を提供すべき。
#tryブロックのコードが実行され、そこでエラーが起きると例外は生成されexceptブロックのコードが実行される。例外が起きなければexceptブロックは実行されない。

>>> short_list=[1,2,3]
>>> position = 5
>>> try:
...     short_list[position]
... except:
...     print("Need a position between 0 and",len(short_list)-1,"but got",position)
...
Need a position between 0 and 2 but got 5

```

```py

#exceptを使うと全ての例外をキャッチする。
#詳細情報が分かるようにしたい場合、except exceptiontype as nameの基本形式を記述する。

>>> short_list=[1,2,3]
>>> while True:
...     value=input("Position[q to quit]?")
...     if value =="q":
...         break
...     try:
...         position=int(value)
...         print(short_list[position])
...     except IndexError as err:
...         print("bad index:",position)
...     except Exception as other:
...         print("Something else broke:",other)
...
Position[q to quit]?1
2
Position[q to quit]?0
1
Position[q to quit]?2
3
Position[q to quit]?3
bad index: 3
Position[q to quit]?2
3
Position[q to quit]?two
Something else broke: invalid literal for int() with base 10: 'two'
Position[q to quit]?q

```

#4.12 独自例外の作成
- 独自の例外型を定義できる。

```py

#例外が生成された時に何を表示すべきか親クラスのExceptionに任せている。
>>> class OopsException(Exception):
...     pass
...
>>> try:
...     raise OopsException("panic")
... except OopsException as exc:
...     print(exc)
...
panic

```

#4.13 復習課題

##4-1 変数guess_meに7を代入しよう。次にguess_meが7より小さければtoo low、7よりも大きければtoo high、等しければjust right表示しよう。

```py

>>> if guess_me<7:
...     print("too low")
... elif guess_me>7:
...     print("too high")
... elif guess_me==7:
...     print("just right")
...
just right

```

##4-2 変数guess_meに7、変数startに1を代入し、startとguess_meを比較するwhileループを書こう。

```py

>>> while True:
...     if start<guess_me:
...         print("too low")
...     elif start==guess_me:
...         print("found it!")
...         break
...     elif start>guess_me:
...         print("oops")
...         break
...     start+=1
...
too low
too low
too low
too low
too low
too low
found it!

```

 ##4-3 forループを使ってリスト[3,2,1,0]の値を表示しよう。

```py

>>> list=[3,2,1,0]
>>> for x in list:
...     print(list[x])
...
0
1
2
3

```

##4-4 リスト内包表記を使ってrange(10)の偶数のリストを作ろう。

```py

>>> list=[number for number in range(10) if number%2==0]
>>> list
[0, 2, 4, 6, 8]
```

##4-5 辞書内包表記を使ってsquaresという辞書を作ろう。ただし、range(10)を使ってキーを返し、各キーの辞書の自乗をその値とする。
```py
>>> squares={number:number*number for number in range(10)}
>>> squares
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}

```

##4-6 集合内包表記を使ってrange(10)の奇数からoddという集合を作ろう。

```py

>>> odd= {number for number in range(10) if number%2==1}
>>> odd
{1, 3, 5, 7, 9}
```

##4-7 ジェネレータ内包表記を使ってrange(10)の数値に対しては"Got　"と数値を返そう。

```py

>>> for thing in ("Got %s" %number for number in range(10)):
...     print(thing)
...
Got 0
Got 1
Got 2
Got 3
Got 4
Got 5
Got 6
Got 7
Got 8
Got 9

```

##4-8 ["H","R","Hermione"]というリストを返すgoodという関数を定義しよう。

```py

>>> def good():
...     print(["H","R","Hermione"])
...
>>> good()
['H', 'R', 'Hermione']

```

##4-9 range(10)から奇数を返すget_oddsというジェネレータ関数を定義しよう。また、forループを使って、返された3番目の値を見つけて表示しよう。

```py

#ジェネレータ関数はyieldで返す。
>>> def get_odds():
...     for x in range(1,10,2):
...             yield x
...
#enumerate()関数を使うと、for文の中でリスト（配列）などのイテラブルオブジェクトの要素と同時にインデックス番号（カウント、順番）を取得できる。インデックス番号, 要素の順に取得できる。
#オフセットを1からに指定。
>>> for count,number in enumerate(get_odds(),1):
...     if count==3:
...         print("The third number is",number)
...         break
...
The third number is 5

```

##4-10 関数が呼び出された時に"start"、終了した時に"end"を表示するデコレータを定義しよう。

```py

>>> def test(func):
...     def new_func(*args,**kwargs):
...         print("start")
...         result = func(*args,**kwargs)
...         print("end")
...         return result
...     return new_func
...
>>> @test
... def greeting():
...     print("Hello!")
...
>>> greeting()
start
Hello!
end

```

##4-11  OopseExceptionという例外を定義しよう。次に何が起きたかを知らせるためにこの例外コードとこの例外をキャッチして"Caught an oops"と表示するコードを書こう。

```py

#OopseExceptionの定義
>>> class OopseException(Exception):
...     pass
...
>>> raise OopseException()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
__main__.OopseException
>>> try:
...     raise OopseException
... except OopseException:
...     print("Caught an oops")
...
Caught an oops

```


##4-12 zip()を使ってmoviesという辞書を作ろう。辞書はtitles=["C","B","A"]、plots=["D","W"]というリストを組み合わせて作るものとする。

```py

>>> titles=["C","B","A"]
>>> plots=["D","W"]
>>> movies=dict(zip(titles,plots))
>>> movies
{'C': 'D', 'B': 'W'}

```


#感想

4章の復習ボリューム多かったな。1日がかりだった。

######参考文献
「Bill Lubanovic著 『入門 Python3』(オライリージャパン発行)」