毎日ちょっとPython ~ 7日目 ~
さぁ8日目。毎日とついてはいるものの、毎日できてないですね。
とりあえずスタート
例題プログラム : リストを用いたマジック8ボール
import random
messages = [
'確かにそうだ',
'間違いなくそうだ',
'はい',
'なんとも。もう一度やってみて',
'後でもう一度聞いて',
'集中してもういちどきいてみて',
'私の答えはノーです',
'見通しはそれほど良くない',
'とても疑わしい'
]
print(messages[random.randint(0,len(messages) - 1)])
こんなかんじで、書けばOK。
字下げルールの例外
コードの字下げはPythonにブロックを伝えてる。ただ、いくつか例外があるるそうです。
1. リストは閉じかっこが現れるまで複数の行に分けて書くことができます。
2. 行末に行連結文字の「\(バックスラッシュ)」をつける
バックスラッシュは可読性を上げる際に用いることが多いそうです。
リスト風のデータ型:文字列
文字列も結構おもしろい。リスト風なので以下のことができたりします。
>>> name = 'SK3-Blueowl'
>>> name[0]
'S'
>>> name[-2]
'w'
>>> name[0:4]
'SK3-'
>>> 'SK' in name
True
>>> 'i' in name
False
>>> 'c' not in name
True
>>> for i in name:
print('* * * ' + i + ' * * *')
* * * S * * *
* * * K * * *
* * * 3 * * *
* * * - * * *
* * * B * * *
* * * l * * *
* * * u * * *
* * * e * * *
* * * o * * *
* * * w * * *
* * * l * * *
おもしろいですねぇ。
ミュータブル、イミュータブルなデータ型
ミュータブル(変更可能)→リスト型
イミュータブル(変更不可)→文字列型
変更不可の文字列は以下のことができないです
>>> name = 'Zophie a cat'
>>> name[7] = 'the'
Traceback (most recent call last):
File "<pyshell#16>", line 1, in <module>
name[7] = 'the'
TypeError: 'str' object does not support item assignment
もし変更するなら、スライスと連結を使って新しい文字列を使う
>>> name = 'Zophia a cat'
>>> new_name = name[0:7] + 'the' + name[8:12]
>>> name
'Zophia a cat'
>>> new_name
'Zophia the cat'
みたいな感じ
リストのミュータブル
ミュータブルは、実は元の値は変更しているわけではないので注意しろよっていう話。
たとえば、
>>> eggs = [1,2,3]
>>> eggs = [4,5,6]
>>> eggs
[4, 5, 6]
とコードを書いた場合にeggsの値が置き換わっただけで元のリストは[1,2,3]となります。
もし完全に書き換えたい場合はこんな感じで変更する必要があります。
>>> eggs = [1,2,3]
>>> eggs[0] = 4
>>> eggs
[4, 2, 3]
>>> eggs[1] = 5
>>> eggs
[4, 5, 3]
>>> eggs[2] = 6
>>> eggs
[4, 5, 6]
なんでこんなことするのかというと、参照渡しがあるそうです。関数の引数として渡すときにミュータブルかどうかで挙動が変わるらしい。
タプル型
タプルとは次の二つの点を除き、リストとほぼ同じデータ型です。
1. タプルは 角カッコ[ ] ではなく 丸カッコの ( ) を用いて書く
2. タプルはイミュータブル
>>> eggs = ('hello',42,0.5)
>>> eggs[0]
'hello'
>>> eggs[1:3]
(42, 0.5)
>>> len(eggs)
3
>>> eggs[1] = 99
Traceback (most recent call last):
File "<pyshell#62>", line 1, in <module>
eggs[1] = 99
TypeError: 'tuple' object does not support item assignment
最後のインタラクティブシェルを見てもらうと、タプルは要素の代入をサポートしてないよってエラーが出ます
要素を一つだけのタプルを記述する場合は、文字列の後にカンマを入れてください。
>>> type(('hello',))
<class 'tuple'>
>>> type(('hello'))
<class 'str'>
※ほかの言語と違って、空のカンマがあっても怒られない(気が付かなくて困りそうな将来が見える...
list()関数とtuple()関数を使って型を変換する
>>> tuple(['cat','dog',5])
('cat', 'dog', 5)
>>> list(('cat','dog',5))
['cat', 'dog', 5]
>>> list('hello')
['h', 'e', 'l', 'l', 'o']
参照
以前に説明したように、変数には文字列や整数の値を格納することができます。たとえば、こんな感じのものがあった場合の挙動を軽く
>>> spam = 42
>>> cheese = spam
>>> spam = 100
>>> spam
100
>>> cheese
42
変数 spam に 42 を代入してその値を spam にコピーし、さらに変数cheeseに代入する。spam の値を 100 しても cheese の値に影響はありません。これは、spam と cheese は異なる値を格納することなる変数であるためです。
ただ、リストはこうはいきません。リストを変数に代入する場合、リストの参照を変数に代入しています。どういうのがわかりやすいのかな。物は変わらんけど見る場所をいくつも増やしているイメージですかね。
>>> spam = [0,1,2,3,4,5]
>>> cheese = spam
>>> cheese[1] = 'Hello!'
>>> spam
[0, 'Hello!', 2, 3, 4, 5]
>>> cheese
[0, 'Hello!', 2, 3, 4, 5]
こんな感じで、みるとわかるんですけど同じリストを参照しているので更新されちゃう。なので、参照を渡しているという事は忘れないようにしないとなぁ...マジで。
関数に参照を渡す
関数が呼び出されるときに、引数の値はパラメータ変数にコピーされる。なのでリストを引数に渡すと参照がコピーされてしまう。
def eggs(some_parameter):
some_parameter.append('Hello')
spam = [1,2,3]
eggs(spam)
print(spam)
## 結果は [1, 2, 3, 'Hello']になる
eggs()を呼び出すときに、リストをインプレースで変更してこのプログラムを実行すると、上記のように実行されました。参照を渡しているので、関数の中でそのリストを更新するとそのリストも更新されてしまいます。バグの原因になるので注意しないとなぁ。
copyモジュールのcopy()関数とdeepcopy()関数
リストや辞書を参照で渡すのは便利ですけど、元のリストや辞書の値は変更されずに済ませたい場合は copy() , deepcopy() という関数を備えた copy というモジュールがあります。
>>> import copy
>>> spam = ['A','B','C','D']
>>> cheese = copy.copy(spam)
>>> cheese[1] = 42
>>> spam
['A', 'B', 'C', 'D']
こんな感じで参照を渡すわけではなく、リストをコピーして作るイメージになるので更新されないです。
リストを含むリストをコピーする場合は、 copy.deepcopy()関数のほうを使います。
最後に
参照渡しをするのか。便利だけど...どうなんだろうか。今後も楽しみです。