Be a Pythonista ! その3 変数②

注:本記事は比較的がちふぁい勢向けかもしれません。


目次



前の記事

この記事は続き物なので、良ければひとつ前の記事もどうぞ!


型について少し詳しく話そう

今回は以下の型について解説します。
他にも型はありますが、初学者には必要ないかな~と思います。

  • int型(イント、インテジャー)

  • float型(フロート)

  • str型(ストリング)

  • bool型(ブール、ブーレン)

  • list型(リスト)

  • tuple型(タプル)

  • set型(セット、集合)

  • dict型(ディクショナリ、辞書)


型を知ろう

int型(イント、インテジャー)

整数を扱う型です。
使い方は簡単で、変数に整数をそのまま代入すれば使えます。

x = 2
y = -9
print(x * y)

これで変数xと変数yはint型で使えています。

軽く算術演算子も列挙しておきましょう。
算術演算子とは、「+」とか「×」のことです。

a = 10 + 3 # 足し算
b = 10 - 3 # 引き算
c = 10 * 3 # 掛け算
d = 10 / 3 # 割り算
e = 10 ** 3 # べき乗
f = 10 // 3 # 割り算の整数部分
g = 10 % 3 # 割り算のあまり部分

基本的な計算はこれで事足ります。
割り算の整数部分やあまり部分を使うタイミングに疑問を感じるかもしれませんが、意外と使います。
代表例として、あまりを使えば数が偶数か奇数かを確認する時に便利です。

具体的にコードを見てみましょう。

x = 19 # xが偶数か奇数かを判定したい!

# 余りを使った場合の判定コード
if x % 2 == 0: # xを2で割ってあまりが0ならば・・・
    print('ぐうすう')
else: # それ以外の場合は・・・
    print('きすう')

# 初心者が書きがちな凄まじい判定コード
if str(x)[-1] == '0':
    print('ぐうすう')
elif str(x)[-1] == '2':
    print('ぐうすう')
elif str(x)[-1] == '4':
    print('ぐうすう')
elif str(x)[-1] == '6':
    print('ぐうすう')
elif str(x)[-1] == '8':
    print('ぐうすう')
elif str(x)[-1] == '1':
    print('きすう')
elif str(x)[-1] == '3':
    print('きすう')
elif str(x)[-1] == '5':
    print('きすう')
elif str(x)[-1] == '7':
    print('きすう')
elif str(x)[-1] == '9':
    print('きすう')

あまりを上手に使うと偶数と奇数の判定は4行のコードで書けます!
if文の使い方は別の記事で解説します。


float型(フロート)

小数を扱う型です。
もう少し正確に言いますと、浮動小数点を扱う型です。

x = -0.9
y = 1.3
print(x * y)

これで今度はxとyはfloat型になっています。
計算方法はint型の時と同じです。


str型(ストリング)

文字列を表す型です。
文字型ではなく「文字列」型なので注意しましょう。
※Pythonに「文字型」は存在しませんが、
 言語によっては「文字型」と「文字列型」を区別します。
 一般には文字型は「char型」、文字列は「string型」と表現します。

str型は使い方がいろいろあります。

x = 'Reala is cute.'
print(x)

x = "Reala is cute."
print(x)

シングルクォーテーション「''」または
ダブルクォーテーション「""」で文字を囲えば文字列型になります。
シングルorダブルクォーテーションに違いは特にありません。

他にも、文字列どうしを組み合わせるような便利な使い方もあります。

x = 'Reala'
y = 'is'
z = 'cute'

script = x + ' ' + y + ' ' + z
print(script, end='.')

script = '{} {} {}'.format(x, y, z)
print(script, end='.')

script = f'{x} {y} {z}'
print(script, end='.')

上記のprint文は全て同じ出力になります。

一つ目は単純な文字列の足し算です。
二つ目は「format関数」を用いて、文字列に変数を代入できます。
三つ目は「f文字列」と呼ばれるもので、format関数を改良したものです。
f文字列はPython3.6で導入された機能なので、Python3.5以前で実行すると
エラーがでるので、注意しましょう。

format関数とf文字列は、xやyに文字列以外の値を入れても動作します。

x = '1 + 1'
y = 1 + 1

script = '{} = {}'.format(x, y)
print(script)

script = f'{x} = {y}'
print(script)

上記だと、xが文字列型、yが整数型で使われていますが、
問題なくprint文が実行されます。

また、このように動的に文字列の内容を変更して処理をするときは、
f文字列が推奨されています。
理由としては、可読性の向上が見込めるからです。

例として、以下のコードを比較しましょう。

name = 'Nayose Reala'
age = 23
property = 'PON'

print('name: {}, age: {}, property: {}'.format(name, age, property))
print(f'name: {name}, age: {age}, property: {property}')

二つのprint文の出力は同じですが、コードを見たときにどちらが読みやすいでしょうか?
おそらく、後者のf文字列の方が読みやすいと思います。
なので、積極的にf文字列を使っていきましょう!

