カテゴリ:Python( 9 )

Python datetime モジュール

Pythonの datetime モジュールを使ってみた。今日の日付と曜日を調べた。

>>> from datetime import date
>>> today = date.today()
>>> today.isoformat()
'2018-01-12'
>>> today.year
2018
>>> today.month
1
>>> today.day
12
>>> today.weekday()
4
>>> weekday = 'Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday'.split(',')
>>> weekday
['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
>>> weekday[today.weekday()]
'Friday'

ついでに安室奈美恵の生まれた日の曜日を求めた。

>>> amuro = date(1977,9,20)
>>> weekday[amuro.weekday()]
'Tuesday'

Python 言語の基本的な使い方が分かったら、あとは標準や非標準のモジュールを調べて、問題解決に使う必要がある。しかし、そのためにはプログラミングで解決すべき問題がなければならない。さしあたって解決したい問題がないというのがアマチュアプログラマの弱点だ。

自分が何をやりたいのか。それを解決するためのプログラミング言語以外の知識を持ち合わせているか。そちらのほうを明確にするのが必要なのだ。

[PR]
by tnomura9 | 2018-01-12 12:54 | Python | Comments(0)

Python で木構造を扱う

関数型言語でよく使われるデータ構造にはリスト型以外に木構造がある。Haskell の場合には木構造は代数的データ型で定義する。例えば次のようにノードには左右の木構造を収め、リーフに文字列を収めるデータ構造 Tree を定義する。

Prelude> data Tree = Node Tree Tree | Leaf String deriving Show
Prelude> let a = Node (Leaf "hello") (Node (Leaf "world") (Leaf "."))

この木構造のリーフの値を列挙する go_around 関数は次のように再帰的に定義できる。

Prelude> :{
Prelude| let
Prelude| go_around (Leaf str) = [str]
Prelude| go_around (Node left right) = go_around(left) ++ go_around(right)
Prelude| :}
Prelude> go_around a
["hello","world","."]

Python で木構造を定義するには、ノードとリーフのデータ型をクラスで定義する。Haskell で代数的データ型で定義したところを、Python ではクラスを定義することで木構造のデータ型を作ることになる。代数的データ型のデータがクラスのインスタンスに相当するわけだ。こう考えると、クラスの定義の、新しいデータ型を定義するという一面が明らかになる。あるいは、オブジェクト指向言語のクラスの性質の一部は Haskell の代数的データ型として捉えることができることが分かる。

class Node:
    def __init__(self,left,right):
        self.left = left
        self.right = right

class Leaf:
    def __init__(self,data):
        self.data = data
    def get(self):
        return self.data
def go_around(tree):
    if isinstance(tree, Leaf):
        return [tree.get()]
    else:
        return go_around(tree.left) + go_around(tree.right)

a = Node(Leaf('hello'), Node(Leaf(','), Leaf('world')))
print(go_around(a))

実行例

================== RESTART: C:/Users/****/Desktop/tree.py ==================
['hello', ',', 'world']

このように、Haskell の代数的データ型とPythonのクラス定義の意味合いが同じ事が分かる。どんなプログラム言語であれ、プログラムをデータ構造とアルゴリズムとして捉えるとそれらのプログラム言語の本質をとらえて活用できるような気がする。データ構造とアルゴリズムへのアプローチの仕方に言語の特徴がみられるだけだからだ。
Python という言語の使い方がなんとなくわかった気がするので Python 関係の記事はこれで終わりにする。AI との関連性についてはもう少し沈黙して勉強しないといけない。


[PR]
by tnomura9 | 2018-01-08 15:49 | Python | Comments(0)

list.pop()

Python のリスト型のメソッドの list.pop() と list.append() を使うとスタックが実装できる。スタックを使ったプログラムの例として逆ポーランド記法で記述した整数の電卓を作ってみた。コマンドラインから逆ポーランド記法でスペース区切りで数式を入力すると計算をしてくれる。数値のスタックが動的にどのように推移していくかを見ることができる。

実行例

input expression: 1 2 + 3 *
[1]
[1, 2]
[3]
[3, 3]
[9]
9

プログラム(IDLEを使って作ったが、シェルからも動かせるはず。)

value_stack = []
stack = []

def calc(stack):
    for acc in stack:
        if acc.isdigit():
            value_stack.append(int(acc))
        elif acc == '+':
            a = value_stack.pop()
            b = value_stack.pop()
            value_stack.append(a+b)
        elif acc == '*':
            a = value_stack.pop()
            b = value_stack.pop()
            value_stack.append(a*b)
        print(value_stack)

while True:
    line = input('input expression: ')
    if line == 'quit': break
    stack = line.split(' ')
    calc(stack)
    print(value_stack.pop())


[PR]
by tnomura9 | 2018-01-07 19:04 | Python | Comments(0)

reduce()

Python 3.6 の組み込み関数からは reduce() が削除されていたので作ってみた。

>>> def reduce(f,x,xs):
...     for y in xs:
...         x = f(x,y)
...     return x
...
>>> reduce(lambda x,y: x+y, 0, [1,2,3,4,5])
15
>>> reduce(lambda x,y: x*y, 1, [1,2,3,4,5])
120

これは Haskell の foldl に相当する。f(x,y) = x+y, x =0, xs = [1,2,3,4,5] の場合この関数は次のような計算になる。

reduce(f,0,[1,2,3,4,5]) = (((((0+1)+2)+3)+4)+5)

つまり、リスト [1,2,3,4,5] の要素を先頭からアキュームレータ x に加算していく。Haskell の場合は変数への再代入ができないので、右項の計算式を展開していってリストの最後まで展開していった時点で式の計算を行っている。Python のような手続き型言語は、変数への再代入が可能なので明示的に x をアキュームレータにしてループを回すことで計算できる。

Haskell のリスト関連の関数で、foldr, foldl は難解なほうだが、手続き型の言語と比較することで、その意味がよくわかる。

reduce と foldl のような等価な動作をする関数を関数型と手続き型とで対比することによって、それぞれのコードの意味がよくわかるのが面白い。

[PR]
by tnomura9 | 2018-01-06 19:12 | Python | Comments(0)

Python のリスト処理

Python のリスト処理の組み込み関数には map() と fliter() がある。ネットの記事では reduce() もあるようだが、手元の環境では組み込み関数から除かれているようだ。

map() 関数はリストの要素に同じ関数を適用したリストを作成する。また、filter() 関数はリストの要素のうち特定の条件を満たすものを抜き出したリストを作成する。

>>> list(map(lambda x: x*x, [1,2,3,4,5]))
[1, 4, 9, 16, 25]
>>> list(filter(lambda x: x % 2 == 0, [1,2,3,4,5]))
[2, 4]

これは次のようにリストの内包表記を使うこともできる。処理速度はこちらのほうが速い。ロジックの読みやすさも損なわれていない。

>>> [x*x for x in [1,2,3,4,5]]
[1, 4, 9, 16, 25]
>>> [x for x in [1,2,3,4,5] if x % 2 == 0]
[2, 4]

リストの要素を取り出したり、部分リストを取り出すにはスライス [x : y] を使う。これを利用すると Haskell 風にリストの先頭の要素を取り出す head や、リストの先頭以外の部分を取り出す tail を定義できる。

>>> head = lambda x: x[0]
>>> tail = lambda x: x[1:]
>>> head([1,2,3,4,5])
1
>>> tail([1,2,3,4,5])
[2, 3, 4, 5]

head と tail を利用して Haskell のパターン (x:xs) を利用した再帰的な関数の定義のようなことができる。

>>> def double(x):
...    if x == []: return x
...    else: return [head(x)*2] + double(tail(x))
...
>>> double([1,2,3,4,5])
[2, 4, 6, 8, 10]

何が何でも関数型でプログラムするのは行き過ぎだが、リスト処理をするとループを記述するより見通しのよいプログラムが書ける場合がある。特に内包表記は速度も速いし、map と filter ができることは簡潔に記述できる。ただし入れ子になったループの場合内包表記ではかえって可読性の低いプログラムになってしまうことがある。適材適所が必要だ。

[PR]
by tnomura9 | 2018-01-06 09:04 | Python | Comments(0)

str.format() その2

str.format() メソッドのもう一つの機能は {} の中に記述するデータの書式設定だ。前回の記事で紹介したサイトの引用になるが次のような書式設定が可能だ。書式設定は {} の中に : (コロン)の後に記述する。{0:X} のように format の引数のインデックスと組み合わせることもできる。

左寄せ、センタリング、右寄せ

>>> '{:<10}'.format('hello')
'hello '
>>> '{:^10}'.format('hello')
' hello '
>>> '{:>10}'.format('hello')
' hello'

数値(10進、16進、8進、2進)

>>> 'int: {0:d}; hex: {0:X}; oct: {0:o}; bin: {0:b}'.format(42)
'int: 42; hex: 2A; oct: 52; bin: 101010'

3桁のカンマ区切り

>>> '{:,d}'.format(1234567)
'1,234,567'

固定小数点

>>> '{:.3f}'.format(12.34)
'12.340'

日付フォーマット

>>> d =datetime.datetime.now()
>>> '{:%Y-%m-%d %H:%M:%S}'.format(d)
'2018-01-05 13:11:19'

これだけの知識でも数値計算の書式付きの出力は簡単に記述できる。

>>> for i in range(1,11):
...     print('{0:3} {1:3} {2:5.3f}'.format(i,i*i,math.sqrt(i)))
...
 1   1 1.000
 2   4 1.414
 3   9 1.732
 4  16 2.000
 5  25 2.236
 6  36 2.449
 7  49 2.646
 8  64 2.828
 9  81 3.000
10 100 3.162


[PR]
by tnomura9 | 2018-01-05 13:27 | Python | Comments(0)

str.format()

Python でモニタに出力するときは str.format() 関数で体裁を整えるのが便利だ。format() は 文字列型(str 型)オブジェクトのメソッドだ。出力を表示するテンプレートになる文字列にデータを供給する働きがある。次のサイトを読めば概要がよくわかるが、自分でもいろいろ試してみた。

format関数による文字列フォーマット(新しい形式 / 3.6対応)

format は文字列オブジェクトのメソッドなので、文字列のドット記法になる。つまり文字列中の {} の中に引数を注入する働きがある。format の引数は、文字列でも、数値でも、変数でもよい。データは {} の中に表示される。

>>> 'My name is {}'.format('John')
'My name is John'
>>> 'pi = {}'.format(3.14)
'pi = 3.14'
>>> m = 'Mary'
>>> 'I love {}'.format(m)
'I love Mary'

文字列中の {} の数は複数個でもよい。この場合 format の引数は順番に {} に代入されていく。引数の数が {} より少ない場合はエラーになるが、多すぎる場合は単に無視されるだけだ。

>>> '{} {} {}'.format(0, 1, 2)
'0 1 2'
>>> '{} {} {}'.format(0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: tuple index out of range
>>> '{} {} {}'.format(0, 1, 2, 3)
'0 1 2'

文字列中の {} は {0}, {1} のようにインデックスをつけることで format() の引数を指定することができる。これを利用すると表示の順番を自由に変えることができる。

>>> '{0} {1}'.format('hello', 'world')
'hello world'
>>> '{0} {0}'.format('hello', 'world')
'hello hello'
>>> '{1} {0}'.format('hello', 'world')
'world hello'

{0[1]}, {0[2]} のようにするとリストやタプルの要素を表示できる。

>>> '{0[0]} {0[1]}'.format([1,2])
'1 2'
>>> '{0[0]} {0[1]}'.format((1,2))
'1 2'

オブジェクトのプロパティも表示できる。

>>> class Person:
...     def __init__(self, name, age):
...         self.name = name
...         self.age = age
...
>>> p = Person('John', 25)
>>> '{0.name} {0.age}'.format(p)
'John 25'

fomat() メソッドで文字列の {} の中にデータを送り込むやり方はまだいろいろあるようだが、とりあえずこれだけ試してみた。

format() メソッドはデータを {} に送り込むだけでなくその時の表示の書式も指定できるがそれはまた別の記事にする。

[PR]
by tnomura9 | 2018-01-04 21:12 | Python | Comments(0)

input()

Python の input() 関数は標準入力から文字列を受け取って文字列として返す。Python3 では入力の関数は文字列しかかえさない。input 関数の引数にはプロンプトの文字列を与える。

>>> input("input any line: ")
input any line: hello, world
'hello, world'

したがって、コマンドラインから数値を入力するためには int() 関数で型変換をしないといけない。これは2行にわたるプログラムなので、対話型環境で入力するために少し工夫をする。ヘッドに if True と記述するのだ。これで、インデントしたコードブロックが終了するまでコードは実行されない。

>>> if True:
...     num = input("input any number: ")
...     a = int(num)
...     print(a * a)
...
input any number: 5
25

しかし、これでは1行で1個の数値しか入力できない。複数個の数値を入力するためにはどうすればいいだろうか。C の scanf() 関数があればいいのだが、調べたが無いようだった。いろいろ考えたが json モジュールを使うことを思いついた。

>>> import json
>>> if True:
...     line = input("input any list: ")
...     a, b = json.loads(line)
...     print(a + b)
...
input any list: [2,3]
5

この方法だと辞書型のデータの入力も可能なので、自分用の使い捨てプログラムには使い勝手がいい。

コマンドラインからの入力は無限ループすることが多いので次のようなプログラムがよく使われる。

>>> while True:
...     line = input("> ")
...     if line == "quit":
...         break
...     else:
...         print(line)
...
> hello, world
hello, world
> a happy new year!
a happy new year!
> quit
>>>

[PR]
by tnomura9 | 2018-01-03 21:11 | Python | Comments(0)

Python を始めた

Python を勉強することにした。AI関連のライブラリを利用するのに必須のようだからだ。情報源は次のサイト。


Windows10 にインストールしたが、インストーラでパスの設定にチェックを入れておくと、インストール後コマンドプロンプトから

python

と入力すると、対話環境が起動する。プログラムを学習するのに対話的に動作させることができるのはありがたい。

C:\Users\xxxx>python
Python 3.6.4 (v3.6.4:d48eceb, Dec 19 2017, 06:54:40) [MSC v.1900 64 bit (AMD64)]
on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>

はじめに戸惑ったのは、ループや条件分岐のときにブロックをインデント(推奨はスペース4個)で表現することだ。for 文や if 文の後にコロンをつけるのを忘れてエラーになっていたが、何回かやっているうちに慣れた。コードブロックをどのように表記してもコードブロックはコードブロックなので。

>>> for i in range(1,5):
...     print(i)
...
1
2
3
4

ラムダ記法が使える。

>>> (lambda x: x * x)(2)
4

リスト処理ができるが、オブジェクト指向なのでメソッドで行う。

>>> array = [1,2,3]
>>> array.insert(0, 0)
>>> array
[0, 1, 2, 3]

高階関数も使える。ただし map 関数の戻り値は iterable 型なので list() 関数でリストに変換する必要がある。

>>> map(lambda x: x * x, range(1,11))
<map object at 0x0000018BDCBC3BE0>
>>> list(map(lambda x: x * x, range(1,11)))
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

関数の定義は def キーワードで行う。このとき関数の本体はインデントしたブロックになる。

>>> def fact(n):
...     if n == 0:
...         return 1
...     else:
...         return n * fact(n-1)
...
>>> fact(5)
120

オブジェクト指向言語なのでクラスも記述できる。クラスの定義は class キーワードで行う。クラス名の冒頭は大文字だ。メソッドはクラス宣言のなかで関数定義するが、引数の最初は自分自身のオブジェクトである self にする。コンストラクタは __init__ という名前を使う必要があるようだ。

>>> class MyClass:
...     def __init__(self):
...         self.name = ""
...     def getName(self):
...         return self.name
...     def setName(self, name):
...         self.name = name
...
>>> a = MyClass()
>>> a.setName("Python")
>>> a.getName()
'Python'

対話環境を終了するには quit() と入力する。

>>> quit()

オブジェクト指向も、リスト処理も、λ記法も、高階関数もある手続き型言語という印象だった。Ruby と Haskell と JavaScript の経験が役立ちそうだ。どの言語も表現力が似てきているからだ。オブジェクト指向のプログラムや関数型のプログラムは必要に応じて出現してきたのだからそれをどの言語も記述できるようになるというのは必然的なような気がする。

プログラム言語の優劣を述べる記事はよく見かけるが、小さいプログラムを作るだけなら、どの言語を使っても変わりない。動けばいいのだから。大きいプログラムを作るときには、問題になるのはデバッグと保守だ。プログラム作成よりもこちらのほうに大きな労力が必要になる。おそらく、これからのプログラム言語はその労力を最小化する方向に発展していくのではないだろうか。Python も常に発達中の言語らしい。


[PR]
by tnomura9 | 2018-01-02 09:19 | Python | Comments(0)