コマンドライン引数

C言語ではコマンドラインオプションの引数を扱う場合、「getopt()」って関数があったと思うんだが、Pythonにも同様のものがあったね。うれしいよ。

import sys
import getopt

def main(argv):
  try:
    opts, args = getopt.getopt(argv, "ho:d", ["help", "outfile="])
  except getopt.GetoptError:
    sys.exit(2)

  for opt, arg in opts:
    if opt in ("-h", "--help"):
      # help
      pass
    elif opt == "-d":
      # debug on/off
      pass
    elif opt in ("-o", "--outfile"):
      # out file
      pass

if __name__ == '__main__':
  main(sys.argv[1:])

凄い簡単だと思う。ロングオプションにも簡単に対応できるってのが良いよね。それも「-h」や「-o」だけじゃなく「-ho」にもきちんと対応している。素晴らしい。ところで「-hod filename」ってやったらどうなるんだろう。試してないな、やってみよ。

>>> import getopt
>>> arglist = '-h -d -o filename'.split()
>>> arglist
['-h', '-d', '-o', 'filename']
>>> optlist, args = getopt.getopt(arglist, 'ho:d', ['help', 'outfile=', 'debug'])
>>> optlist
[('-h', ''), ('-d', ''), ('-o', 'filename')]
>>>

正しい動き。

>>> arglist = '-hod filename'.split()
>>> arglist
['-hod', 'filename']
>>> optlist, args = getopt.getopt(arglist, 'ho:d', ['help', 'outfile=', 'debug'])
>>> optlist
[('-h', ''), ('-o', 'd')]
>>>

ま、そうなるわな〜

>>> arglist = '-hdo filename'.split()
>>> arglist
['-hdo', 'filename']
>>> optlist, args = getopt.getopt(arglist, 'ho:d', ['help', 'outfile=', 'debug'])
>>> optlist
[('-h', ''), ('-d', ''), ('-o', 'filename')]
>>>

これは上手くいった。ある程度予想は出来たけど・・・パラメータが必要なオプションは他のオプションと分離した方が良いよね。ってのは常識?

Pythonの「getopt()」は直感的にわかりやすく利用しやすいね。以上

スクリプトの初期動作追加

そんな大した話じゃないんだが・・・

Pythonスクリプト起動直後、何らかの初期値、環境等を構築したいと思う。例えば特定のモジュールを「import」したいとか、そんな事をスクリプトを起動後、毎回主導でやってたら面倒になると思う。

スクリプト起動直後の自動的に設定してみる。ちなみに「Dive in Python」に載ってる事を書くだけだけどね。「Dive in Python」って本は面白いね。他に出版されている本、特に著者が日本人の本は基本的すぎて読む気がしなかったんだよ。そんな情報はネットで十分乗ってますよって感じで、買うのをためらわせた。でも「Dive in Python」はお勧め。しかも、ネット上に公開されていているから、誰でも見ること可能。すばらしいよ、著者に感謝。

はじめに、

>>> import sys
>>> sys.getdefaultencoding()
'ascii'

次に、Pythonをインストールしたディレクトリに存在する「site-packages」({python_homedir}/lib/site-packages)に下記ファイルを格納。

# sitecustomize.py
import sys
sys.setdefaultencoding('iso-8859-1')

再度、スクリプト起動。

>>> import sys
>>> sys.getdefaultencoding()
'iso-8859-1'

おおお、すげーや、スクリプト起動時に「site-packages」にスクリプトファイルが存在すれば実行するんだね。このファイルに「import」とか記述しておけば、わざわざスクリプト側でインポートせずに済むのかな?あれ?なんか違う気がする。

スクリプト終了後、再度起動して確認。

>>> sys.getdefaultencoding()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'sys' is not defined
>>> dir(sys)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'sys' is not defined

思ったんだが、「site-packages」の「sitecustomize.py」で既に「import sys」を行っているんで、スクリプト起動後、再度「import sys」を行わなくても「sys」パッケージを利用できるんじゃないかと・・・無理だった。結局、「site-packages」ってのは何だ?実行したPython処理系の初期値を設定する事なのかな?例えば文字コードとか、あとは・・・だけど。

ちょっと最初に感じた超便利!ってのとは違うけど面白いね。では

パッケージについて

Pythonにはパッケージって概念あるよね。ただコード上で明示的にパッケージの宣言はしていないと思う。Perlなら「package」と明記するんだがPythonは処理系が自動的にパッケージを認識してくれるの?

# TestClass.py
class happyMonday():
  pass

class badTuesday():
  pass

def dayToHappy():
  pass

def __test():
  pass

if __name__ == '__main__':
  __test()

ちょっと上記のようなクラスを作ってみた。

