2010/10/28

Pythonの基本 型の調べ方

「Python基本 まとめ」に戻る


Release:1.0
Date:October 27, 2010

型名は type() 関数で調べることができます。


type(object)

objectの型を返します。


以下に簡単な例を上げておきます。

>>> type(23)
<type 'int'>
>>> type("cdr")
<type 'str'>
>>> type(u'あいう')
<type 'unicode'>
>>> type([0, 1, 2])
<type 'list'>
>>> type((3, 4, "ab"))
<type 'tuple'>
>>> type({1:"one", 2:"two"})
<type 'dict'>
>>> type(set([1, 2, 3]))
<type 'set'>
>>> type(True)
<type 'bool'>
>>> type(iter([1, 2, 3]))
<type 'listiterator'>
>>> type((x for x in 'hogehoge'))
<type 'generator'>
>>> import re
>>> type(re)
<type 'module'>
>>> type(sum)
<type 'builtin_function_or_method'>

ちなみに、型の名前だけを取り出したいときには __name__ を使います。

>>> type(list('aiueo')).__name__
'list'

ときどき便利なので覚えておいて損はないと思います。


「Python基本 まとめ」に戻る

2010/10/27

Pythonの基本 数の基本

「Python基本 まとめ」に戻る


Release:1.0
Date:October 27, 2010
自分用に書いたPythonの数についてのまとめです。
間違いや勘違いが多分に含まれている可能性があるので、参考にされる方は
公式のドキュメントなどで確認をお願いします。

注) 印はまだ自身のないところです。

テスト環境

テスト環境
OS: Ubuntu10.10
Python 2.5.2
Python 2.6.6
Python 3.1.2

– はじめに

Pythonには、整数などに対する型を宣言する必要はありません。
たとえば、cで整数を扱おうとすると、
#include <stdio.h>
int n;
int main(void)
{
  n = 4 + 6;
  printf("「4 + 6」の計算結果を表示します\n");
  printf("4 + 6 = %d\n", n);
  return 0;
}
などと変数 nint (整数)で型を指定する必要がありますが、
Python2.6以上の場合は(3系の場合は u を省いてください)、単純に
>>> n = 4 + 6
>>> print(u"「4 + 6」の計算結果を表示します\n  4 + 6 = {0}".format(n))
「4 + 6」の計算結果を表示します
  4 + 6 = 10

とするだけですみます。

動的な型付けは便利なぶん、欠点もありますので、「Pythonとはこのようなものだ」
とわりきって覚えてください。

print文は分けて書いたら \n を書かなくても改行されます。

keisan.py の場合はなら、(3系の場合は u を省いてください)

#-*- coding:utf-8 -*-
n = 4 + 6
print(u"「4 + 6」の計算結果を表示します")
print(u"  4 + 6 = {0}".format(n))

実行をしてみると、

$ python test.py
「4 + 6」の計算結果を表示します
  4 + 6 = 10

というふうになります。

Python2.5では下のように書きます。

#-*- coding:utf-8 -*-
n = 4 + 6
print(u"「4 + 6」の計算結果を表示します")
print(u"  4 + 6 = %d " % n)

という具合です。

おさらいになりますが、 n という変数には数(int, float, long, complex)でも文字列でも
なんでも入れておけるということを覚えてくださいね。

他の言語も学習する人は、同じ変数には違う型をなるべく入れない方がいいかもしれません(※)

ここで出てきた format%d については次の「Pythonの基本 まとめ 文字列」で取り上げたいとおもいます。

– Pythonで扱える数とは

まずはどんな数を使えるのか、さらっとみてみましょう。

















ブール型と複素数型が少し複雑そうですね。
複素数型は私がわからないので、勉強してから追記します。

ブール型については、 —- 論理演算についてもう少し で少し詳しく説明します。

– 演算子

演算子についての詳細は http://docs.python.org/reference/expressions.html#summary を参考に
してください。

まずは、何ができるのかざっと例を上げていきます。

>>> 1 + 1
2
>>> (8 * (12 - 3)) / 2
36
>>> 89 % 3
2
>>> 2 ** 16
6553
>>> True and False
False
>>> True or Flase
True
>>> not True
False
>>> 18 < 89
True
>>> 8 <= 8
True
>>> 1 == True
True
>>> 1 != True
False
>>> 1 is True
False
>>> 1 is not True
True
>>> x = 12
>>> x < 5
False
>>> x < 5 or x > 10
True
>>> x < 5 and x > 10
False
>>> x > 10 and x < 20
True
>>> 10 < x < 20
True
>>> x < 5 or (x > 10 and x < 20)
True
>>> 28 % 9
1
>>> 8 | 2
10
>>> 4 ^ 9
13
>>> 2 << 8
512
>>> ~ 26
-27

四則演算

