namedtupleの話
python2.6から導入されたnamedtupleについてちょっくら書いておくか。
namedtupleって?
namedtupleは名前の通り名前付きでアクセスできるtupleを返す。
>>> from collections import namedtuple
>>> p = namedtuple('Point', 'x y')
>>> p1 = p(11, 22)
>>> p1[0]
11
>>> p1[1]
22
>>> p1.x
11
>>> p1.y
22
>>> p2 = p(x = 33, y = 44)
>>> p2[0]
33
>>> p2[1]
44
>>> p2.x
33
>>> p2.y
44
>>>
とまあこんな感じ。
namedtuple関数に名前とフィールドを渡すと名前付きでアクセスできるものを
作成するものを返す。
仕組み
実際にはnametuple関数はクラスを返している。
>>> namedtuple('Point', 'x y')
このクラスはtupleのサブクラスである。
詳細はverboseオプションで確認できる>>> namedtuple('Point', 'x y', verbose=True)
class Point(tuple):
'Point(x, y)'__slots__ = ()
_fields = ('x', 'y')
def __new__(cls, x, y):
return tuple.__new__(cls, (x, y))@classmethod
def _make(cls, iterable, new=tuple.__new__, len=len):
'Make a new Point object from a sequence or iterable'
result = new(cls, iterable)
if len(result) != 2:
raise TypeError('Expected 2 arguments, got %d' % len(result))
return resultdef __repr__(self):
return 'Point(x=%r, y=%r)' % selfdef _asdict(t):
'Return a new dict which maps field names to their values'
return {'x': t[0], 'y': t[1]}def _replace(self, **kwds):
'Return a new Point object replacing specified fields with new values'
result = self._make(map(kwds.pop, ('x', 'y'), self))
if kwds:
raise ValueError('Got unexpected field names: %r' % kwds.keys())
return resultdef __getnewargs__(self):
return tuple(self)x = property(itemgetter(0))
y = property(itemgetter(1))
>>>とまあどんなクラスを生成したか見る事ができる。
で気になったのはこいつの作り方。正直この手のコードが標準ライブラリに
入るとはあんまり思ってなかったんだが。numfields = len(field_names) argtxt = repr(field_names).replace("'", "")[1:-1] # tuple repr without parens or quotes reprtxt = ', '.join('%s=%%r' % name for name in field_names) dicttxt = ', '.join('%r: t[%d]' % (name, pos) for pos, name in enumerate(field_names)) template = '''class %(typename)s(tuple): '%(typename)s(%(argtxt)s)' \n __slots__ = () \n _fields = %(field_names)r \n def __new__(cls, %(argtxt)s): return tuple.__new__(cls, (%(argtxt)s)) \n @classmethod def _make(cls, iterable, new=tuple.__new__, len=len): 'Make a new %(typename)s object from a sequence or iterable' result = new(cls, iterable) if len(result) != %(numfields)d: raise TypeError('Expected %(numfields)d arguments, got %%d' %% len(result)) return result \n def __repr__(self): return '%(typename)s(%(reprtxt)s)' %% self \n def _asdict(t): 'Return a new dict which maps field names to their values' return {%(dicttxt)s} \n def _replace(self, **kwds): 'Return a new %(typename)s object replacing specified fields with new values' result = self._make(map(kwds.pop, %(field_names)r, self)) if kwds: raise ValueError('Got unexpected field names: %%r' %% kwds.keys()) return result \n def __getnewargs__(self): return tuple(self) \n\n''' % locals() for i, name in enumerate(field_names): template += ' %s = property(itemgetter(%d))\n' % (name, i) if verbose: print template # Execute the template string in a temporary namespace and # support tracing utilities by setting a value for frame.f_globals['__name__'] namespace = dict(itemgetter=_itemgetter, __name__='namedtuple_%s' % typename) try: exec template in namespace except SyntaxError, e: raise SyntaxError(e.message + ':\n' + template) result = namespace[typename] # For pickling to work, the __module__ variable needs to be set to the frame # where the named tuple is created. Bypass this step in enviroments where # sys._getframe is not defined (Jython for example). if hasattr(_sys, '_getframe'): result.__module__ = _sys._getframe(1).f_globals['__name__'] return resultとまあ定番のexecとかsys._getframeとかやってますな。
これはなんだか黒魔術的な気がするんだけどどうなんだろう?
明示的な文化を持つPythonでは受け入れられない気がしてたのだが、最近では
緩くなったのかも知れないという話でした。
(namedtupleの実装は自動生成しまくりの真っ黒いメタクソプログラミングしたい人は参考にするといいかも知れない。)まあでも便利は便利よね。
例えばフィールドの定義がだるいのでこんな感じでクラス作るとかclass Point(namedtuple('Point', 'x y')): ...DBからのデータをクラスにMappingするのとかにも重宝する。
EmployeeRecord = namedtuple('EmployeeRecord', 'name, age, title, department, paygrade') import csv for emp in map(EmployeeRecord._make, csv.reader(open("employees.csv", "rb"))): print emp.name, emp.title
Shibuya.pm
まあShibuya.pmの申し込みに間に合わなくてustで見てたわけだけど。
見てるとやっぱ凄いなあ、おもろいなあという印象。
最近は裏でずっとocaml勉強してたんだけど見ててpython熱が復活した。
Shibuya.pm関係者のみなさんホントお疲れさまでした。
selfの話
メソッドのself (2)について少し書いてみる。
pythonの不満点でよく上がる定番の話は
- self地獄
- インデント地獄
- GIL外せよ!
なんだけどそのうちのひとつであるselfの話。
まあ死ぬほど聞く話ではあるけど。
多分和訳より原文の方を読むと掴めるんじゃないかな。
decoratorがなぜ問題になるか?って部分だけど。
decoratorってある意味ブラックボックスでなんでもできちゃうので自動でboundさせるのむずくね?みたいな話なのかなと。
def deco(fun): def meth(self, arg): self.val = "decorator" return self.val return meth class C(object): @deco def meth(self, arg): self.val = arg return self.val c = C() print(c.meth(1)) print(c.val)
渡したfuncを無視するなんてあんまし書かないコードではあるが。
(wrapするケースは多いよね)
selfなしを考える。
decorator内で関数を作ってそいつをメソッドのように振る舞わせる時にselfってどこから持ってこよう?
selfを無くす方向で書くと
def deco(fun): def meth(arg): #このselfって?? self.val = "decorator" return self.val return meth class C(object): @deco def meth(arg): self.val = arg return self.val c = C() print(c.meth(1)) print(c.val)
将来的にあるクラスのメソッドになりえるものを関数として定義しちゃうケースだとself何を指すのか?
関数でself???関数だよ?
ということになっちゃうよね?って話。
selfがあるからこそpythonのメソッドのbound、unboundがぬるく適当でも動いてくれる。
だからこそdecoratorみたいな関数を返す関数でクラスのメソッドを拡張できるわけだな。
pythonのメソッドのbound、unboundみたいな話は前にも書いたけど以下の書籍に割と書いてあるので
読むといいと思う。
![Pythonクィックリファレンス Pythonクィックリファレンス](https://images-fe.ssl-images-amazon.com/images/I/51E77SV460L._SL160_.jpg)
- 作者: アレックスマーテリ,Alex Martelli,クイープ
- 出版社/メーカー: オライリージャパン
- 発売日: 2004/03/01
- メディア: 単行本
- 購入: 2人 クリック: 158回
- この商品を含むブログ (25件) を見る
追記
selflessをmetaclassでやっちゃう話があったので紹介。自分のAttribute舐めてまわるというよくあるコード。
Meta-classes Made Easy
selfless-pythonは死亡してる模様です。。。