Shibuya.lispの比較言語論トピックにPythonコード投稿したお

Pythonでクラスを使った実装。

懺悔

Pythonが得意じゃない僕なんかが記事を書いてごめんなさい。ごめんなさい。

最近はもっぱらCSSとかHTMLをいじってます。あとは画像の修正とか。本当にPythonにわかでごめんなさい。


追記:お詫びした理由は、Python界にすごい人がいすぎて今日は凹んでいるため。

本題

さて、Shibuya.lispGoogle groupsで"比較言語論"というトピックがあったのでそのお話。

そのトピックのテーマはこれ。

オブジェクト指向プログラミングの例題で誰でもやってしまうという例題。
人間と学生クラスを例にとって、各種の言語でプログラミングして比較してみます。

https://groups.google.com/forum/#!topic/shibuyalisp/CgBGde3Fz1E/discussion

現状投稿されているコード。

現状投稿されているコードはこれら。

で今回私が投稿したPythonのコード。

コード

私が投稿したコードはこれ。

Person = type('Person', (), {})
Person.__init__ = lambda self, name: setattr(self, 'name', name)
Person.setAge = lambda self, age: setattr(self, 'age', age)
Person.getAge = lambda self: getattr(self, 'age')
Person.setName = lambda self, name: setattr(self, 'name', age)
Person.getName = lambda self: getattr(self, 'name')

Student = type('Student', (Person,), {})
Student.__init__ = lambda self, grade: (lambda self, grade, _: setattr(self, 'grade', grade))(self, grade, Person.__init__(self, 'hiroshi'))
Student.setGrade = lambda self, grade: setattr(self, 'grade', grade)
Student.getGrade = lambda self: getattr(self, 'grade')

Client = type('Client', (), {})
Client.__init__ = lambda self: setattr(self, 'student', Student(1))

client = Client()
print(client.student.name)
ポイント

ポイントは

  • class構文はtype()の糖衣構文なのでtypeでゴー!
  • lambda構文は無名関数でdefより書きやすいよね。
  • ビルドイン関数のsetattrとgetattrを有効活用!

お詫び

Clientの実装が適当でclient.student.nameと直アクセスしてるのはごめんなさい。ごめんなさい。

皆さんの反応