例を見ていただければ解ると思いますが、数学の四則演算の法則と変わりありません。
一つだけ特殊なものは、 % でしょうか。

% とはあまりを求める演算子、つまり余剰を求めます。

>>> 8 % 3
2

これは、8 ÷ 3 = 2 … 2 の余りの2を出す演算子です。

優先順位

四則演算での優先順位は数学と全く同じです。優先したいものがあれば、丸括弧 ( ) を使います。

>>> 2 * (4 + 2) / 4
3
この例ならば、まずは先に 4 + 2 = 6 を出し、次に、2 × 6 = 12 を出し、最後に
12 ÷ 4 = 3 を出すといった具合です。

いい忘れましたが、 / は ÷ ですので、覚えておいてくださいね。

— 整数型と浮動小数点型の自動型変換

Pythonの型付けは動的だといいましたが、float型とint型を計算する場合はfloat型に
変換されます。 float型の方が優先順位が高い ということになります。
>>> 1 + 0.5
1.5

Pythonでの除算(割り算)

加算(足し算)のときは、int型よりfloat型の方が優先順位が高いといいましたが、除算
(割り算)の商(割り算の答え)の場合はPythonのバージョンによってどちらが優先にな
るのかが変わります。
—- Python2系の場合
Python2系の場合、int型どうしの除算で割り切れない場合の商はint型をまもろうとするので、
float型へは自動で変換されません。float型の方が優先順位が高いといいましたが、この場合
はint型をまもろうとすることが、プログラムとしては当然なのかもしれません。
商が浮動小数点、すなわちfloat型になる場合は、
Python2系の場合、これをint型をまもって小数点以下を切り捨てます。
>>> 3 / 2     # 商は1.5になるはずだが、小数点以下が切り捨てられる
1
>>> 7 / 4     # 商は1.75になるはずだが、小数点以下が切り捨てられる
1
>>> 7 / 8     # 商は0.875になるはずだが、小数点以下が切り捨てられる
0

回避するためにはどちらか、または両方をfloat型にして書く必要があります。

>>> 3.0 / 2
1.5
>>> 7.0 / 4.0
1.75
>>> 7 / 8.0
0.875
—- Python3系の場合
Python3系では、「 / 」演算子は必ず浮動小数点を返すようになりました。
2系のようにint型をまもった(切り捨て算)割り算は「 // 」演算子を使うようにします。
>>> 3 / 2
1.5
>>> 3 // 2
1

ちなみに

2系でも「 // 」演算子は使えるので、将来に3系への移行を考えて、除算はすべて「 // 」
演算子にしておくのもいいかもしれません(※)。

— int型とlong型の自動変換

Python2系の場合は、
CPUが32bitのPCではint型は 0b1111111111111111111111111111111
int((1 << 31) - 1) [ int((2 ** 31) - 1) ] すなわち2147483647までの数字を扱えます。
2147483647を超えると自動的に、long型となり末尾に L が付きます。
long型はメモリの許す限りの数を実質無限に扱えます。
>>> 2147483647       # ここまではint型
2147483647
>>> 2147483647 + 1   # ここからがlong型
2147483648L

Python3系ではlong型は廃止となり、long型がint型となりました。
ですので、末尾に L が付くことはありません。

>>> 2147483647
2147483647
>>> 2147483647 + 1    # 末尾に L が付かない
2147483648

—- 論理演算についてもう少し

Pythonの中の変数は全て、論理値として使う事ができます。
[]、0、""、None のような”空”の値は、偽を意味し、その他の値の時([0]、1、”Hello, world” など)は、真を意味します。

>>> w, x, y, z = [], 0, '', None
>>> w == x == y == z
False
>>> w == False
False
>>> x == False
True
False
>>> y == False
False
>>> z == False
False

偽や真とは、上記のように TrueFalse をそのまま表すものではありません。
ただし、数値の0と1はそのまま TrueFalse と同義になります。

例えば、Python2.6以上は、(3系の場合は u を省いてください)

>>> for i in ([], 0, '', None):
...     if i:
...         print(u"{0!r:4} = 真".format(i))
...     elif not i:
...         print(u"{0!r:4} = 偽".format(i))
...
[]   = 偽
0    = 偽
''   = 偽
None = 偽

Python2.5では

>>> for i in ([], 0, '', None):
...     if i:
...         print(u"%s = 真" % repr(i).ljust(4))
...     elif not i:
...         print(u"%s = 偽" % repr(i).ljust(4))
...
[]   = 偽
0    = 偽
''   = 偽
None = 偽

このような感じとなります。

# 変数 i が真の場合はという意味
if i:
    何かのコード
# 変数 i が偽の場合はという意味
if not i:
    何かのコード

これを覚えておくと便利です。

真の値も試してみましょう。

>>> a, b, c, d = [1], 1, 'hoge', True
>>> a == True
False
>>> a == False
False
>>> b == True
True
>>> b
1
>>> b == False
False
>>> c == True
False
>>> d == True
True
>>> d is True
True