ちなみにですが、f文字列は内部で効率化されているため、
format関数を使うよりも約8.5%程度高速に動作します。
もちろん、処理内容や状況によっては差が出ないこともありますが・・・。

実際にコードを書いて確認してみましょう。

import datetime


x = 'times'
dt = datetime.datetime.now()

for i in range(50000001):
    y = f'{x} = {i}'

print(datetime.datetime.now() - dt)

上記は50000000回f文字列で値を生成し、時間計測をするコードです。
実行結果は以下の通り。

0:00:03.921579

3.921579秒で生成が終了しました。

では、format関数を用いて生成してみましょう。

import datetime


x = 'times'
dt = datetime.datetime.now()

for i in range(50000001):
    y = '{} = {}'.format(x, i)

print(datetime.datetime.now() - dt)

実行結果は以下の通り。

0:00:06.325640

6.32564秒で、この処理だとf文字列の約2倍も処理時間がかかりました。

もっと生成回数を増やすとどうなるでしょうか?
回数を倍の100000000回にした実行結果は以下の通りです。

0:00:07.968453 # f文字列の結果

0:00:12.646238 # formatの結果

両方とも回数に対してほぼ線形に比例して約2倍になりました。
生成回数が多いのであれば、f文字列は処理の高速化にも使えそうです。


bool型(ブール、ブーレン)

boolはTrueとFalseを表す型です。

x = True
y = False

これでxとyはbool型になっています。

たまに見る間違いで、True,Falseを文字列として扱ってしまう事があります。

x = 'True'
y = 'False'

上記だと、xとyは文字列型になってしまいます。
bool型はクォーテーション無しで書きましょう。

bool型の使用場面は「条件分岐(if)」をやるときに解説します!


list型(リスト)

複数の値を一つの変数で保持できる型です。
list型変数に入れる値の型は統一されてなくても問題ありません!
これから解説する配列系の変数は全て値の型に制限はなかったはずです。

x = ['Reala', 1, 0.9, True]

上記のように、「[ ]」で値を囲めばlist型になります。
list型変数に入れる値は、「,(コンマ)」で区切ります。

それぞれの値はインデックスで管理されていて、
インデックスの番号で値を取り出すことができます。
インデックスは左から順番に、0から自動で割り振られます。

x = ['Reala', 1, 0.9, True]
print(x[0])
print(x[1])
print(x[2])
print(x[3])

上記を実行すると、以下の出力が得られます。

Reala
1
0.9
True

このように、「x[インデックス]」の形式でlistから値が取り出せます。
インデックスの指定方法はいろいろありますが、また別の機会に。

listは値を変更することもできます。

x = ['Reala', 1, 0.9, True]
x[0] = 'Nayose'
print(x[0])
print(x[1])
print(x[2])
print(x[3])

x[0] = 'Nayose' のように変数xの0番目の値を 'Reala'から'Nayose'に変えて
出力すると、以下のようになります。

Nayose
1
0.9
True

x[0]の値がちゃんと入れ替わってますね!


tuple型(タプル)

tuple型は、list型と同じように使えますが、値の変更ができません。
使い方は簡単で、list型の時に使った「[ ]」を「( )」に変更するだけです。

x = ('Reala', 1, 0.9, True)
print(x[0])
print(x[1])
print(x[2])
print(x[3])

上記の出力結果はlistの時と同じで、以下の通りです。

Reala
1
0.9
True

ですが、値の変更ができないので以下のコードだとエラーになります。

x = ('Reala', 1, 0.9, True)
x[0] = 'Nayose'

エラー内容は以下の通りです。

TypeError: 'tuple' object does not support item assignment

クソ雑翻訳
型エラー:タプルオブジェクトはアイテムの割り当てができないよ!

tupleは値の変更ができない性質を持っているので、
何かしらの影響で中身が変わってしまうと困るような値を保持するときに
有用だと思います。

ですが、変数xそのものに対しての再代入は可能なので注意しましょう。

x = ('Reala', 1, 0.9, True)
x = ('Nayose', 1, 0.9, True)
print(x[0])
print(x[1])
print(x[2])
print(x[3])

上記だとエラーが出ず、変数xの値は変わってしまいます。
変更したくない値を代入したtuple型でも値が変わることはありますので、
コードを書く際は注意しましょう。
tuple型の不変性を過信しないようにしましょう!


set型(セット、集合)

set型もlist型と同じように使用できますが、保持する値に「順番」の概念が存在せず、値の重複も認められません。
使い方は、list型の「[ ]」を「{ }」に変更するだけです。
コードでその特徴を確認してみましょう。

x = {3, 6, 1, 1, 1, 0, 2, 4, 5} # 0~6の7種類の文字がset型に格納されました。
print(x)

上記のコードの出力を確認すると、

