Python Programming 動的にclassを作成

今日も見にきてくださって、ありがとうございます。

 勉強会でPythonの講師をさせていただいているので、ここのところPythonについて思いを馳せるタイミングが増えてきています。最近Pythonを使っていて、おお!と思ったことをちょっと書こうと思います。なんと、クラス作成後に属性を追加することができるのです!すべてオブジェクトにすると、こんなこともできてしまうのですね。ちょっといろいろと確認してみます。

 まずは、何もないクラスを作成。そして、インスタンスを作成します。

>>> class Box:
...     pass
... 
>>> box1 = Box()

なんと、このあと動的にアトリビュートを追加することができるのです。

>>> box1.x = 100
>>> box1.y = 120         
>>> print(box1.x, box1.y)
100 120
>>> 

当然、メソッドも追加できますよね?
ということで、やってみます。まず関数を定義して、代入して、実行してみます。

>>> def getpos(self):
...     print(self.x, self.y)
...
>>> box1.getpos = getpos
>>> box1.getpos()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: getpos() missing 1 required positional argument: 'self'
>>> </module></stdin>

おっと、うまくいきませんね。ちなみに、この「self」というのは、クラスのメソッドがインスタンスになった時、関数実行時に一つ目のパラメータとしてインスタンスが渡されるために必要になる引数です。ホントはどんな名前でもいいのですけど慣例として「self」とすることになっています。
あ、ここまで書いて気づきました。インスタンスに関数を定義したのがいけないのかも知れません。Boxクラスの方へセットしてみます。

>>> Box.getpos = getpos
>>> box2 = Box()
>>> box2.x = 300
>>> box2.y = 320
>>> box2.getpos()
300 320
>>>              

できました!
このように、クラス作成後に動的にクラスを変更できてしまうので、プログラムを使ってコーディングすることができますね。ちなみに、アトリビュートは文字列を使ってセットできることを確認しました。dir()関数を使って、定義したクラスの中身を調べてみました。

>>> dir(Box)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'getpos']
>>> 

名前からアトリビュートっぽいのを探ります。以前いろいろと探して「__dir__」に含まれているのを知っていたので、中を見てみましょう。

>>> dir(Box.__dict__)
['__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'copy', 'get', 'items', 'keys', 'values']

ええと、ディクショナリークラスの中身のようですね。見方が違いました。きっと、こうですね。

>>> Box.__dict__
mappingproxy({'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Box' objects>, '__weakref__': <attribute '__weakref__' of 'Box' objects>, '__doc__': None, 'getpos': <function getpos at 0x0000029DA5021E18>})

先程追加した関数getposはありましたが、xとyがありませんね。あ、、、そういえばインスタンスの方にセットしただけでした。では、インスタンスの方を確認しましょう。

>>> box1.__dict__
{'x': 100, 'y': 120, 'getpos': <function getpos at 0x0000029DA5021E18>}

そうそう、こんな感じです。
では、文字列の’x’と’y’を使って、クラスの方へアトリビュートと初期値をセットしてみます。

>>> setattr(Box,'x',10)
>>> setattr(Box,'y',20)
>>> dir(Box)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'getpos', 'x', 'y']
>>> box3 = Box()
>>> box3.getpos()
10 20
>>>

はい、見事、xとyがクラスに追加されて、メソッドが最初から利用できるようになりましたね。オブジェクトの場合は「__dict__」を普通のdictと同じようにしてアトリビュートを追加することができたのですけど、クラスの場合の「__dict__」はmappingproxyになっていて読み込みのみでしたので、setattrを使う必要がありました。ちなみに、中を見てみるとどうなっているかというと、

>>> Box.__dict__
mappingproxy({'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Box' objects>, '__weakref__': <attribute '__weakref__' of 'Box' objects>, '__doc__': None, 'getpos': <function getpos at 0x0000029DA5021E18>, 'x': 10, 'y': 20})
>>>

こんな感じです。オブジェクトの方にアトリビュートを追加するもう一つの方法は以下の通りです。

>>> box3.__dict__['z'] = -100
>>> box3.__dict__
 {'z': -100}
>>>

おっと、少し想定と違いました。xとyがいませんね。クラスで定義したアトリビュートはここには入らない、ということでしょうか。以下の通り、値は取り出せました。

>>> box3.x
10
>>> box3.y
20
>>> box3.z
-100
>>>

ちょっと、xに値を代入したらどうなるかやってみます。

>>> box3.x = -10
>>> box3.__dict__
{'z': -100, 'x': -10}
>>> 

なるほど、思った通りですね。クラスで定義した値が変更されていなければインスタンスごとにその値を持つ必要がない、ということですね。

そういえは、関数を文字列から作成して組み込むのはどうやるのだろうか、と、気になってまいりましたが、長くなってきたので今回はこれくらいで。

WordPressでMarkdownを使うには

International Arrivals

 今日も見に来てくださって、ありがとうございます。今日は、Markdownについてです。

 先日Google Colaboratoryを発見した、という記事を書きました。Pythonのスクリプトを実行したり、簡単なドキュメントをMarkdown形式で記載できるツールです。これまでにこのブログを書いている中で、スクリプトとか、もうちょっと書きやすくならないかなぁ、と不便を感じていました。Colaboratoryでは、Markdownで美しく書くことができていいなぁ、そういえば、WordPressはいろんなプラグインを入れられるから、もしかしたらMarkdownも使えるようにできるかも、と、調べてみました。
 ちなみに、Markdownとは、テキストだけでHTMLを出力できるように考えられた記法のことです。例えば、行の先頭に「# 」と記載することで、HTMLの見出しである<H1>タイトル</H1>を表現します。これらの記法を少し学べばテキストだけで美しい文章が作れ、テキストだけ見ても何となく構成がわかる、というのが特徴です。

 で、Google先生に聞いてみました。なんと、ぼくの環境では、Gutenbergが入っているので、すでに使えるようになっているそうです。早速やってみます。

# タイトル

 ええと、見出しになりませんねぇ。(汗)(Markdown記法では、先頭に「# 」が付いた行はHTMLで言うところのH1の見出しになります。「## 」「### 」と、個数が増えると順に小さくなっていきます。)さらにGoogle先生に聞いてみます。。。おお、シャープを入力した後にスペースを入力せよ、と書いてありました。実際にやってみましたが、なぜかできない。いろいろ試した結果、やっとできました。シャープ1個の時は、ダメで、2個以上だと動作するようです。

はい、こんな感じです。できました!

そして、バッククオートを三つ書いてエンターキーを押すと「```」
このような、コードを入力モードになりました。
  • 「- 」とスペースで箇条書きがスタートします。
  • エンターで次の箇条書きになります。

 なるほど、ブロック単位で設定していくようです。コードブロックは先頭行で「``` 」だし、箇条書きは先頭行で「- 」という感じですね。ただ、ブロック毎に変換されるので、これは、編集中に出てくるボタンを使ってやるのとあんまり変わりませんね。文章の編集にGutenbergを使っていると、Markdownはうまく使えないようです。編集効率を上げるためにMarkdown形式をバッチリ使いたいなら、他の方法を検討しないとダメですね。Markdownが使えると何となくエンジニアっぽい気がするので、Markdownのプラグインを探してきて(あれば)入れてみますかねぇ。。。

【追記】Markdownで記載したものをコピペすると、いい具合に使えることがわかりました。ただ、そうすると、テキストと記事を二重管理することになりそうだなぁ、と、いうことでやっぱりプラグインかな、と、思っています。