先で説明したように, 1 == True で同義でしたね。

>>> for i in ([1], 1, 'hoge', True):
...     if i:
...         print(u"{0!r:4} = 真".format(i))
...     elif not i:
...         print(u"{0!r:4} = 偽".format(i))
...
[1]  = 真
1    = 真
''   = 真
True = 真

Python2.5では

>>> for i in ([1], 1, 'hoge', True):
...     if i:
...         print(u"%s = 真" % repr(i).ljust(4))
...     elif not i:
...         print(u"%s = 偽" % repr(i).ljust(4))
...
[1]  = 真
1    = 真
''   = 真
True = 真

以上で数の基本は終了です。抜けているものは気づいたときに付け足していきます。

比較の説明などはまた機会があったら記事を書きたいと思います

「Python基本 まとめ」に戻る

2010/10/26

Pythonの基本 まとめ 基本

「Python基本 まとめ」に戻る

Release:1.0
Date:October 26, 2010

自分用に書いたPythonの基本について簡単にまとめたものです。
詳しくは、Pythonのルール まとめを参考にしてみてください。
間違いや勘違いが多分に含まれている可能性があるので、参考にされる方は
公式のドキュメントなどで確認をお願いします。

注) 印はまだ自身のないところです。

テスト環境

テスト環境
OS: Ubuntu10.10
Python 2.5.2
Python 2.6.6
Python 3.1.2

プログラムを書く

インタープリタを起動しないでプログラムを書くときは、基本的には hello_world.py といった
拡張子 .py 形式のファイルを生成してそのファイルの中に記述していきます。

hello_world.py の中身を見てみましょう。

#!/usr/bin/env python
#-*- coding:utf-8 -*-
"""Echo 'your likes words' 5 times"""

# 好きな言葉をいれる
word = "Hello, World!"

# nを5回表示させる  ←これはダメなコメントの例
for i in range(5):
    print(word)

'''
Created on 2010-09-08
@author Komiyam Iyori
'''
を参考にPEP8の規則をしっかりマスターし、プログラムを書いていきましょう。

実行するには、pythonコマンドの引数にスクリプトファイルを入力します。

$ python hello_world.py
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!

多くのバージョンのPythonが入っている場合で、デフォルトのPython以外を使いたいのであれば、

$ python2.5 hello_world.py

$ python3 hello_world.py

としてください。

プログラムを実行ファイルとしたいのならば、ファイルの先頭に

#!/usr/bin/env python

と宣言します。

や公式のドキュメントを参考にしてください。

スクリプトファイルのデフォルトエンコーディングも同様に上記を参考にしてください。

この場合、

$ ./hello_world.py
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!

として、実行できます。

前回からPEP8、PEP8と口酸っぱくいっていますが、縛られすぎもよくありません。

例えは、

import os, os.path

i = 1
if i: print("ok")
elif not i: print("no")

なども書く場合があるかもしれません。これも全然OKです。
臨機応変に対応できたほうがいい場合もあります。可読性が高ければ、それでOKなのではないでしょうか(※)

コメント文を書く

基本となるものはすでに Pythonのルール まとめ ¶コメント
に書きましたが、取りあえずもう一度説明をしておきたいと思います。というか一緒に勉強していきましょう。
すべての行の最大の長さは 79 文字とする

プログラムの1行の長さは、上記のようにするというだいたいの決まりがあります。

これに対してコメントやドキュメンテーションは、

長いテキストのブロック(docstring やコメント)に関しては、72文字以下を推奨する。

と72文字を目安に改行したらいいとおもいます。

ドキュメンテーションの書き方は人それぞれですが、 Pythonのルール まとめ ¶コメント
に少しだけ、例をあげました。

インラインコメント について。

インラインコメントは控えめに使う。

ということらしいです。

インラインコメントは、文と同じ行に入れるコメントである。インライン
コメントは、控えめに使う。コメントは、文から少なくともスペース2個分
離しておき、# とスペース1個で始まるようにするべきである。

これも人それぞれですが、基本は上記のことを守りましょう。

最後に、これが一番重要なことですが、

分かりきったことを書くくらいなら、インラインコメントは不要で、むしろ
気が散るものである。次のようなことを書かないように。

    x = x+1                 # Increment x

しかし場合によっては、次のようなコメントは有用である。

    x = x+1                 # Compensate for border

ということです。

ソースを読む人は大抵はプログラムの知識のある人ですから、見てわかるようなプログラムの内容へのコメントは書かないほうがよいでしょう。
私もまだよくわかっていませんが、プログラムを書いて見せて叱られて覚えていきましょう。

Note

引用 ふるかわとおるさん和訳PEP8: http://oldriver.org/python/pep-0008j.html

つづく

