-- RyuSawada - 2016-12-06

このページは、https://twiki.cern.ch/twiki/bin/viewauth/AtlasComputing/SoftwareTutorialPythonTutorial をベースにして書かれています。

AtlasJapanSoftwareTutorial16DecPython

イントロダクション

パイソンはC++よりも簡単にかけてコンパイルせずに実行できるプログラム言語です。C++と比較した場合の特徴は、

  • コンパイルせずに実行可能
  • 豊富なライブラリ
  • 文法はC++よりも単純
といった便利な点がありますが、実行速度、より複雑なクラスの定義、コンパイル時のチェック等の点でC++の方がいい点もあるので、ATLASではC++とパイソンを使い分けています。 実行速度が必要な大規模なソフトウェア (シミューレーションやイベント再構成のコア部分)はC++が使われます。 パイソンはC++の関数を呼び出して使うことができるので、上記のようなソフトウェアのインターフェイスとしてパイソンが使われます。 また計算機で走らせる各種のモニター用プログラムにもパイソンが使われます。

パイソンの最新バージョンは3.5ですが、ATLASでは2.7を標準的に使っています。

また、他のスクリプト言語と比較すると、

  • オブジェクト指向
  • 書き方の柔軟性が少ないため、誰が書いても比較的似たようなコードになる。このためメンテナンスがしやすい。 (例えば、PERLは逆)
といった特徴があります。

パイソンの実行方法

通常はプログラムをファイルに書いて実行しますが、インタープリターを呼び出して実行することもできます。まずはhelloをプリントしてみましょう。

$ setupATLAS
$ localSetupPython
$ python
>>> print "hello"
hello
>>> CTRL-d # # to exit python

では、同じことをスクリプトから実行しましょう。ファイル(hello.py)に上のprint文を書いて以下のコマンドを実行してください。

$ python hello.py

一番最初の行にスクリプトのコマンドを書けば、実行時のコマンドを短くすることができます。

  • hello.py の中身:
#!/usr/bin/env python
# This script prints hello to the screen
print "hello"
# EOF #
   
  • コマンドの実行:
$ chmod +x hello.py
$ ./hello.py 
hello

