Python
python3

Pythonのデータ型についてまとめてみた

はじめに

Pythonのデータ型がいろいろあってPythonをやっていくうちにまとめないと最初は分からなくなりそうと感じまとめた記事になっています。

想定の倍以上の量になってしまったため、間違いがあるかもしれません。
見つけ次第やっつけていく予定です。
ごめんなさい。

2018/04/04 23時頃
@shiracamus さんよりいただいたコメントの修正を行いました。

環境

Python 3.6.4
Visual Stdio Code

データ型の基本

  • 宣言と同時に型が分かるように初期化が必要です。
# これは実行時にエラーとなる
value
# こうする必要がある
value = 0
  • 変数には型という概念がないようで、いつ何時どんな型でも入れ替え自由です(ただの入れ物って感じみたいです)
# これはエラーになりません
value = 0
value = "string"
print(value)

boolean型(真偽値)

0 or Flaseが偽
1 or Trueが真
です。

bool関数で調べてみると0がFalseでそれ以外がTrueになっています。
マイナスでもTrueなのはちょっと驚きました。

コメントで条件分岐ではboolで直して真偽判断していると教えていただきました。
真偽判断をわざわざ色々記載いただいていたので、こちらで再掲します。

>>> bool(0)
False
>>> bool(0.)
False
>>> bool(0j)
False
>>> bool(.1)
True
>>> bool(1)
True
>>> bool(1j)
True)
>>> bool(2)
True
>>> bool('')
False
>>> bool('a')
True
>>> bool([])
False
>>> bool([0])
True
>>> bool(())
False
>>> bool((0,))
True
>>> bool({})
False
>>> bool({0})
True

number型(数値)

数字を扱うデータ型です。
小数点と整数を扱うデータ型で計算する際中に2つ混ざっても問題ありません。

value = 1.2 + 1 - (-1)
print(value)
>>> 3.2

n進数

扱える進数には2進数、8進数、10進数、16進数が存在しています。

print(0b11) # 2進数は0bを先頭に付ける
print(0o11) # 8進数は0oを先頭に付ける
print(0x11) # 16進数は0xを先頭に付ける
>>>3
>>>9
>>>17

計算

計算の優先順位については他の言語と変わりません。
書き方もほとんど同じですが、調べたので記載します。

記述方法 備考
加算 1 + 1
減算 1 - 1
乗算 1 * 1
除算 1 / 1 浮動小数点ありの計算結果になる。整数の場合は1//1と記述する。
除算(余り) 1 % 1
べき乗 1 ** 1

他にもビット演算、シフト演算も可能となっていますが、あんまり使用しないと思ったので記載しません。

あと、Decimal型、Fraction型(有理数)、複素数なんかも対応していますが、書き始めると大変なことになる気がするので今回は書きません。(そのうち書くかもしれません)

数値型に変換

変換はint関数、float関数で行います。

print(int("1") + int("2"))
print(float("1.1") + int("2"))
>>> 3
>>> 3.1

整数をfloat関数で変換しても問題ありませんが

print(float("1"))
>>> 1.0

小数点付きの文字列をint関数に指定するとエラーになってしまうので注意が必要です。

print(int("1.1"))
>>> ValueError: invalid literal for int() with base 10: '1.1'

その場合は小数点付きの数値に直してからint関数でさらに変換させます。

print(int(float("1.1")))
>>> 1

ちなみに負の数は素直に変換が可能となっているようです。

print(int("-1"))
>>> -1

絶対値が必要な場合はabs関数で変換できます。
ただし、数値型じゃないとエラーになるので数値型への変換は必要です。

print(abs(int("-1")))
>>> 1

String型(文字列)

文字列を扱うデータ型です。
文字列をシングルクォート、ダブルクォートで囲むことが出来、クォートは1つもしくは3つで構成されます。

print("aaa") # 1つは改行なし
print('aaa') # 1つは改行なし
print('''aaa
aaa
bbb''') # 3つは改行を含める
print("""aaa
aaa
bbb
""")# 3つは改行を含める
>>> aaa
>>> aaa
>>> aaa
aaa
bbb
>>> aaa
aaa
bbb

1つしか記述していないのに改行すると、エラーです。
シングルとダブルの使い分けは結構好みぐらいな感じに見えます。
シングルクォートの時はダブルクォータのエスケープがいらないといったことは可能ですが、エスケープしてあげれば普通に表現可能なので、わざわざ使い分ける必要かなというのが感想です。