基本となることは、これ以外にもあると思うので、思いつき次第追加していきます。


「Python基本 まとめ」に戻る

2010/10/22

文字列を一文字ずつに区切りたい

Release:1.1
Date:October 27, 2010

テスト環境
OS: Ubuntu10.10
Python2.6.6
Python2.5.2
Python3.1.2:

timeitモジュールで計測しました

単純に、リストで区切る場合

string = "I like spam."
list(string)
['I', ' ', 'l', 'i', 'k', 'e', ' ', 's', 'p', 'a', 'm', '.']

これを基準として速いか、遅いかを見比べてみてください。

■2系で計測

>>> from timeit import Timer
>>> print(Timer("list(string)", 'string = "I like spam."').repeat(number=100000))
[0.16765809059143066, 0.16036391258239746, 0.15968680381774902]

■3系で計測

3系はめちゃくちゃ速いですね。なぜだろう。

>>> from timeit import Timer
>>> print(Timer("list(string)", 'string = "I like spam."').repeat(number=100000))
[0.08800315856933594, 0.07892894744873047, 0.07909393310546875]

タブルで区切る場合

タブルは変更不可能なオブジェクトなので、中身を変更したいのであれば、リストを使います。

tuple(string)
('I', ' ', 'l', 'i', 'k', 'e', ' ', 's', 'p', 'a', 'm', '.')

■2系で計測

tuple() の方が速いことがわかります。

>>> from timeit import Timer
>>> print(Timer("tuple(string)", 'string = "I like spam."').repeat(number=100000))
[0.15124106407165527, 0.1477971076965332, 0.14786481857299805]

■3系で計測

とんでもなく速いです。

>>> from timeit import Timer
>>> print(Timer("tuple(string)", 'string = "I like spam."').repeat(number=100000))
[0.057578086853027344, 0.048284053802490234, 0.04918789863586426]

for文で回す

slist = []
for i in string:
    slist.append(i)
print(slist)
['I', ' ', 'l', 'i', 'k', 'e', ' ', 's', 'p', 'a', 'm', '.']

■2系で計測

リスト内包表記よりも少し遅いです。

>>> from timeit import Timer
>>> print(Timer("for i in string: slist.append(i)", 'string = "I like spam."; slist = []').repeat(number=100000))
[0.29812097549438477, 0.28661012649536133, 0.28408908843994141]

■3系で計測

>>> from timeit import Timer
>>> print(Timer("for i in string: slist.append(i)", 'string = "I like spam."; slist = []').repeat(number=100000))
[0.1907179355621338, 0.17830896377563477, 0.1801919937133789]

リスト内包表記で

[string[i] for i in range(len(string))]
['I', ' ', 'l', 'i', 'k', 'e', ' ', 's', 'p', 'a', 'm', '.']

■2系で計測

list() よりちょっとだけ遅いです。

>>> from timeit import Timer
>>> print(Timer("[string[i] for i in range(len(string))]", 'string = "I like spam."').repeat(number=100000))
[0.25739216804504395, 0.25221920013427734, 0.25009298324584961]

■3系で計測

なぜ2系よりも遅くなるんだろう、、、

>>> from timeit import Timer
>>> print(Timer("[string[i] for i in range(len(string))]", 'string = "I like spam."').repeat(number=100000))
[0.2948720455169678, 0.2680661678314209, 0.2661139965057373]

もっとスマートに。

[x for x in string]
['I', ' ', 'l', 'i', 'k', 'e', ' ', 's', 'p', 'a', 'm', '.']

■2系で計測

さっきよりは速いですが、ちょっとだけ遅いです。

>>> from timeit import Timer
>>> print(Timer("[x for x in string]", 'string = "I like spam."').repeat(number=100000))
[0.21160793304443359, 0.20747208595275879, 0.20700287818908691]

■3系で計測

>>> from timeit import Timer
>>> print(Timer("[x for x in string]", 'string = "I like spam."').repeat(number=100000))
[0.15147709846496582, 0.13379907608032227, 0.13328313827514648]

イテレータを使う

>>> list(iter(string))
['I', ' ', 'l', 'i', 'k', 'e', ' ', 's', 'p', 'a', 'm', '.']

■2系で計測

イテレータなので、速いと思ったのですが、ちょっとだけ遅かったので残念です。

>>> from timeit import Timer
>>> print(Timer("list(iter(string))", 'string = "I like spam."').repeat(number=100000))
[0.25499391555786133, 0.24986600875854492, 0.25281620025634766]

■3系で計測

>>> from timeit import Timer
>>> print(Timer("list(iter(string))", 'string = "I like spam."').repeat(number=100000))
[0.17393803596496582, 0.15649890899658203, 0.15940403938293457]

イテレータのリスト内包表記では?

>>> [x for x in iter(string)]
['I', ' ', 'l', 'i', 'k', 'e', ' ', 's', 'p', 'a', 'm', '.']