私の周りの方々の反応。

  • 普通に書けばいいと思うけど…
  • まぁ、これが Python だって紹介されたら使う気をなくすコードだとは思う。
  • 可読性が低くて、何をやっているか推測しづらいと思いますけど?
  • ああ、たしかにあのコードは Python で書いてあるけど JS っぽいんだ。
  • 普通のプログラムでやることじゃないと思う(わら
  • 自分でフレームワークやら足回りをごりごり書くときに使うとは思いますよ
  • ま、こういう魔法はなるべく隠しておいた方がきれいにかけるのですよ。
    • 魔法をふんだんに使ったプログラムはマジシャンにしか扱えないですからね
  • Pythonic じゃない、、、気がする
  • lambdaの使い方が面白かったです
  • 純粋でない関数型言語といわれてるLISP好きとしてはありだと思う
    • つーか、Pythonのlambdaは中途半端だよね
  • 正直面白い。

肯定的なコメントと否定的なコメントをそれぞれいただきました。

これ以外にもコメントがあったら、この記事へでもよいので連絡ください!

考察

魔法を使ったプログラムはPythonicではないというのがしっくりと飲み込める考えです。Pythonicの重要な構成要素かもしれませんね。

改良

type関数の第三引数を積極的に利用した改良後。
(もう少し改良して投稿すればよかった。)

Person = type('Person', (), {
        '__init__': lambda self, name: setattr(self, 'name', name),
        'setAge': lambda self, age: setattr(self, 'age', age),
        'getAge': lambda self: getattr(self, 'age'),
        'setName': lambda self, name: setattr(self, 'name', age),
        'getName': lambda self: getattr(self, 'name')})

Student = type('Student', (Person,), {
        '__init__': lambda self, grade: (lambda self, grade, _: setattr(self, 'grade', grade))(self, grade, Person.__init__(self, 'hiroshi')),
        'setGrade': lambda self, grade: setattr(self, 'grade', grade),
        'getGrade': lambda self: getattr(self, 'grade')})

Client = type('Client', (), {
        '__init__': lambda self: setattr(self, 'student', Student(1))})

client = Client()
print(client.student.name)
皆さんの反応
  1. 「魔法」が指しているのは type() なので全然変わっていない。
  2. いきなり getAge() を呼ぶと落ちそう
  3. setter, getter を作るのは python っぽくない
  4. getter があるのに client.student.name ってアクセスするのおかしくない?

再改良

コメントいただいたので再改良。

  1. いきなり getAge() を呼ぶと落ちそう
  2. setter, getter を作るのは python っぽくない
  3. getter があるのに client.student.name ってアクセスするのおかしくない?

という意見をもらったので、アクセサーを廃止。

Person = type('Person', (), {
        '__init__': lambda self, name: setattr(self, 'name', name)})

Student = type('Student', (Person,), {
        '__init__': lambda self, grade: (lambda self, grade, _: setattr(self, 'grade', grade))(self, grade, Person.__init__(self, 'hiroshi'))})

Client = type('Client', (), {
        '__init__': lambda self: setattr(self, 'student', Student(1))})

client = Client()
print(client.student.name)

再々改良

  1. 「魔法」が指しているのは type() なので全然変わっていない。

というわけでclass構文を使う。

class Person(object):
  __init__ = lambda self, name: setattr(self, 'name', name)

class Student(Person):
  __init__ =  lambda self, grade: (lambda self, grade, _: setattr(self, 'grade', grade))(self, grade, Person.__init__(self, 'hiroshi'))

class Client(object):
  __init__ = lambda self: setattr(self, 'student', Student(1))

client = Client()
print(client.student.name)

再々改良。その2

追記。

class Person(object): __init__ = lambda self, name: setattr(self, 'name', name)
class Student(Person): __init__ =  lambda self, grade: (lambda self, grade, _: setattr(self, 'grade', grade))(self, grade, Person.__init__(self, 'hiroshi'))
class Client(object): __init__ = lambda self: setattr(self, 'student', Student(1))
client = Client()
print(client.student.name)
皆さんの反応
  • Python の lambda って便利だからみんな使うけど、どっちかで選んだら Pythonic じゃない方になると思う
  • あえて lambda を多用する目的ってあんまりないと思う
    • 個人的な考えね、本当にそうかどうかは分からない

再々々改良

def構文を使う。

class Person(object):
  def __init__(self, name): setattr(self, 'name', name)

class Student(Person):
  def __init__(self, grade): (lambda self, grade, _: setattr(self, 'grade', grade))(self, grade, Person.__init__(self, 'hiroshi'))

class Client(object):
  def __init__(self): setattr(self, 'student', Student(1))

client = Client()
print(client.student.name)

再々々々改良

lambda 廃止。

class Person(object):
  def __init__(self, name): setattr(self, 'name', name)

class Student(Person):
  def __init__(self, grade):
    Person.__init__(self, 'hiroshi')
    setattr(self, 'grade', grade)

class Client(object):
  def __init__(self): setattr(self, 'student', Student(1))

client = Client()
print(client.student.name)

再々々々々改良

setattrとgetattrを使わない。

class Person(object):
  def __init__(self, name):
    self.name = name

class Student(Person):
  def __init__(self, grade):
    Person.__init__(self, 'hiroshi')
    self.grade = grade

class Client(object):
  def __init__(self):
    self.student = Student(1)

client = Client()
print(client.student.name)

以上、おしまい。
あなたはどの書き方がしっくりきましたか?

関連

@t2y(id:t2y-1979)さんが技評さんの記事、そうだ! EuroPython 2011へ行こう #4 5 Years Of Bad Ideas:過去5年間のバッドアイディアの記事にmitsuhikoことArmin Ronacherさんの発表をまとめています。

このセッションは

  • メタクラス
  • AST(抽象構文木)のハック
  • インタープリターとの魔大戦
  • 暗黙のself
    • Pythonの特徴の1つ,明示的なselfを隠してしまうハックです。
      • selfが嫌いな方もこれでこまりませんね:-p

を含んでおり楽しめると思いますよ:-)

動画

発表の動画もあります♪