>>> import TestClass
>>> TestClass
<module 'TestClass' from 'TestClass.py'>
>>> from TestClass import happyMonday
>>> happyMonday
<class TestClass.happyMonday at 0x00AEDDB0>
>>> from TestClass import dayToHappy
<function dayToHappy at 0x00AF6B70>
>>> from TestClass import __test
>>> __test
<function __test at 0x00AF6C30>
>>> __test()

面白いね。ファイルごとにパッケージ管理を行っているイメージなのかな?クラスはわかるが、ファンクションを「import」できるのには新しい発見だ。あと「__test」がcallできちゃうんだね。なんで?そんなもの?まあいいや。

凄く面白いなと思ったのは、ディレクトリの中に「__init__.py」を作ると、ディレクトリをインポートできるんですね。面白いです。そのパッケージをインポートする際に必要なモジュール、変数を設定したりするのかな?まだ、使いどころがわからないが、少し驚いた。

「./test」を作って、その直下に空ファイル「__init__.py」を作成。

>>> import test
>>> test
<function test at 0x00AF6BB0>
>>> t = test()
>>> t
<module 'test' from 'test\__init__.py'>
>>>

「import test」をやる事によって、そのディレクトリ配下の「__init__.py」を見に行くんだね。この事により階層構造を容易に作成できインポートも簡単に行えるわけだって本当か!ディレクトリ配下に「__init__.py」が存在しなければ、ただのディレクトリに過ぎないので「import」は行えない。

>>> import ss
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named ss

ちなみに、「import test」とやると、標準モジュールに「test」が存在し、そっちを見に行くので注意かな。では

printの表現方法

Pythonをがんばって勉強中の私です。本当はGoogle App Engineで何か作ろうと思ったんですが、Pythonがあまりにも面白いんで、GAEのほうは手がつきません。一応、オンラインマニュアルは読んだかな〜

>>> print("%s" % "This is it!")
This is it!
>>> print("%s: %d" % ("Age", 20))
Age: 20
>>> print("%10s -- %03d" % ("Good", 89))
      Good -- 089

まあ〜この辺は通常の使い方だよね。他の言語でも同じようなものだ。

>>> params = {"os":"Linux", "web":"Apache", "db":"MySQL", "lang":"Python"}
>>> print("%(web)s" % params)
Apache
>>> print("%(os)s -- %(web)s -- %(db)s -- %(lang)s" % params)
Linux -- Apache -- MySQL -- Python

これって物凄く便利だと思う。少し感動。それだけなんだけどね。

あと、「locals()」と「globals()」も便利ですよね。

>>> def foo(arg):
...     x = 1
...     y = 2
...     z = 3
...     return locals()
...
>>> foo(4)
{'y': 2, 'x': 1, 'z': 3, 'arg': 4}

定義されたローカル変数の全てを表しているってのが「locals()」、便利ですよね。

>>> def bar(arg):
...     text = "Hello, Python"
...     num  = 99
...     print("%(num)d :  %(text)s -- %(arg)s" % locals())
...
>>> bar("Good")
99 :  Hello, Python -- Good

本当に便利だと思います。感動です。

>>> globals()
{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', 'foo
': <function foo at 0x00AF6B30>, 'bar': <function bar at 0x00AF6B70>, '__doc__':
 None}

「foo()」と「bar()」が定義されていますね。面白いです。では

sgmllibを使ってみた

sgmllibとは、SGML形式のテキストファイルを解析するモジュールって事なんだけどSGML形式ってなんぞ?SGML形式とは、簡単に解釈すればHTML形式やXML形式の親って事で良いよね。で、親だからHTMLやXMLを解釈できるって事。

sgmllibを使ってみた。

from sgmllib import SGMLParser

class HtmlLister(SGMLParser):
  def reset(self):
    SGMLParser.reset(self)
    self.urlList = []

  def start_a(self, attrs):
    href = [v for k, v in attrs if k == 'href']
    if href:
      self.urlList.extend(href)

def getHtml(url):
  import urllib
  sock = urllib.urlopen(url)
  html = sock.read()
  sock.close()
  return html

if __name__ == "__main__":
  html = getHtml("お好みのアドレス")

  parser = HtmlLister()
  parser.feed(html)
  parser.close()

  text = parser.urlList
  text.sort()

  link = [t for t in text if t.find("http", 0, 4) >= 0]

  print("\n".join(link))