■2系で計測

さっきより少しだけ早くなったのはなぜだろう?

>>> from timeit import Timer
>>> print(Timer("[x for x in iter(string)]", 'string = "I like spam."').repeat(number=100000))
[0.23363304138183594, 0.22565793991088867, 0.22397589683532715]

■3系で計測

>>> from timeit import Timer
>>> print(Timer("[x for x in iter(string)]", 'string = "I like spam."').repeat(number=100000))
[0.16548705101013184, 0.15951204299926758, 0.16102194786071777]

ジェネレータを使う

list(x for x in string)
['I', ' ', 'l', 'i', 'k', 'e', ' ', 's', 'p', 'a', 'm', '.']

■2系で計測

なぜかジェネレータ少し遅い…。どうしてだろう。

>>> from timeit import Timer
>>> print(Timer("list(x for x in string)", 'string = "I like spam."').repeat(number=100000))
[0.51497101783752441, 0.50594687461853027, 0.50299286842346191]

■3系で計測

>>> from timeit import Timer
>>> print(Timer("list(x for x in string)", 'string = "I like spam."').repeat(number=100000))
[0.30000805854797363, 0.2889258861541748, 0.2886178493499756]

Note

@ucqさん教えてくださってありがとうございます。

map()を使って

■Python2系のみ

map(None, string)
['I', ' ', 'l', 'i', 'k', 'e', ' ', 's', 'p', 'a', 'm', '.']

私の環境での結果ですので、なんとも言えませんが list() よりも速いです。すごい。

■2系で計測

>>> from timeit import Timer
>>> print(Timer("map(None, string)", 'string = "I like spam."').repeat(number=100000))
[0.15664911270141602, 0.14838504791259766, 0.15289902687072754]

■Python3系にも対応するならlambdaを使う

[x for x in map(lambda x: str(x), string)]
['I', ' ', 'l', 'i', 'k', 'e', ' ', 's', 'p', 'a', 'm', '.']

■3系で計測

遅いです。3系は list() がめちゃくちゃ速いので map() での工夫は必要無いでしょう。

>>> from timeit import Timer
>>> print(Timer("[x for x in map(lambda x: str(x), string)]", 'string = "I like spam."').repeat(number=100000))
[0.8320629596710205, 0.8203780651092529, 0.8253698348999023]

いろいろ使えそうなmap

map(lambda *x: ''.join(i for i in x if x), *(iter(string),))

■2系で計測

ものすごく遅いのは、いろいろな計算が入っているからでしょう。

>>> from timeit import Timer
>>> print(Timer("map(lambda *x: ''.join(i for i in x if x), *(iter(string),))", 'string = "I like spam."').repeat(number=100000))
[4.4103279113769531, 4.5268661975860596, 4.3819801807403564]

■3系で計測

2系と3系でここまで違うのはびっくりです。5回計測しなおしましたけど、結果はほとんど同じです。
どうしてこんなに速いんだろう。
>>> from timeit import Timer
>>> print(Timer("map(lambda *x: ''.join(i for i in x if x), *(iter(string),))", 'string = "I like spam."').repeat(number=100000))
[0.08291816711425781, 0.07854080200195312, 0.07890486717224121]

Note

参考
くだすれPython(超初心者用) その7

正規表現で

import re
re.findall('.', string)
['I', ' ', 'l', 'i', 'k', 'e', ' ', 's', 'p', 'a', 'm', '.']

■2系で計測

>>> from timeit import Timer
>>> print(Timer("re.findall('.', string)", 'import re; string = "I like spam."').repeat(number=100000))
[0.54255795478820801, 0.53627490997314453, 0.53358101844787598]

■3系で計測

モジュールのimport時間も含まれているのでなんとも言えませんが、少し遅いです。

>>> from timeit import Timer
>>> print(Timer("re.findall('.', string)", 'import re; string = "I like spam."').repeat(number=100000))
[0.6249690055847168, 0.6120860576629639, 0.6094479560852051]

Note

参考
くだすれPython(超初心者用) その7

もう何がしたいのかわからないとき

■2系のみ

map(lambda x: ''.join(tuple(x)), string)
['I', ' ', 'l', 'i', 'k', 'e', ' ', 's', 'p', 'a', 'm', '.']

遅いです。 map() が早かったぶん少し期待したのですが…。

■2系で計測

>>> from timeit import Timer
>>> print(Timer("map(lambda x: ''.join(tuple(x)), string)", 'string = "I like spam."').repeat(number=100000))
[2.1053709983825684, 2.0983140468597412, 2.1047899723052979]

■3系では

[x for x in map(lambda x: ''.join(tuple(x)), string)]
['I', ' ', 'l', 'i', 'k', 'e', ' ', 's', 'p', 'a', 'm', '.']

■3系で計測

