このBlogは移転しました。今後は aish.dev を御覧ください。

Pythonで++i

この間、Pythonで変数のインクリメントないよね、って話がでた。こんなのだ。

i = 99
++i
print i

ちなみに、Pythonでも ++i や --i と書くことはできる。書くことはできるが、Pythonでは ++ や -- はインクリメント/デクリメント演算子ではなく、単項の + や - を二つ続けて書いているだけで、こう書いたのと同じ結果になるだけだ。

++i == +(+(i))
--i == -(-(i))

だが、ここで重要なのは、この式はコンパイルエラーにはならないという事実だ。コンパイルエラーにならないということは、すなわち抽象構文木(AST)を作れるということだ。そしてASTを作れるということは、みんな大好きastモジュールで好き勝手できるということなのである。

import ast

class InclTransformer(ast.NodeTransformer):
    def visit_Expr(self, node):
        if isinstance(node.value, ast.UnaryOp):
            if isinstance(node.value.op, ast.UAdd):
                if isinstance(node.value.operand, ast.UnaryOp):
                    if isinstance(node.value.operand.op, ast.UAdd):
                        num = ast.copy_location(ast.Num(n=1), node)
                        n = ast.AugAssign(
                                target=node.value.operand.operand,
                                op=ast.Add(), value=num)
                        return ast.copy_location(n, node)
        return node

s = """
def inc(i):
    ++i
    return i
"""
m = ast.parse(s, "filename", "exec")
exec compile(InclTransformer().visit(m), "filename", "exec") in globals()

print inc(99)