文法の基本

  • ハッシュ( # )よりも後ろの部分は行の終わりまでコメントになる
  • 改行がコマンド文の終わりになる。 (C++では改行に意味はない)
  • 1行に複数の文を書きたい場合はセミコロン( ; )で分ける。
  • 一つの文を複数行に続けたい場合は行の最後にバックスラッシュをつける。
  • ブロック (ifやforの中身) は同じインデントで決められる。 (C++のように{ } で囲むわけではない)
    • タブではなくスペース4つが推奨
  • かっこを使った場合 にはかっこが閉じられるまでブロックが続く ()[]{}

l = [ "muon",
      "electron" ]
   

基本的な変数の型

  • int: 整数型( C++ のlongに対応)。これよりも大きな整数には long が使われる。
  • float: 64ビットの浮動小数点型 (C++のdouble)
  • complex: 複素数
  • str: イミュータブル 文字列, C++の const char* みたいなもの
  • bool: ブーリアン TrueFalse
  • list: (通常は同じ型の) データの列 (C++の std::vector みたいなもの)
  • tuple: イミュータブルなデータ列 (C++の構造体みたいな使い方)
  • dict: キーと値のペアの列, ( C++std::map<K,V> みたいなもの)
  • ある変数の型は type 関数で知ることができる。
>>> type("hello")
<type 'str'>
>>> type("hello").__name__
'str'
   

ミュータブルとイミュータブル

パイソンにはミュータブル(値を変更可能)とイミュータブル(値変更不可)の型があります。上の例でいうとint, float, complex, str, bool, tuple等の多くの型の変数がイミュータブルです。 例えば、

>>> v = 10
>>> v = 20
とした場合、vの値が変わっているように見えますが、実際には二つ目の文で、20という値に対してvというラベルがつけられたという風に見ます。 この見方はC++等多くの言語と違うので注意してください。

C++や多くの他のスクリプト言語では変数の型は変化することがありませんが、パイソンでは値に応じて自動的に変わります。 例えば、

>>> v = 10
>>> type(v)
>>> v = v + 2.2
>>> type(v)
とした場合、一行目の実行後のvの型はintですが、二行目の後にはfloatになります。

演算子

  • + , - , * , / , ** : 四則演算, 累乗
  • += , -= , *= , /= : 演算と代入 (=C++=と同様)
  • %: (intに対して)割り算の余り, (文字列に対して)フォーマット指定
  • or and: 論理和と積
  • not: 否定
  • < <= > >= : 大小比較
  • == , != : 同値、非同値
  • is, is not: 同じオブジェクトを指しているかどうか
  • in, not in: ある要素が集合に含まれているかどうか
  • | ^ & ~ << >>: bit演算子
  • X < Y < Z: YX and Z の間にある場合に真

>>> u = 10
>>> v = 10.0
>>> u == v
>>> u is v
   

リスト

パイソンのリストは C++std::vector<T*> に似ています。値の列を格納し、その値も変更可能です。 異なる型も格納することができますが、同じ型の列として使うことが多いです。

>>> v = []      # empty list
>>> v = list()  # empty list
>>> v = [1, 2, 3, 4, 5]
>>> v = ['a', 'b', 'c']
>>> v = range(4, 10, 2)   # results in [4, 6, 8]
>>> print v
>>> v = [4, 2.5, "Hi", [1,3,5]] # can mix types
>>> print v

要素の追加

>>> v = range(4)
>>> print v
[0, 1, 2, 3]
>>> v.append(70)
>>> print v
[0, 1, 2, 3, 70]

リストの結合

>>> v = range(4)
>>> print v
[0, 1, 2, 3]
>>> v += [5, 6, 7]
>>> print v
[0, 1, 2, 3, 5, 6, 7]

要素の削除

>>> v = [4, 2.5, "hi"]
>>> v.remove(2.5)
>>> print v
[4, 'hi']
>>> del v[0]
>>> print v
['hi']

サイズの取得

>>> v = [4, 2.5, "hi"]
>>> len(v)
3

要素の読み書き

>>> v = range(4,8)
>>> print v
[4, 5, 6, 7]
>>> v[0]
4
>>> v[0] = "hey"
>>> print v
['hey', 5, 6, 7]
>>> v[-1]          # last element. negative index: count from the end
7
>>> v[1:3]         # subrange by index (start idx, one-beyond-last idx)
[5, 6]
>>> v[1:3] = ["a", "b"]
>>> print v
['hey', 'a', 'b', 7]

要素の存在のテスト

>>> v = range(4,8)
>>> if 4 in v:
...  print "found it"
...  
found it

>>> if 200 not in v:
...  print "not found"
...  
not found

要素に対するループ実行

>>> v = range(4)
>>> for i in v:
...  print i
...  
0
1
2
3

ここでは扱いませんが、リストに対してはC++のvectorと同様に沢山の関数が用意されています。 ( sort , reverse , index , count , ...). 以下のコマンドで詳しい説明を見ることができます。

>>> help(list)

リストを作成する内包表現

パイソンでは内包表現を使うことでリストへの値の代入を短い文で書くことができます。例えば、

>>> v = [x**2 for x in range(10) if x % 3 == 0]
>>> print v
とすると、10までの3の倍数の二乗のリストができます。

文字列

  • 文字列はシングルクォート(')かダブルクォート(")を使います。複数行にわたる場合にはこれらを三つ重ねます( '''"""):
>>> a = 'hello'
>>> b = "how are you?"
>>> c = '''first line
... second line'''
>>> d = """look ma! another
... line"""
>>> print a
hello
>>>print b
how are you?
>>> print c
first line
second line
>>> print d
look ma! another
line

>>> aa = "'"; bb = '"'
>>>print aa
'
>>> print bb
"

>>> aa = "\""; bb = '\''

結合

>>> a = 'hello'
>>> d = a + "! "
>>> d                # will show object on screen
'hello! '            # note the quotes
>>> b = 'how are you?'
>>> d += b
>>> print d
hello! how are you?  # no quotes printed here

要素へのアクセス

>>> d[1]             # read-only. d[0] is first character
'e'
>>> d[1] = '2'       # will fail
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment

部分文字列

>>> d      
'hello! how are you?'

>>> d[7:10]  # start at char at index 7 (8th char), up to
             # (but not including) char at index 10 (11th char)
'how'

>>> d[-4:]   # start at 4th char from the end, continues until the end
'you?'

検索

>>> d      
'hello! how are you?'

>>> d.find('o')    # returns the index of the first match
4
>>> d.find('H')    # returns -1 if not found (case sensitive)
-1
>>> d.find('how')  # returns index of first character
7

C言語風のフォーマット

>>> print "%6.4f %4d" % (5.2, 200)
5.2000  200

後でまた説明があるますが、

  • "%s" はそのクラスの__str__が返す文字列
  • "%r" は(calls __repr__が返す文字列が入ります。

もし、特にフォーマットが必要ない場合は、

>>> number = 5.6
>>> str(number)
のようにすることで数字を文字列に変換できます。

繰り返し

>>> 10 * "#"
'##########'

文字列の分割

>>> d 
'hello! how are you?'

>>> d.split()           # default split character is whitespace
['hello!', 'how', 'are', 'you?']

>>> d.split('h')
['', 'ello! ', 'ow are you?']

文字列の連結

>>> ds = d.split()
>>> ds
['hello!', 'how', 'are', 'you?']
>>> "---".join(ds)
'hello!---how---are---you?'

辞書型

辞書型 はキーと値のペアの集合です。例えば、粒子名を指定して粒子の数を返して欲しい場合には以下のような dict を作ります。

>>> k = {}           # empty dictionary
>>> k = dict()       # empty dict
>>> k
{}
>>> k = {'muon': 4, 'electron': 2}  # key: value
>>> k
{'electron': 2, 'muon': 4}
>>> k['muon']
4

要素の操作

>>> k['jet'] = 1          # add or overwite an entry
>>> k
{'electron': 2, 'jet': 1, 'muon': 4}

>>> k.update({'tau':1, 'photon': 2}) # add from another dict
>>> k
{'electron': 2, 'jet': 1, 'muon': 4, 'photon': 2, 'tau': 1}

>>> k.setdefault('muon', 1)          # only sets if not already there
4
>>> k
{'electron': 2, 'jet': 1, 'muon': 4, 'photon': 2, 'tau': 1}

>>> k.setdefault('higgs', 42)        # only sets if not already there
42
>>> k
{'electron': 2, 'higgs': 42, 'jet': 1, 'muon': 4, 'photon': 2, 'tau': 1}

dict へのアクセス

>>> k.get('muon', 0)        # returns 0 if 'muon' is not in dict
4

>>> k.keys()                # returns the keys in a list
['muon', 'jet', 'higgs', 'photon', 'tau', 'electron']

>>> k.values()              # returns the values in a list
[4, 1, 42, 2, 1, 2]

>>> k.items()               # returns the items: a list of (key, value) tuples
[('muon', 4),
 ('jet', 1),
 ('higgs', 42),
 ('photon', 2),
 ('tau', 1),
 ('electron', 2)]

要素の削除

>>> k
{'muon': 4, 'jet': 1, 'higgs': 42, 'photon': 2, 'tau': 1, 'electron': 2}

>>> del k['electron']
>>> k
{'muon': 4, 'jet': 1, 'higgs': 42, 'photon': 2, 'tau': 1}

繰り返し操作

>>> k
{'higgs': 42, 'jet': 1, 'muon': 4, 'photon': 2, 'tau': 1}

>>> for n in k:        # loop over keys
...  print n, k[n]     # ok, bad example: use items() to access both the key and the value!!
...
muon 4
jet 1
higgs 42
photon 2
tau 1

>>> for n,v in k.items():  # n,v is an implicit tuple filled for every pass in the loop
...  print "k[%r]= %r" % (n,v)
...
k['muon']= 4
k['jet']= 1
k['higgs']= 42
k['photon']= 2
k['tau']= 1

dict の要素の検査

>>> k
{'higgs': 42, 'jet': 1, 'muon': 4, 'photon': 2, 'tau': 1}

>>> if 'muon' in k:          # searches in the keys
...  print "got a muon"
...
got a muon

>>> if 'electron' not in k:
...  print "no electron"
... else:
...  print "got an electron"
...
no electron

ブーリアン

ブール値は TrueFalse と書く。( C++ と違い最初は大文字)

それぞれの型は TrueFalse に対応した値を持っている:

Type True False
int, long, float non-zero 0
list non-empty empty []
tuple non-empty empty ()
dict non-empty empty {}
str non-empty empty ""

  • X or Y:
    • X and Y がどんな型でもよい
    • 返り値は、 XTrue の時には X (その場合は Y は評価されない), そうでない場合は Y が返される
    • 返り値の型は XY の型。 必ずしも bool ではない !!
  • X and Y:
    • or とほぼ同じだが、:
    • 返り値は、 XFalse の時には XY は評価されず、返り値は Y

変数

  • 変数の型を明示的に指定することはない
  • 変数の型は動的に変化する
    • 変数の型を人が指定することはない
    • 変数の型はその時に参照しているオブジェクトの型
    • 変数の型は変更可能
>>> a = "hello"
>>> type(a)
<type 'str'>
>>> a = 5
>>> type(a)
<type 'int'>

>>> b = a     # b = 5, a and b now point to the same object
>>> a = 7     # does this change the object pointed to ?
>>> print b   # what should this print ? 5 ? or 7 ?
>>> print b   # yep, 5. a has been 'rebound' to a new object, 
              # but b has been left untouched, so it still points to
         # the object 'the integer 5'
5

  • listdict はミュータブルな型であることに注意 (それでも変数は実際のオブジェクトの参照)
>>> a = [1, 2, 3, 4]
>>> b = a              # b now points to the same list
>>> b[3] = 6           # changes an element in the list
>>> print a            # a is changed
[1, 2, 3, 6]
   
    • この場合は b に変更を加えた時には a も変化する
    • 他の操作の時も同様。例えば b.append(7)b += [8,9]

  • では文字列の場合はどうでしょうか?
>>> a = "hello"
>>> b = a
>>> a += " !"
>>> b   # what should this print ?
>>> b   # yep, 'hello'. a has be 'rebound' to a new object (as strings 
        # are immutable), but b has been left untouched, so it still points
   # to the object 'the original string "hello"'
'hello'

コピーの作成

では、今行ったような実際のオブジェクトへの新しい参照を作るのではなくコピーを別に作りたい時にはどのようにすればいいでしょうか?

リスト

>>> v1 = [1, 2, 3, 4]
>>> v2 = v1[:]       # makes a copy ('sub'-list of the complete original list)
>>> v3 = list(v1)    # another way to make a copy
>>> v2[3] = 8        # only modifies v2, leaves v1 and v3 unchanged
>>> v3[3] = 12       # only modifies v3, leaves v1 and v2 unchanged
>>> v1
[1, 2, 3, 4]
>>> v2
[1, 2, 3, 8]
>>> v3
[1, 2, 3, 12]

>>> import copy
>>> v4 = copy.copy(v1)
>>> v1[3] = 0
>>> v4
[1, 2, 3, 4]

辞書型

>>> d1 = {"muon": 2, "electron": 2}
>>> d2 = d1.copy()
>>> d3 = dict(d1)
>>> d4 = copy.copy(d1)
>>> d1["muon"] = 22

>>> d1
{'electron': 2, 'muon': 22}
>>> d2
{'electron': 2, 'muon': 2}
>>> d3
{'electron': 2, 'muon': 2}
>>> d4
{'electron': 2, 'muon': 2}

浅いコピーと深いコピー

  • 今まで見てきたコピーは浅いコピーと呼ばれます。この場合は新しい listdict を作り元の要素への参照を書き込みます。
  • v5 = copy.deepcopy(v1) のような深いコピーをすると要素それぞれについても再帰的にコピーが作られます。

>>> d = {"cutflow": [10, 5, 3]}
>>> d1 = dict(d)
>>> d2 = copy.deepcopy(d)

>>> d["cutflow"][2] = 0
>>> d, d1, d2 # what should they print ?
>>> d
{'cutflow': [10, 5, 0]}
>>> d1
{'cutflow': [10, 5, 0]}
>>> d2
{'cutflow': [10, 5, 3]}

メモリ管理

python では(C++と違って) ガーベッジコレクターによってメモリが自動的に管理されます。 実際には python がオブジェクトへの参照の数を数えていて、その数が0になった時に自動的にメモリが解放されます。

>>> a = [1,2]
>>> b = a
>>> del a   # deletes the variable a, not the object
>>> del b   # deletes last reference, hence deletes the object too   

分岐とループ

if-then-else

>>> d1 = {"muon": 2, "electron": 3}
>>> if "muon" in d1:
...  print "found a muon"
... elif "electron" in d1:
...  print "found an electron but no muon"
... else:
...  print "no muon and no electron"
...                                  # what should this print ?
found a muon

forループ

ここから先のコードは見るだけで進めます。

for k,v in d1.items():    # loop over items in d1
    if k == "muon":
        process_muon(v)
        continue          # go to next item in the dict
    if k == "unknown":
        break             # bail out of the loop
else:
    # executed if no 'break' was executed
    print "All OK: no 'unknown' particles"

関数

関数の定義

def muonSelector(particle, ptmin=50*GeV):
    """
    muonSelector selects particles which are muons with a
    pt greater or equal to `ptmin`
    """
    if particle.type() == "muon":
       if particle.pt() &gt;= ptmin:
          return True
    return False

  • def キーワードの後の部分は新しい関数の定義になる
  • 最初に新しい関数の名前と引数
  • 関数の説明を先頭に書いておくとわかりやすくなる
  • その次に関数の中身が書かれる

  • さっきの muonSelector 関数では particle は呼び出す際に必ず引数として渡さなけばならない
  • ptmin は必須ではなく、省略された場合はデフォルト値の 50*GeV が使われる

関数に可変長の引数やキーワード引数を加えることもできる:

>>> def function(a, b, c):
...     print a
...     print b
...     print c
以上のような関数を以下のように呼ぶことができます。
>>> function(c=1, a=2, b=3)
2
3
1
このようにキーワード指定すると、順序と関係なく名前で引数が設定されます。

def complicated_fct(arg1, *args, **kwargs):
    """my documentation"""
    if arg1 &gt; 10 and len(args) &gt; 0:
        return args[0] == 42
    if 'ptmin' in kwargs:
        return kwargs['ptmin'] &gt; 50.*GeV
    return

# example: args[0]==55., kwargs['ptmin']==25.*GeV
complicated(5, 55., ptmin=25.*GeV)

# example: len(args)==0, kwargs['ptmin']==25.*GeV
complicated(5, ptmin=25.*GeV)

  • 関数の定義では引数の型は指定しない。(が、関数の中身は特定の型を想定して書かれるのが普通)

  • 関数の返り値も同様に型は指定しないが、呼び出し側で特定の型を想定しているのが普通。

  • return 文がない場合は None が返される。

それでは色々な関数を見ていきましょう。

### a variadic function --------------------------------
>>> def one(*args):
...     print args

>>> one()
()
>>> one(0)
(0,)
>>> one(0, 1, 2)
(0, 1, 2)

### a function with arguments with default values ------
>>> def two(b=1, c=2):
...  print "b=%s c=%s" % (b, c)

>>> two()
b=1 c=2
>>> two(0,1)
b=0 c=1
>>> two(b=2, c=3)
b=2 c=3
>>> two(c=2, b=3)
b=3 c=2

### a variadic function, with keyword arguments ---------
>>> def three(*args, **kwds):
...     print "args=%s" % (args,)
...     print "kwds=%s" % (kwds,)

>>> three(0)
args=(0,)
kwds={}
>>> three(0,1)
args=(0, 1)
kwds={}
>>> three(0, 1, a=1, b=2)
args=(0, 1)
kwds={'a': 1, 'b': 2}
>>> three(a=1, b=2)
args=()
kwds={'a': 1, 'b': 2}

### everything in the mix -------------------------------
>>> def four(a, b=1, *args, **kwds):
...     print "a=%s b=%s" % (a, b)
...     print "args=%s" % (args,)
...     print "kwds=%s" % (kwds,)

>>> four(0)
a=0 b=1
args=()
kwds={}
>>> four(0, 2)
a=0 b=2
args=()
kwds={}
>>> four(0, 2, 3, 4)
a=0 b=2
args=(3, 4)
kwds={}
>>> four(0, 2, 3, 4, c=2, d=3)
a=0 b=2
args=(3, 4)
kwds={'c': 2, 'd': 3}
>>> four(0, c=2, d=2)
a=0 b=1
args=()
kwds={'c': 2, 'd': 2}
>>> four(a=2, b=42, zzz=666)
a=2 b=42
args=()
kwds={'zzz': 666}
>>> four(b=2, a=32)
a=32 b=2
args=()
kwds={}

可変長引数を持つ関数は便利な場合もありますが、間違えをおかしやすく、メンテナンスもしにくくなるのでなるべく避けるようにして下さい。

ミュータブルな引数を持つ関数について

イミュータブルな変数が引数の場合は、関数の中で値を変えても呼び出し元では変更がありませんが、 ミュータブルな変数の場合は、呼び出し元でも反映されます。

ミュータブルな型( listdict )をデフォルト値として持つ関数については注意が必要です。

>>> def f(a, L=[]):
...     L.append(a)
...     return L
...
>>> print f(1)
[1]

>>> print f(2)     # what should this print ?
>>> print f(2)
[1, 2]

>>> print f(3)
[1, 2, 3]

# default values are evaluated ONCE.
# this means 'L' is always pointing at the same object
# in which we repeatedly append elements.

# prefer this way of passing mutable default values:
>>> def f(a, L=None):
...     if L is None: L = []
...     L.append(a)
...     return L
最後の例のようにすると、Lは毎回空のリストが使われます。

クラス

クラスは以下のように定義できます。

>>> class Track(object):
...    """Track represents a track with p, eta and phi components"""
...    def __init__(self, p=0, eta=0, phi=0):
...        """create a new track with p,eta,phi"""
...        self.p = p
...        self.eta = eta
...        self.phi = phi

>>> help(Track)
>>> help(Track.__init__)

>>> t1 = Track()
>>> t2 = Track(200)
>>> t3 = Track(200, 0.5, 1.2)
>>> print t1
&lt;__main__.Track instance at 0x1007ad440&gt;
>>> print t1.p
0
>>> print t1.phi
0
>>> print t3.phi
1.2
>>> print t3
&lt;__main__.Track instance at 0x1007ad128&gt;

>>> print t1.__init__ 
<bound method Track.__init__ of <__main__.Track instance at 0x1007ad440>&gt;
>>> print t1.__dict__
{'p': 0, 'phi': 0, 'eta': 0}

  • __init__ はクラスのコンストラクタです。
  • コンストラクタの最初の引数 (self, by convention) はオブジェクト自身の参照で必ず必要です。
  • データメンバーは__init__の中で動的に追加します。__dict__ が追加されたデータメンバーを格納しています。

>>> t1.p
0
>>> t1.p = 100   # change existing data member
>>> t1.p
100

>>> t1.z         # accessing non-existing member is an error
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: Track instance has no attribute 'z'
>>> t1.z = 0.5
>>> t1.z
0.5
>>> del t1.z
>>> t1.z
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: Track instance has no attribute 'z'

クラスの表示

クラスの表示の仕方は、__str__という関数で設定することができます。この関数は以下の場合に使われます。

  • print
  • str()
  • "%s" でフォーマットされた場合

Track クラスの表示の仕方を設定して見ます。:

class Track(object):
   """Track represents a track with p, eta and phi components"""
   def __init__(self, p=0, eta=0, phi=0):
       """create a new track with p,eta,phi"""
       self.p = p
       self.eta = eta
       self.phi = phi

   def __str__(self):
       """nice string representation"""
       return "Track(p=%8.2f eta=%5.2f phi=%5.2f)" % (self.p, self.eta, self.phi)

>>> t1 = Track()
>>> print t1
Track(p=    0.00 eta= 0.00 phi= 0.00)

  • メソッドからデータメンバーにアクセスする場合は、上の例で示した通り self.my_member のように書きます。( C++ ではthisは省略可能)

特別なメソッド

以下のように数をカウントするためのクラスを例にします。

class Counter(object):
    def __init__(self, start=0):
        self.count = start

    def up(self, n=1):
        self.count += n

    def down(self, n=1):
        self.count -= n

このクラスは以下のようにして数をカウントするのに使います:

>>> a = Counter(10)
>>> a.count
10
>>> a.up(2)
>>> a.count
12

ではここで、クラスの表現と、 + オペレーターを加えてみましょう。

class AddCounter (Counter):
      def __repr__(self):
          return "AddCounter(%s)" % self.count

      def __add__(self, other):
          return AddCounter (self.count + other.count)

__repr____str__ と同様に文字列を返します。 __repr__ は普通そのオブジェクトを作るためのコマンドを入れるようにします。

今定義したメソッドは以下のように使います。

>>> a = AddCounter (10)
>>> b = AddCounter (20)
>>> a
AddCounter (10)
>>> b
AddCounter (20)

>>> a+b                 # uses __add__ to create a new AddCounter instance
AddCounter (30)
>>> c = a+b             # ditto.
>>> c
AddCounter (30)

>>> c += AddCounter (2)  # there is no __iadd__ method defined on AddCounter,
>>> c                   # but python sees and then uses __add__:
AddCounter (32)          # c = c.__add__(AddCounter (2))

### corollary: beware that in this case, c is a new object (ie: if no __iadd__
### has been implemented) so its previous state will be somewhat lost:
>>> c = AddCounter (10)
>>> c
AddCounter (10)
>>> c.foo = 42
>>> c.foo
42
>>> c += AddCounter (3)
>>> c
AddCounter (13)
>>> c.foo
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: AddCounter instance has no attribute 'foo'

### this doesn't happen with the following version of AddCounter:
class AddCounter (Counter):
      def __repr__(self):
          return "AddCounter(%s)" % self.count

      def __add__(self, other):
          return AddCounter (self.count + other.count)

      def __iadd__(self, other):
          self.count += other.count
          return self

>>> c = AddCounter (10)
>>> c
AddCounter (10)
>>> c.foo = 42
>>> c.foo
42
>>> c += AddCounter (3)
>>> c
AddCounter (13)
>>> c.foo
42

多重継承

class MuonTrack (Track, Muon):
    def __init__(self, p, eta, phi, muon_type):
        super(MuonTrack, self).__init__(p, eta, phi)
   self.muon_type = muon_type

    def __str__(self):
        s = "MuonTrack(%s muon_type=%s)" % (
       super(MuonTrack, self).__str__(),
       self.muon_type)
   return s

  • 親クラス名をカッコの中に羅列します。
  • 子クラスの __init__ の中から親クラスの __init__ を呼びます。 Track.__init__(self, p, eta, phi) のように直接呼ぶか、上記のように super を使います。 super では両方の親クラスのメソッドが呼ばれます。
    • C++ のように自動的に呼ばれるわけではないので注意しましょう。

プライバシー

  • 全てのメンバーがパブリック
  • 慣習的にアンダースコア(_)一つで始まるメンバーは C++ で言う所の"protected"になる。: クラス外からのアクセスは(可能だが)してはいけない。
  • 同様に名前がアンダースコア二つで始まる場合は内部的特殊な名前に変換されるので、外からは通常の方法ではアクセスできなくなる。
  • アンダースコア二つで挟まれている名前は特殊なメンバー。
    • __init__, __repr__, __add__, ...

スロット

  • __slots__を定義した場合は、動的にデータメンバーが追加できなくなる。
class Track(object):
      __slots__ = ('p', 'eta', 'phi')
      def __init__(self, p=0, eta=0, phi=0):
          self.p = p
          self.eta = eta
          self.phi = phi

  • アトラスでは打ち間違えによるバグを避けるためによく使われる。
>>> t = Track()
>>> t.rta = 2        # oops! 'rta' instead of 'eta'!!
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Track' object has no attribute 'rta'

例外

例外は何らかのエラーが起こった時に、それよりも後のコードを飛ばして関数からぬける仕組み。 そのままだとプログラムが終了してしまうが、普通はその例外を捕捉して何らかの処理を行う。

通常例外はエラーによって発生するが、以下のようにユーザーが発生させることもできる。

raise RuntimeError ("something went terribly wrong")
   

  • 例外を捕捉するには以下のように、 tryexcept を使う。
d = {}
try:
   # exceptions raised in this block are caught
   mistake = d['no-such-key']
except KeyError, e:
   # a KeyError exception was raised, and will be assigned to 'e'
   print e    # print the exception. uses KeyError.__str__
except AttributeError, e:
   # an AttributeError was raised
   print e
except Exception, e:
   # any other exception raised (that derives from class Exception)
   print e
else:
   # no exception was raised
   pass       # do nothing
   

組み込みの例外

  • KeyError: dict に指定されたキーがない
  • IndexError: listtuple の大きさを超えたインデックスが指定された
  • AttributeError: クラスに存在しないアトリビュートにアクセスされた
  • NameError: 値の設定されていない変数へのアクセス
  • SyntaxError: 構文が間違っている
  • ImportError: import で指定されたモジュールが見つからない
  • RuntimeError: その他の例外

組み込み例外のフルリスト: http://docs.python.org/library/exceptions.html

例外クラスを自作する時には以下のように Exception クラスを継承するようにする。

class MyException (Exception):
     pass

>>> raise MyException ("foo")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
__main__.MyException: foo

モジュール

モジュールとは(普通は関連のある機能を持つ)パイソンのクラス等がまとめて書かれているファイル。拡張子は .py

  • 他のパイソンコードからモジュール内の機能を使うには、以下のように import する。
import MyModule
   

  • モジュールの中に書かれているクラスなどは、そのモジュールの名前空間内にあるので、アクセスする時は以下のようにモジュール名とクラス名をつなげる。
import MyModule
a = MyModule.SomeClass ()
   

  • モジュール名無しでクラス名をそのまま使いたい場合は以下のようにする。
from MyModule import SomeClass
a = SomeClass ()
   

  • あるディレクトリに __init__.py というファイルがある場合はそのディレクトリがモジュールとなる。 __init__.py ファイルはモジュールがインポートされた時に実行されるが、中身が空でもよい。 そのディレクトリ内の python ファイルはそのモジュールのサブモジュールになる。
from RecExConfig.RecFlags import Rec
   
    • RecExConfig がモジュール
    • RecFlags がサブモジュール
    • RecRecExConfig.RecFlags のオブジェクト

  • import$PYTHONPATH という環境変数のパスにあるモジュールを探す。
  • import によってそのモジュールの中身が実行されるのは1回目のみ。(C++のようにヘッダーファイルに複数のincludeに対するガードをつける必要がない)

標準モジュール

  • "batteries included" (電池が付属)の思想があり、多くの人が使うモジュールはすぐに使えるようになっている。

  • sys: システム関連のモジュール
>>> import sys
>>> sys.path     # list of paths to search for python modules
                 # i.e.: $PYTHONPATH + some system paths

>>> sys.modules  # dict of currently loaded modules

>>> sys.argv     # list of command line arguments
                 # sys.argv[0]  == name of the python script being run
       # sys.argv[1:] == arguments to the script
   
  • os: OS関連のモジュール
>>> import os
>>> os.listdir("/") # returns a list containing the names of the 
                    # entries in the provided directory

>>> os.path.join("home", "foo") # joins 2 or more pathname components (cross-platform)

>>> os.path.exists("/tmp")   # tests whether a path exists
   

ファイル入出力

>>> f = open("myfile.txt", "w")   # open a file in write mode
>>> print &gt;&gt; f, 1, 2, 3, 4        # print a bunch of numbers into that file
>>> f.write('5 6 7 8')            # ditto
>>> f.write('9 10\n')             # ditto, but append a new-line
>>> f.flush()                     # commit any lingering buffer to file
>>> f.close()

>>> f = open("myfile.txt")        # default is to open file in read-only mode "r"
>>> for line in f:
...    print line
1 2 3 4

5 6 7 89 10

>>> f.seek(0)                     # rewind to beginning of file
>>> for line in f:
...    print repr(line)
'1 2 3 4\n'
'5 6 7 89 10\n'

ATLAS jobOptions ファイル

最後に例として、ATLASのjobOptions (Athenaのコンフィグレーションファイル)を見てみましょう。 次のファイルは明日の講習で使うHiggs崩壊事象のイベント生成のためのファイルです。

https://svnweb.cern.ch/trac/atlasoff/browser/Generators/MC15JobOptions/trunk/share/DSID341xxx/MC15.341102.Pythia8EvtGen_A14NNPDF23LO_ZllH125_bb.py

文字列やリストを使ってヒッグスの質量等の設定しているのがわかると思います。

参考

http://docs.python.org

http://www.doughellmann.com/PyMOTW/

http://jacek.home.cern.ch/jacek/python-course/

http://pypi.python.org/pypi

http://www.scipy.org/

http://docs.scipy.org/doc/

http://matplotlib.sourceforge.net/index.html

http://docs.cython.org/

http://www.pytables.org/moin

http://h5py.alfven.org/docs/guide/quick.html

http://wlav.web.cern.ch/wlav/pyroot/

http://root.cern.ch/drupal/content/how-use-use-python-pyroot-interpreter


Edit | Attach | Watch | Print version | History: r15 < r14 < r13 < r12 < r11 | Backlinks | Raw View | WYSIWYG | More topic actions
Topic revision: r15 - 2017-01-23 - RyuSawada
 
    • Cern Search Icon Cern Search
    • TWiki Search Icon TWiki Search
    • Google Search Icon Google Search

    Main All webs login

This site is powered by the TWiki collaboration platform Powered by PerlCopyright &© 2008-2024 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
or Ideas, requests, problems regarding TWiki? use Discourse or Send feedback