めちゃくちゃ意外な健闘ぶりです。Python3.1.2で計測したからでしょうか。しかし遅いです。

>>> from timeit import Timer
>>> print(Timer("[x for x in map(lambda x: ''.join(tuple(x)), string)]", 'string = "I like spam."').repeat(number=100000))
[1.1109910011291504, 1.100836992263794, 1.0959341526031494]

もう何がしたいかわからないとき2

[x for x, y in zip(string, string)]
['I', ' ', 'l', 'i', 'k', 'e', ' ', 's', 'p', 'a', 'm', '.']

■2系で計測

ワケがわからない部門では見事1位です。yを捨てているぶん、無駄があるんでしょう。

>>> from timeit import Timer
>>> print(Timer("[x for x, y in zip(string, string)]", 'string = "I like spam."').repeat(number=100000))
[0.45125102996826172, 0.44776821136474609, 0.44718194007873535]

■3系で計測

>>> from timeit import Timer
>>> print(Timer("[x for x, y in zip(string, string)]", 'string = "I like spam."').repeat(number=100000))
[0.2564361095428467, 0.25066709518432617, 0.2534060478210449]

結論(仮)

一文字ずつに区切るときは、 list()tuple()map() を使いましょう。
できれば、 tuple() で、変更可能オブジェクトにしたいときには、 list()map() を使いましょう。

しかし、

Note

英語がわからないから、読めない。

によれば、map(None, x)は使えなくなるみたいなので、素直に、 list() を使ったほうがいいみたいでした。

イテレータやジェネレータを使ったら、メモリの消費量が抑えられる?? ので、できるだけ使いましょう。

2系と3系の差に驚かされた実験でした。

つづく

募集中です。


[2010-10-27] イテレータとジェネレータを追加。timeitモジュールの計測結果を追加。

2010/10/18

ワンライナーで自分の名前を表示する(8しばり)

Python Recipesに戻る
Text Recipesに戻る

Release:1.0
Date:October 18, 2010

テスト環境
OS: Ubuntu10.10

Python2系

Python2.6.6及び、Python2.5.2でテストを行ないまいた。

はじめに

私の尊敬している西尾さんが、7しばりで自分の名前を表示するコードを書いて
いらっしゃったので自分もしてみたくなりました。

7しばりではなく、末広がりの8で頑張ってみようと思います。

ちなみに、西尾さんのコードはこれです

$ python -c "print ''.join(chr(x) for x in(77777/7/77, 77+777/7, 77-7%~77, 7-~777-7*77, ~7%77+~-77, 77^77+77, ~7&77+77, -777/~7)).decode('cp932')"
最後の「.decode(‘cp932’)」は私のOSがUbuntuなので、文字化け対策のために自分で書き加えたものです。
Windowsの場合はdecode~はいらないと思います。

Note

このコードのライセンスがわかりませんでした(TT)
もし、不具合があった場合は削除しますので、ご連絡をおねがいします。

手順1

まずは、自分の名前を1コードずつに区切り、文字コードに対応する整数を導き出します。
文字コードは UTF-8 にしてあります。
ord_num = tuple(map(ord, u"込山伊典".encode('utf8')))
print(ord_num)
(232, 190, 188, 229, 177, 177, 228, 188, 138, 229, 133, 184)

Note

Python2.6では
from __future__ import unicode_literals
とすると文字列がすべてunicode扱いになるので、 u が省けます。

手順2 8で表すことができる数を洗い出す

8という整数だけで、ord_nameのlistにある整数を表さないといけません。
そこで、8という整数ではどのような整数をあらわせるか洗い出してみましょう。
~8で       -9     -0b1001
~8/8で     -2     -0b10
8/~8で     -1     -0b1
8/8で       1      0b00000001
88%~-8      4      0b00000100
~8%8で      7      0b00000111
8で         8      0b00001000
888/88で    10     0b00001010
88/8で      11     0b00001011
~-88/~-8で  12     0b00001100
888/8/8で   13     0b00001101
8+8で       16     0b00010000
8+8+8で     24     0b00011000
88^88+8で   56     0b00111000
8*8で       64     0b01000000
~8%88で     79     0b01001111
~8&88で     80     0b01010000
88          88     0b01011000
88+8        96     0b01100000
8888/88で   101    0b01100101
888/8で     111    0b01101111
88+88       176    0b10110000

このくらい分かれば十分かな???

手順3 手順2を基本に整数を導き出す

まずは、ord_num[0]の232を8だけで表す。

232 0b11101000

これは、
88+88 176 0b10110000
88 88 0b01011000
との排他的論理和でできそうだ。
>>> 88^88+88
232

ビンゴ!!

残り11個
(88^88+88, 190, 188, 229, 177, 177, 228, 188, 138, 229, 133, 184)

ord_num[1]の190を8だけで表す。

190 0b10111110