string = "bbb\""
print(string)
>>> bbb"

改行やタブなんかのエスケープ文字も埋め込んでprint可能です。
なので、3つでやるのも見づらくなるだけかなとか思ってます。

string = "bbb\n"
print(string + string)
>>> bbb
bbb
string = "bbb\tbbb"
print(string)
>>> bbb     bbb

Charcter

Pythonには明示的なCharacterがないので、1文字もStringです。
文字列から特定のIndexにある文字を取り出すことは可能となっています。

string = "bbb"
print(string[1]) # indexを指定すると1文字
print(string[1:3] # indexの開始と終わりを指定するとその分取得してくれる
>>> b
>>> bb

ただし、1文字の文字列が文字として扱われることもあるとのことです。
コメントでord関数の例をいただいたので、確かにこれは1文字をUnicodeコードポイントを表す整数に変換する関数で2文字以上入れた場合はエラーとなってしまいました。
(下のソースはコメントでいただいた例をそのまま載せてます)

>>> ord('a')
97
>>> ord('aa')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: ord() expected a character, but string of length 2 found

文字列型に変換

変換はstr関数を使用して可能です。

print(str(1).replace("1","2"))
>>> 2

デフォルトのエンコードはUTF-8となっています。他も指定可能ですが、指定した場合はbytesもしくはbytearrayでなくてはいけません。

print(str(1, "Shitf-Jis"))
> TypeError: decoding to str: need a bytes-like object, int found

Byte型(バイト)

Byteを扱うデータ型です。
見ている限りでは、自分は余り使う機会がないかなという感じな型です。
作るにはbytearrayかbytesで作り出します。
違いは前者がmutableで後者がimmutableです。
使える組み込み関数が違うのと性質が違うので、きちんと使い分けましょう。

abd = bytearray(b'aaa')
print(list(abd))
>>> [97, 97, 97]

abd = bytes(b'aaa')
print(list(abd))
>>> [97, 97, 97]

関数呼び出しの外にあるbは接頭辞というらしいです。
byte型であることを明示することになっています。
ない場合はエラーになってしまいます。

abd = bytes('aaa')
print(list(abd))
>>> TypeError: string argument without an encoding

エラーになる原因としては、デフォルト(bという接頭辞を付与した場合)のエンコードがASCIIですが、上記の例ではUTF-8文字aaaが渡されたためにエラーとなっています。
Python2系では暗黙的に変換されるそうですが、3系から変換されないためにByte型とString型の比較は偽です。

if(b'aaa' == 'aaa'):
    print("真")
else:
    print("偽")
>>> 

それでは文字列を変換したい場合ですが、引数としてエンコードを渡せるので渡してやるとエラーにならずに変換されます。

abd = bytes('aaa', 'UTF-8')
print(list(abd))
>>> [97, 97, 97]

エンコード付きで変換してやると、ASCIIではエラーになる日本語も変換してあげることが可能です。

abd = bytearray('あいえうお', 'UTF-8')
print(list(abd))
>>> [227, 129, 130, 227, 129, 132, 227, 129, 136, 227, 129, 134, 227, 129, 138]
abd = bytes(b 'あいえうお')
print(list(abd))
>>> SyntaxError: invalid syntax

逆にエンコード無しで変換しようとした場合、文字列から直接変換しようとするとASCIIじゃないので怒られるので注意が必要です。

abd = bytes('aaa')
print(list(abd))
>>> TypeError: string argument without an encoding

Hex(16進数)

Byte配列の場合は16進数とのやり取りが行われることが多いですが、文字列とのやり取りのみのようです。

abd = bytearray.fromhex('f0 f1f2')
print(list(abd))
>>> [240, 241, 242]
abd = bytearray(b'\xf0\xf1\xf2').hex()
print(abd)
>>> f0f1f2

Byte型に変換

bytesとかbytearrayが文字列からの変換みたいなものになっていますが、bytes(immutable)であればencodedecodeで変換可能でした。
デフォルトエンコードがUTF-8になっているので、こっちの方が明示的で良さそうです。

value = 'abc'.encode()
print(value)
print(value.decode())
>>> b'abc'
>>> abc

数字型はやり方が別でbytesやbytearrayみたいな関数ではなくto_bytesを使います。

value = 1
print(value.to_bytes(2, 'big'))
>>> b'\x00\x01'

ただしint(整数)である必要があります。
float(小数)には関数が定義されていません。

value = 1.1
print(value.to_bytes(2, 'big'))
>>> AttributeError: 'float' object has no attribute 'to_bytes'

byte配列からintも戻せます。
こちらも整数のみとなっています(intのclassmethod)。

value = 1
print(int.from_bytes(value.to_bytes(2, 'big'), 'big'))
>>> 1

List型 (リスト)

なんとなくリストをばしばしいじるとプログラムしている感じが出てくるのは自分だけでしょうか。
リストは複数の要素をまとめることが出来る型となっていてmutableなオブジェクトとなっています。

list = [1,2,3]
print(list)
>>> [1, 2, 3]

少し問題となりそうなのが、このリストは複数の型が混在していても問題なく動作してしまいます。
問題となる場面が出てきてもおかしくないので、自分はそんなに好きじゃない挙動です。

list = [1,"2",3]
for data in list:
    print(type(data))
>>> <class 'int'>
<class 'str'>
<class 'int'>

リスト内部の特定要素を取得したい場合はインデックス表記やスライス表記を使用して取り出すことが可能となっています。

list = [1,2,3]
print(list[0])
print(list[1])
print(list[2])
>>> 1
>>> 2
>>> 3
list = [1,2,3]
print(list[0:1]) 
print(list[1:2])
print(list[2:3])
>>> [1]
>>> [2]
>>> [3]

List in List ...

リスト内のオブジェクトは特に問題なく全ての要素を扱えるようなので、List in List in Listみたいなことも可能です。

list = [[[1,2,3],2,3],2,3]
print(list)
>>> [[[1, 2, 3], 2, 3], 2, 3]

Listの中身を入れ替える

中身を入れ替える場合は要素を取り出す時と同じようにインデックス表記やスライス表記で入れ替えを行うことが出来ます。

list = [[[1,2,3],2,3],2,3]
list[0] = 1
print(list)
>>> [1, 2, 3]
list = [[[1,2,3],2,3],2,3]
list[0:2] = [1,1]
print(list)

スライスで入れ替える場合は要素を入れ替える分Listを渡す必要があります。

list = [1,2,3]
list[0:2] = [4,5,6,7]
print(list)
>>> [4, 5, 6, 7, 3]

多い分にはうまくやってくれているので、単なる入れ替えだけではなく挿入も一緒にやってくれるようです。

list = [1,2,3]
list[0:2] = 1
print(list)
>>> TypeError: can only assign an iterable

ただしスライス表記の場合はListを渡す必要があるので注意してください。

list = [1,2,3]
list[0:0] = [4]
print(list)
>>> [4, 1, 2, 3]

ちなみにスライス表記で同じ箇所を指定した場合は挿入動作だけしてくれます。

Listに要素を追加する

List要素を1つ追加するか複数追加するかでやり方が変わります。

list = [1,2,3]
list.append(4)
print(list)
>>> [1, 2, 3, 4]

1つの場合はappendを使用して追加します。

list = [1,2,3]
list.append([4,5])
print(list)
>>> [1, 2, 3, [4, 5]]

Listを入れても1つの要素として扱われます。

list = [1,2,3]
list.extend([4,5])
print(list)
>>> [1, 2, 3, 4, 5]
list = [1,2,3]
list += [4,5]
print(list)

複数個追加したい場合はextendするか+=でListを渡してあげると出来ます。

list = [1,2,3]
list.insert(5,6)
print(list)
>>> [1, 2, 3, 6]

挿入であればList型関数にinsert(index, value)が用意されているので、こちらを使用する手もあります。

Listから削除

削除する方法はいくつかあります。

list = [1,2,3]
list[0:1]= []
print(list)
>>> [2, 3]

まずはスライス表記を使用して空を入れて入れ替えをしてあげる方法です。

list = [1,2,3]
del list[0:1]
print(list)
>>> [2, 3]

もう1つはdel文を使用して行う方法があります。

list = [1,2,3]
list.remove(1)
print(list)

ピンポイントで消したい場合はList型の関数としてremove(index)が用意されているので、こちらを使用する手もあります。
ただし、なぜかremoveは1オリジンとなっているので注意なのと削除できないインデックスを指定した場合は(index == 0 or size < index)エラーです。

list = [1,2,3]
list.clear()
print(list)
>>> []

全部消したい場合はList型の関数としてclearが用意されているので、こちらを使用する手もあります。

文字列からListへ変換

数字型からは直接変換出来ないようですが、文字列型からは変換が可能となっています。
やり方はlist関数を呼ぶだけです。

value = list('123')
print(value)
>>> ['1', '2', '3']

List内包処理

詳しくは他の記事を参照してください!(まだ良くわかってない)

その他

List型は他にもmap filter sort reverseなど一般的にList操作として行えることは出来ます。
多すぎるのでここでは省略します。

Tuple型(タプル)

TupleはListと同じく要素を複数持つことが出来る型です。
Listとの違いは大きく2つあり

  1. immutableである
  2. 要素内に複数の型を混ぜることが出来ない

コメントで指摘いただきましたが、Tupleで型は混ぜれます。

といった違いがあります。
他は操作を含めて基本的にはListと同じ感じで使うことが出来ます。
もちろんimmutableなので副作用のある操作を行うことは出来ません。

Tupleへ変換

文字列やListから変換が可能となっています。
逆にTupleからListへの変換も可能です。
やり方はtuple関数を呼ぶだけです。

value = [1,2,3]
print(tuple(value))
print(list(tuple(value)))
>>> (1, 2, 3)
[1, 2, 3]
value = 'abc'
print(tuple(value))
>>> ('a', 'b', 'c')

Range型(数値シーケンス)

rangeは数値のシーケンスを簡単に扱うことが出来る型です。
このシーケンスはimmutableです。
やり方はrange関数を呼ぶだけです。

value = range(10)
for i in value:
    print(i)
print(value)
>>> 0
1
2
3
4
5
6
7
8
9
range(0, 10)
class range(stop)
class range(start, stop[, step])

range関数はこの2つがあり、細かいシーケンスを作成することが可能となっています。

set型(集合体)

同一の値が持てないListみたいなものです。
set関数とfrozenset関数があり、それぞれmutableimmutablesetを作成することが出来ます。
List Tuple Range Stringから作成することが出来ます。

value = set([1,2,3,1])
print(value)
>>> {1, 2, 3}
value2 = frozenset([1,2,3,1])
print(value2)
>>> frozenset({1, 2, 3})
value = set((1,2,3,1))
print(value)
>>> {1, 2, 3}
value2 = frozenset((1,2,3,1))
print(value2)
>>> frozenset({1, 2, 3})
value = set(range(10))
print(value)
>>> {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
value2 = frozenset(range(10))
print(value2)
>>> frozenset({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})
value = set('abc')
print(value)
>>> {'a', 'c', 'b'}
value2 = frozenset('abc')
print(value2)
>>> frozenset({'a', 'c', 'b'})

setfrozenset内の要素は混ぜても良いですが、List型だけは駄目みたいです。

value = set([1,2,3,b'4', '5', (1,2,3)])
print(value)
>>> {'5', 1, 2, 3, b'4', (1, 2, 3)}
value = set([1,2,3,'4',[1,2,3]])
print(value)
>>> TypeError: unhashable type: 'list'
value = {1,2}
print(value)
>>> {1, 2}

また、setであれば{}で作成することも出来ます。

要素の操作

SetではSetSetを組み合わせたりSetListを組み合わせたりすることで要素の操作を行うことが出来ます。
関数で操作する場合は副作用があるのでfrozensetでは出来ません。
演算でやる場合は副作用がなく、新しい集合体を作るのでfrozensetでも使えます。

要素を追加する

update|=は2つの要素をマージするので、実質追加のような動作をします。

value = set([1,2,3])
value.update([4])
print(value)
>>> {1, 2, 3, 4}
value = set([1,2,3])
value |= set([4])
print(value)
>>> {1, 2, 3, 4}

重複している要素を抽出

intersection_update&=は2つの要素で重複している要素を抽出します。

value = set([1,2,3])
value.intersection_update([4])
print(value)
>>> set()
value = set([1,2,3])
value &= set([4])
print(value)
>>> set()

要素の削除

difference_update-=は要素の削除を行います。

value = set([1,2,3])
value.difference_update([3])
print(value)
>>> {1, 2}
value = set([1,2,3])
value -= set([3])
print(value)
>>> {1, 2}

要素の追加と重複削除

symmetric_difference_update^=は重複していない要素であれば追加し、重複している要素であれば削除を行います。

value = set([1,2,3])
value.symmetric_difference_update([4])
print(value)
>>> {1, 2, 3, 4}
value = set([1,2,3])
value.symmetric_difference_update([1])
print(value)
>>> {2, 3}
value = set([1,2,3])
value ^= set([4])
print(value)
>>> {1, 2, 3, 4}
value = set([1,2,3])
value ^= set([1])
print(value)
>>> {2, 3}

単体要素の操作

今までの操作はSetListと組み合わせた操作になっています。
多くて面倒になってきたので、まとめて紹介します。

value = set([1,2,3])
# 追加
value.add(4)
print(value)
>>> {1, 2, 3, 4}
# 削除(存在しなければKeyError)
value.remove(3)
>>> {1, 2, 4}
print(value)
# 存在していれば削除、しなければ何もしない
value.discard(5)
print(value)
>>> {1, 2, 4}
# 存在していれば削除、しなければ何もしない
value.discard(2)
print(value)
>>> {1, 4}
# 任意の要素を削除(空の場合はKeyError)
value.pop()
print(value)
>>> {4}
# 全削除
value.clear()
print(value)
>>> set()

dict型(マッピング、辞書)

mutableなマップ型です。
マップ型なので、key-value方式となっています。

a = dict(one=1, two=2, three=3)
print(a)
>>> {'one': 1, 'two': 2, 'three': 3}
b = {'one': 1, 'two': 2, 'three': 3}
print(b)
>>> {'one': 1, 'two': 2, 'three': 3}
c = dict(zip(['one', 'two', 'three'], [1, 2, 3]))
print(c)
>>> {'one': 1, 'two': 2, 'three': 3}
d = dict([('two', 2), ('one', 1), ('three', 3)])
print(d)
>>> {'two': 2, 'one': 1, 'three': 3}
e = dict({'three': 3, 'one': 1, 'two': 2})
print(e)
>>> {'three': 3, 'one': 1, 'two': 2}

初期化方法は幾つも存在しており、どれでも大丈夫です。

value = {'one': 1, 'two': 2, 'three': '3'}
print(value)
>>> {'one': 1, 'two': 2, 'three': '3'}
value = {'one': 1, 'two': 2, 3: 3}
print(value)
>>> {'one': 1, 'two': 2, 3: 3}

keyとvalueは型が違っていても大丈夫です。

value = {'one': 1, 'two': 2, 'three': [1,2,3]}
print(value)
>>> {'one': 1, 'two': 2, 'three': [1, 2, 3]}
value = {'one': 1, 'two': 2, 'three': (1,2,3)}
print(value)
>>> {'one': 1, 'two': 2, 'three': (1, 2, 3)}

また、valueにはListだったりTupleだったりも大丈夫ですが、

value = {[1,2,3]: 1, 'two': 2, 'three': 3}
print(value)
>>> TypeError: unhashable type: 'list'
value = {(1,2,3): 1, 'two': 2, 'three': 3}
print(value)
>>> {(1, 2, 3): 1, 'two': 2, 'three': 3}

keyはListがダメでTupleが大丈夫という感じで違いがあります。

コメントいただきましたが、keyはimmutableなオブジェクトのみが指定出来るそうです。

辞書の操作

基本はListとかTuoleにあるような操作は行えるようです。
重複する感じになってしまうので、ここでは割愛します。

読み込み専用辞書の作成

読み込み専用としたい場合はMappingProxyTypeを使います。

from types import MappingProxyType

value = {(1,2,3): 1, 'two': 2, 'three': 3}
print(MappingProxyType(value))

MappingProxyTypeは今までと違ってtypes内にあります。
これはimportしないと使用することが出来ません。
typesは動的な型生成と組み込み型となっていますが、ここでは扱いません。

ビューオブジェクト

keys() values() items()で取り出した場合、それはビューオブジェクトとして扱われます。

value = {'one': 1, 'two': 2, 'three': 3}
print(value.keys())
>>> dict_keys(['one', 'two', 'three'])
print(value.values())
>>> dict_values([1, 2, 3])
print(value.items())
>>> dict_items([('one', 1), ('two', 2), ('three', 3)])

ビューオブジェクトはイテレートすることで(list() tuple() for)、それぞれの型へ変換したり使用可能です。
また、ビューオブジェクトは'Set'と同じような操作が可能となっています。

最後に

3週くらいに分けてこれ書いたんですが、この間に小さい記事どれだけ書いたと思ってるんだと言いたくなるぐらい時間かかりましたが、この記事の作成にあたってtutorialをものすごく読んで理解が深まったので、こういう機会って重要だなと思いました。
自分は余り読まずに動かしてしまうことが多いのですが、読みながら1つずつやるという重要さを改めて知りました。

参考リンク

https://docs.python.jp/3/tutorial/introduction.html
https://docs.python.jp/3/library/stdtypes.html