これも、Dive Into Pythonにあるコードをちょっと改造しただけなんだがね。「SGMLParser」を直接使うんじゃなくって継承して、そのクラス内で必要なメソッドをオーバーライトしている。「reset()」で初期化、なんでも「__init__」は、ここでは使わないみたい。その他、沢山メソッドがあったが、あとはPythonのオンラインマニュアル参照(http://www.python.jp/doc/release/lib/module-sgmllib.html

やっている事は、HTMLテキストを取得して、そのHTMLテキストの中に存在するAタグのhref属性の値を取得している。ちなみに画面に表示するのは先頭4文字がhttpで始まるものにしている。

あと

  text = parser.urlList
  text.sort()

の部分だけど、「parser.urlList」はリスト形式なので、そのままソートしたかった。「parser.urlList.sort()」とかってやるとエラーになる。「(parser.urlList).sort()」や「[parser.urlList].sort()」とやってもエラーになった。ので他の変数に格納してソートしている。もう少し良いやり方がありそうなんだが、良くわかんないや。このやり方がベストなのかな?

Pythonって作りたいものがすぐに作れる。利用しやすいモジュールがそろっているのが魅力ですね。素晴らしいと思う。うん

リストの操作

便利ですねPythonって、でリスト操作の一部を見てみる。

>>> li = []
>>> dir(li)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delsli
ce__', '__doc__', '__eq__', '__ge__', '__getattribute__', '__getitem__', '__gets
lice__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '
__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__r
educe_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__
', '__setslice__', '__str__', 'append', 'count', 'extend', 'index', 'insert', 'p
op', 'remove', 'reverse', 'sort']
>>> li = [1, 2, 3, 4]
>>> li
[1, 2, 3, 4]
>>> li.append(5)
>>> li
[1, 2, 3, 4, 5]
>>> li.extend(6)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
>>> li.extend([6])
>>> li
[1, 2, 3, 4, 5, 6]
>>> li.extend([7, 8, 9, 0])
>>> li
[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]

なるほど、「append()」はリストに要素を追加し「extend()」はリスト要素を結合する。ですね〜それだけ、では

ディレクトリ内検索

ディレクトリ内検索と言うか、ディレクトリ内のファイル一覧を表示してみる。

>>> import os
>>> os.path.join("c:\work\python", "sample001.txt")
'c:\\work\\python\\sample001.txt'
>>> os.path.expanduser("~")
'C:\\Documents and Settings\\username'

おお〜凄いね〜存在しないディレクトリを指定しても特にエラーが出ないね。

>>> os.path.split("c:\work\python\sample001.txt")
('c:\\work\\python', 'sample001.txt')
>>> os.path.splitext("sample001.txt")
('sample001', '.txt')
>>> os.path.splitext("sample001.txt.zip")
('sample001.txt', '.zip')
>>> os.path.splitext("sample001.1.2.3.4.5.6")
('sample001.1.2.3.4.5', '.6')

ファイルバスの分解とファイル名の分解。拡張子は一番最後のものを取得するのね。では拡張子がなければどうなるのよ。

>>> os.path.splitext("sample001")
('sample001', '')

面白いね。少し気になった点。

>>> os.path.split("c:\work\python\test\sample001.txt")
('c:\\work\\python\test', 'sample001.txt')
>>> os.path.split("c:\work\python\test\sample001.txt")[0]
'c:\\work\\python\test'
>>> print os.path.split("c:\work\python\test\sample001.txt")[0]
c:\\work\\python    est

おお〜「\test」の「\t」がタブと認識されているのね。こういった場合は「\\test」とするなり。

ディレクトリ内検索をしてみる。

>>> os.listdir("c:\work\python")
['sample001.py', 'sample002.py', 'sample003.py', 'test']
>>> for i in os.listdir("c:\work\python"):
...   ext = os.path.splitext(i)[1]
...   if ext == '.py':
...     print(i)
...
sample001.py
sample002.py
sample003.py

Pythonスペシャリストは上記「for」文の内容を一行でやっちゃうんだろうね。「"\n".join(...)」とかやって簡単に作るんだろうが今の私には無理だ挑戦してみたがわけわからなくなった。また今度ね。ちなみに文字列の比較って「==」でいいの?1byte文字なら問題ないのかもしれないが2byte文字、日本語の比較ってできるの?そんな関数あるんですか〜「strcmp」とか。

必要になったら調べる。あとグロブ。

>>> import glob
>>> glob.glob('c:\\work\\python\\*.py')
['c:\\work\\python\\sample001.py', 'c:\\work\\python\\sample002.py', 'c:\\work\\python\\sample003.py']

あと「os.path.normcase()」に関して、

>>> os.path.normcase("c:\work\\python\\sample001.py")
'c:\\work\\python\\sample001.py'
>>> os.path.normcase("C:\WORK\\PYTHON\\SAMPLE001.py")
'c:\\work\\python\\sample001.py'

システムで利用するファイル形式の一般化を行う、でいいの。Windowsでは全て小文字とする。Windowsは大文字小文字を区別しないから、区別する場合は変化ないんだろうね。

ふ〜Pythonって面白いよね〜完全にとりこにされた。