これは単純に、111に88を足し9を引いても短く書ける
よって、 ~8+88+888/8
>>> ~8+88+888/8
190
残り10個
(88^88+88, ~8+88+888/8, 188, 229, 177, 177, 228, 188, 138, 229, 133, 184)

ord_num[2]の188を8だけで表す。

188 0b10111100

これも単純に、101に88を足し9をを引いて8を足す。
うまいやり方が思いつかない。
よって、 ~8+88+8888/88+8
>>> ~8+88+8888/88+8
188
残り9個
(88^88+88, ~8+88+888/8, ~8+88+8888/88+8, 229, 177, 177, 228, 188, 138, 229, 133, 184)

ord_num[3]の229を8だけで表す。

229 0b11100101

ord_num[0]の232に10を足し7をを引く。
(88^88+88)-(888/88-~8%8)
カッコをつけなくてはいけないので却下。

まずは、88と101の排他的論理和を求める。

88 88 0b01011000
88+888/8/8 101 0b01100101

求められる数は
88^88+888/8/8 61 0b00111101

あとは、88を足して出来上がり!

>>> 88^88+888/8/8+88
229
残り8個
(88^88+88, ~8+88+888/8, ~8+88+8888/88+8, 88^88+888/8/8+88, 177, 177, 228, 188, 138, 229, 133, 184)

ord_num[4]の117を8だけで表す。

117 0b1110101

これは簡単!!
176に1を足せばできる。

88+88+8/8

>>> 88+88+8/8
177
残り7個
(88^88+88, ~8+88+888/8, ~8+88+8888/88+8, 88^88+888/8/8+88, 88+88+8/8, 177, 228, 188, 138, 229, 133, 184)

ord_num[5]も117なので省略。

残り6個
(88^88+88, ~8+88+888/8, ~8+88+8888/88+8, 88^88+888/8/8+88, 88+88+8/8, 88+88+8/8, 228, 188, 138, 229, 133, 184)

ord_num[6]は228。

これは、229に1引いたものだから。

229 88^88+888/8/8+88
1 8/8
よって
88^88+888/8/8+88+8/8
>>> 88^88+888/8/8+88-8/8
228
残り5個
(88^88+88, ~8+88+888/8, ~8+88+8888/88+8, 88^88+888/8/8+88, 88+88+8/8, 88+88+8/8, 88^88+888/8/8+88+8/8, 188, 138, 229, 133, 184)

ord_num[7]は188。

188 0b10111100
なんか、88+88 176 0b10110000
に近い感じがする。
~-88/~-8の12を足せば完成だ!!
論理和 | のほうが、短いのでこれを適用して。

88+88|~-88/~-8

>>> 88+88|~-88/~-8
188
残り4個
(88^88+88, ~8+88+888/8, ~8+88+8888/88+8, 88^88+888/8/8+88, 88+88+8/8, 88+88+8/8, 88^88+888/8/8+88+8/8, 88+88|~-88/~-8, 138, 229, 133, 184)

ord_num[8]は138。

138 0b10001010
8888/88で 101 0b01100101
これをベースに考えてみる。だんだん面倒くさくなってきた。
これに11と16を足せば完成だ。

888/8+88/8+8+8

>>> 888/8+88/8+8+8
138
残り3個
(88^88+88, ~8+88+888/8, ~8+88+8888/88+8, 88^88+888/8/8+88, 88+88+8/8, 88+88+8/8, 88^88+888/8/8+88+8/8, 88+88|~-88/~-8, 888/8+88/8+8+8, 229, 133, 184)

ord_num[9]の229は前にも書いたので省略。

>>> 88^88+888/8/8+8
229
残り2個
(88^88+88, ~8+88+888/8, ~8+88+8888/88+8, 88^88+888/8/8+88, 88+88+8/8, 88+88+8/8, 88^88+888/8/8+88+8/8, 88+88|~-88/~-8, 888/8+88/8+8+8, 88^88+888/8/8+8, 133, 184)

ord_num[10]は133。

133 0b10000101
8888/8 111 0b01101111
これをベースに考えてみる。
8+8+8 24 0b00011000
を足してみると、135 0b10000111
あとは2を引いてやるだけだ。

888/8+8+8+8+~8/8

>>> 888/8+8+8+8+~8/8
133
残り1個
(88^88+88, ~8+88+888/8, ~8+88+8888/88+8, 88^88+888/8/8+88, 88+88+8/8, 88+88+8/8, 88^88+888/8/8+88+8/8, 88+88|~-88/~-8, 888/8+88/8+8+8, 88^88+888/8/8+88, 888/8+8+8+8+~8/8, 184)

ord_num[11]の184は簡単だ。

88+88 176 0b10110000
に8を足してやるだけでいい。

88+88+8