{0, 1, 2, 3, 4, 5, 6}

となります。
値の重複がなくなり、元の配列から勝手に順番が変更されています。

set型は順番の概念が無いので、
list型のようにインデックスで値を指定することができません。

x = {1, 2, 3}
print(x[0])

上記のコードを実行すると、以下のエラーが出ます。

TypeError: 'set' object is not subscriptable

クソ雑翻訳
型エラー:セットオブジェクトは添え字で指定できないよ!

つまり、インデックスで指定できないということです。
気をつけましょう。

set型は配列の中から重複する値を除去する際に非常に便利です。
また、特定の値を配列が持っているかを確認する際はlist型やtuple型の中
から探すよりset型の中から探した方が効率が良いです。

これは、list型のサーチは一つ目の要素から順番に探す必要があるので、
処理時間は配列の長さに比例します。
必要な要素が最後にあった場合なんて最悪です。
対してset型はハッシュテーブルを用いたサーチを実行するので、
処理時間は配列の長さの影響を受けにくく、安定して高速です。

以下のコードで速度を比較しましょう。

import datetime


x = set(range(500000001))

dt = datetime.datetime.now()
print(500000000 in x)
print(datetime.datetime.now() - dt)

上記はset型変数に0~500000000までの数列を格納しています。
その中から500000000(一番最後にある要素)が存在するかどうかを
確認し、時間計測するコードです。
実行結果は以下の通り。

True
0:00:00.004000

0.004秒でサーチが終了しました。

では、リストだとどうでしょうか。

import datetime


x = list(range(500000001))

dt = datetime.datetime.now()
print(500000000 in x)
print(datetime.datetime.now() - dt)

上記は、実行している内容は何も変わりません。
xがset型からlist型に変わっただけです。
実行結果は以下の通り。

True
0:00:04.481999

4.482秒かかってしまいました。

もっと大きな値でも試してみましょう。
以下は、配列の範囲を倍の0~1000000000に変更したそれぞれの結果です。

True
0:00:00.005451 # set型の結果

True
0:01:22.428370 # list型の結果

set型は配列の長さを2倍にしても、ほとんど影響を受けていませんが、
list型はなんと約18.4倍の1分22秒も時間がかかってしまいました!

値のサーチは特別な事情が無い限りset型を使った方がよさそうですね。


dict型(ディクショナリ、辞書)

dict型も配列の一つで、他の配列とは使い方が少し違います。
使い方は、set型と同じで「{ }」を使いますが、set型と違って
key」と「value」をニコイチで保持するのがdict型の特徴です。

具体的には以下の形で使います。

x = {'key': 'value'}
print(x['key'])

上記のコードを実行すると、「value」が出力されます。

dict型の何が便利かというと、値を「key」で呼び出せるところです。
慣れると極めて便利です。

例えば、色のデータを配列で保持し、呼び出すとします。
そのとき、list型だと以下のようなコードになると思います。

colors = [(0, 0, 255), (0, 255, 0), (255, 0, 0)] # RGBの順番で色の値をlist型で保持している
print(colors[0]) # 青色の値を出力
print(colors[2]) # 赤色の値を出力

上記のようにコード量が少ない場合はコードを読んで理解できるでしょう。
ですが、コード量が増えたり、配列の宣言が離れた位置にあったり、
ファイルが別だったりすると「colors[0]」が何を指しているのか、
何のための値なのかがぱっと見で分かりません。

配列が処理の中で自動的に生成されるようなコードだと、
colors[0]」が色のRGB値の配列なのか、色のRGB成分なのか、
試しに出力するか処理を追って考えないとわかりません。

こういう時にdict型を使えば、値のヒントになります。

上記をdict型にしてみましょう。

colors = {'blue': (0, 0, 255), 'green': (0, 255, 0), 'red': (255, 0, 0)}
print(colors['blue'])
print(colors['red'])

一目瞭然でdict型のコードの方が見やすいと思います。
以下のようにすると、それぞれの色の成分もkeyで取得できますね!

colors = {
    'blue': {'r': 0, 'g': 0, 'b': 255},
    'green': {'r': 0, 'g': 255, 'b': 0},
    'red': {'r': 255, 'g': 0, 'b': 0}
}
print(colors['blue']['b'])
print(colors['red']['r'])

上記のように、dict型の値にさらにdict型を入れることもできます。

ただ、あまり考えなしに配列の中に配列を入れまくるとデータ構造が複雑になって逆にわかりにくくなることがあるので、データ構造はしっかり考えてコードを書きましょう!
これは配列系の変数に共通して言えることです。


変数編おわり!

7500文字の超大作になってしまい申し訳ありません!!
書いてあることを全部覚えるんじゃなくて、必要に応じて参照する形の方が
いいと思います。


次の記事

次の記事は条件分岐①です。
よければどうぞ!

この記事が気に入ったらサポートをしてみませんか?