>>> 88+88+8
184
残り0個
(88^88+88, ~8+88+888/8, ~8+88+8888/88+8, 88^88+888/8/8+88, 88+88+8/8, 88+88+8/8, 88^88+888/8/8+88+8/8, 88+88|~-88/~-8, 888/8+88/8+8+8, 88^88+888/8/8+88, 888/8+8+8+8+~8/8, 88+88+8)

完成!!

これを西尾さんのはforで回してありましたが、map()という便利な関数が増えているので、これを使って

>>> print(''.join(map(chr, (88^88+88, ~8+88+888/8, ~8+88+8888/88+8, 88^88+888/8/8+88, 88+88+8/8, 88+88+8/8, 88^88+888/8/8+88-8/8, 88+88|~-88/~-8, 888/8+88/8+8+8, 88^88+888/8/8+88, 888/8+8+8+8+~8/8, 88+88+8)))).decode('utf8')
込山伊典
ちゃんと出来ています!!!
長ったらしいコードになってしまったのは、自分がビット演算をよく理解していないためです、、、

ちなみに、ターミナルや端末からは、

$ python -c "print(''.join(map(chr, (88^88+88, ~8+88+888/8, ~8+88+8888/88+8, 88^88+888/8/8+88, 88+88+8/8, 88+88+8/8, 88^88+888/8/8+88-8/8, 88+88|~-88/~-8, 888/8+88/8+8+8, 88^88+888/8/8+88, 888/8+8+8+8+~8/8, 88+88+8)))).decode('utf8')"

で表示されます。コマンドとしてはちょっと長すぎですが、、、

Python3系

※Python3.1.2でテストを行ないました。

手順1

まずは、自分の名前を1コードずつに区切り、文字コードに対応する整数を導き出します。
文字コードは UTF-8 にしてあります。
まだPython3の勉強はあまりしていないのですが、byte文字に対しては ord() しなくても
文字コード1文字の整数が求められるようです。(※要確認)

ord() はunicode文字列のみで動作するみたいです(※要確認)

ord_num = tuple("込山伊典".encode('utf8'))
print(ord_num)
(232, 190, 188, 229, 177, 177, 228, 188, 138, 229, 133, 184)

手順2

2系の手順2と同じようにします。

残り0個
(88^88+88, ~8+88+888/8, ~8+88+8888/88+8, 88^88+888/8/8+88, 88+88+8/8, 88+88+8/8, 88^88+888/8/8+88+8/8, 88+88|~-88/~-8, 888/8+88/8+8+8, 88^88+888/8/8+88, 888/8+8+8+8+~8/8, 88+88+8)

完成!!

Python3では bytes() という関数があります。
これはunicode以外の文字列を扱うときに使うものだと思います。
これを使えば、sourceを(2系の) chr() した上で join することができるのだと、
たぶん思います(※要確認)
2系の unichr() は3系の chr() で置き換わっていますので、byte文字列に使う
ことができなくなっているのです。

このとき、注意すべきは、 .decode() はバイト文字にしか適用てきないということです。

Python2系では

print(u'あ'.encode('cp932')).decode('cp932')
あ

などと、プリント文のあとにも適用できましたが、Python3系では必ず、byte文字のあとに適用しましょう。

print('あ'.encode('cp932').decode('cp932'))
あ

んん〜いいたいことがうまく伝えられてないような、、、
詳しくは、ページ最下部にある 参考・引用 をご覧ください。

print(bytes((88^88+88, ~8+88+888//8, ~8+88+8888//88+8, 88^88+888//8//8+88, 88+88+8//8, 88+88+8//8, 88^88+888//8//8+88-8//8, 88+88|~-88//~-8, 888//8+88//8+8+8, 88^88+888//8//8+88, 888//8+8+8+8+~8//8, 88+88+8)).decode('utf8'))

ちなみに、ターミナルや端末からは、

$ python -c "print(bytes((88^88+88, ~8+88+888//8, ~8+88+8888//88+8, 88^88+888//8//8+88, 88+88+8//8, 88+88+8//8, 88^88+888//8//8+88-8//8, 88+88|~-88//~-8, 888//8+88//8+8+8, 88^88+888//8//8+88, 888//8+8+8+8+~8//8, 88+88+8)).decode('utf8'))"

で表示されます。コマンドとしてはちょっと長すぎですが、、、

2系と3系をどちらもいれていらっしゃる場合は、

$ python3 -c "print(bytes((88^88+88, ~8+88+888//8, ~8+88+8888//88+8, 88^88+888//8//8+88, 88+88+8//8, 88+88+8//8, 88^88+888//8//8+88-8//8, 88+88|~-88//~-8, 888//8+88//8+8+8, 88^88+888//8//8+88, 888//8+8+8+8+~8//8, 88+88+8)).decode('utf8'))"

おわりに

Python3でのやり方はtwitterで @ucq さんに教えていただきました!!
親切に教えていただきありがとうございます!

参考・引用

Note