#概要
Pythonはホワイトスペースを有効活用した言語ですが、その結果、ワンライナーをすることが非常に困難でもあります。
しかし、以前書いたように、リスト内法表記は素晴らしい表現力を持っています。また、リスト内法表記でLISPを実装した猛者もいます。
この記事では、リスト内包表記を主武器に「Pythonでワンライナー」を目指します。
なお、可読性のために改行やインデントをしていますが、全て省くことも可能です。
現在、自動生成器(変換器?)を作ってます。
#免責
著者は、本記事の情報を用いることで生じたいかなる不都合に対しても責任を負いません。
#第一目標
以下のコードをリスト内包表記で実装してみましょう。
a = 7
b = 2
a = 3
c = a + b
print(c)
#=> 5
とりあえず答え
[ns
for ns in [dict()] if not(
ns.update({"a" : 7) or
ns.update({"b" : 2}) or
ns.update({"a" : 3}) or
ns.update({"c" : ns["a"] + ns["b"]}) or
print(ns["c"]) or
None)]
リスト内包表記オバケでありながら、「何がしたいコードか」は何となく分かるのではないでしょうか?
具体的に中身を見ていきましょう。
##名前空間
この作業の上で、最も重要な概念は「名前空間」です。1
名前空間、というのは一般的なプログラミングにも使われる用語ですが、端的に言うと「変数とそれが保持する値の一覧」です。
したがって、これは辞書として実装することができます。2
つまり、変数の操作は「辞書への操作」によって行えることになります。
このコードでは名前空間はns
という辞書で表現されています。
ns
は最初の行の[dict()]
というところで初期化されます。
ようするに、このns
を操作することが、変数操作の肝になるわけです。
辞書の操作は関数を実行することが多いので、まず先に関数の実行についてみてみましょう。
##関数の実行
ここに書いたように、[i for i in list if hoge]
のhoge部分に関数をぶち込むと、関数が実行できます。or
でつなぐと、短絡評価の仕組みに基づいて、複数実行してくれます。3
if not
とnot
を入れているのは、次の段階への布石です。現段階では、not
を省いても出力は変わりません。
##変数の操作
変数の操作は「ns
という辞書への操作」」と同義である、という説明をしました。
関数が実行できるようになったので、辞書のメソッドを使ってみましょう。
上で用いているのは、ns.update({key:value})
という操作です。これは「辞書への追加」操作なので、「変数の追加」ということになります。
##目標1のまとめ
リスト内包表記によるプログラミングでは、「名前空間」という「変数名と値をつなぐ辞書」を使います。
名前空間は関数により操作され、収録する変数の状態が変化します。
関数の実行にはif not (func1() or func2())
を使います。
これは返値のない関数はNone
を返す性質を利用しています。
#目標2
以下のコードをリスト内包表記で実装してみましょう。
num = int(input())
if num % 2:
print("odd")
else:
print("even")
#<= 3
#=> odd
答え
[ns
for ns in [dict()] if not(
ns.update({"num" : int(input())}) or
(( #if
print("odd") or
None)
if ns["num"] % 2
else (
print("even") or
None) or
None) or
None)]
3項演算子を使ってます。
私は3項演算子の利用を「純粋なリスト内包表記」とは認めていません。(何者)
しかし、この記事の目的は「pythonワンライナー」なので許容します。
#目標3
num = 5
for i in list(range(num)):
print(i)
i = i + 1
print(i)
#=> 0
#=> 1
#=> 1
#=> 2
#=> 2
#=> 3
#=> 3
#=> 4
#=> 4
#=> 5
答え
[ns
for ns in [ns
for ns in [dict()] if not(
ns.update({"num" : 5})
)]
for l in [list(range(ns["num"]))] for _ in l if not (ns.update({"i":_}) or
print(ns["i"]) or
ns.update({"i": ns["i"] + 1}) or
None)]
##for文
この中でfor i in list(range(num)):
に相当するのは
for l in [list(range(ns["num"]))] for _ in l if not (ns.update({"i":_}) or
です。
-
l
にイテレーターを格納 - 一つだけ値を取り出し、
_
に一時的に格納 -
if not
以降の関数操作で名前空間に登録
という処理をしています。
もっと短くも書けるのですが、このように書くとl.clear()
を使って、break
ができるようになります。
より注目してほしいのは、for ns in [ns
と書いてある二行目です。
このコードでは、for文を実装するためにnum
への代入操作をしたリストを包含したリストを作っています。
構造としては、
[... [nsの操作] for文]
という入れ子構造をしているわけです。
for ns in [ns
は、内側で作成した名前空間を一つ外側の入れ子構造に継承する役割を持っています。
#目標4
num = 0
while True:
print("#=>",num)
if num == 5:
break
else:
num = num + 1
continue
#=> 0
#=> 1
#=> 2
#=> 3
#=> 4
#=> 5
答え
[ns
for ns in [ns
for ns in [dict()] if not(
ns.update({"num" : 0})
)]
for l in [[None]] for _ in l if not (
print(ns["num"]) or
(( #if
l.clear() or
None)
if ns["num"] == 5
else (
ns.update({"num":ns["num"] + 1}) or
l.append(None) or
None)) or
None)]
基本的に、目標3の改変です。無限ループを扱うためにfor文を有効活用しています。
#目標5
モジュールとか関数とかクラスとかを使った、より本格的なプログラミングもしてみましょう。
import random
class LiveEntity(object):
def __init__(self, name,HP, ATK, DEF = 0):
self.name = name
print("Hi, I'm {0}.".format(self.name))
self.HP = HP
self.ATK = ATK
self.DEF = DEF
self.is_alive = True
def attack(self, target):
attack_v = random.randint(0,self.ATK)
print("{0} attacked {1} : {2}".format(self.name, target.name, attack_v))
target.attacked(attack_v)
def attacked(self, attack_v):
defence = random.uniform(0,self.DEF)
damage = int(attack_v * (1 - defence))
print("{0} lost HP : {1} - {2}".format(self.name, self.HP, damage))
self.HP -= damage
if self.HP <= 0:
self.HP = 0
self.die()
def die(self):
self.is_alive = False
print("{0} is gone".format(self.name))
Player = LiveEntity("KTakahiro", 10, 2)
Enemy = LiveEntity("Exam", 100, 100, 0.5)
print("-----\nFIGHT!\n-----")
while True:
if Enemy.is_alive and Player.is_alive:
break
Player.attack(Enemy)
Enemy.attack(Player)
出力の一例です。
Hi, I'm KTakahiro.
Hi, I'm Exam.
-----
FIGHT!
-----
KTakahiro attacked Exam : 0
Exam lost HP : 100 - 0
Exam attacked KTakahiro : 9
KTakahiro lost HP : 10 - 9
KTakahiro attacked Exam : 1
Exam lost HP : 100 - 0
Exam attacked KTakahiro : 47
KTakahiro lost HP : 1 - 47
KTakahiro is gone
モジュールの利用には__import__
、関数定義にはlambda
、クラス定義にはtype
を使います。詳細はリスト内包表記の活用と悪用を読んでください。
最後の例になるので、上から順に少しずつやっていきましょう。
まず、ライブラリのインポート
[ns
for ns in [dict()] if not(
ns.update({"random" : __import__("random")}) or
None)]
次にクラスの定義
[ns
for ns in [dict()] if not(
ns.update({"random" : __import__("random")}) or
ns.update({"LiveEntity": type("LiveEntity",(
),{
"__init__": lambda self, name, HP, ATK, DEF = 0:(setattr(self, "local0", dict([])) or
setattr(self, "name", name) or
print("Hi, I'm {0}.".format(self.name)) or
setattr(self, "HP", HP) or
setattr(self, "ATK", ATK) or
setattr(self, "DEF", DEF) or
setattr(self, "is_alive", True) or
delattr(self, "local0") or None),
"attack": lambda self, target:(setattr(self, "local1", dict([])) or
self.local1.update({"attack_v": int(ns["random"].uniform(0, self.ATK))}) or
print("{0} attacked {1} : {2}".format(self.name, target.name, self.local1["attack_v"])) or
target.attacked(self.local1["attack_v"]) or
delattr(self, "local1") or None),
"attacked" : lambda self, attack_v:(setattr(self, "local2", dict([])) or
self.local2.update({"defence":ns["random"].uniform(0,self.DEF)}) or
self.local2.update({"damage" : int(attack_v * (1-self.local2["defence"]))}) or
print("{0} lost HP : {1} - {2}".format(self.name, self.HP, self.local2["damage"])) or
setattr(self,"HP", self.HP - self.local2["damage"]) or
(( #if
setattr(self, "HP", 0) or
self.die() or
None)
if self.HP <= 0
else (None)) or
delattr(self, "local2") or None),
"die" : lambda self:(setattr(self, "local3", dict([])) or
setattr(self, "is_alive", False) or
print("{0} is gone".format(self.name)) or
delattr(self, "local3") or None),
})}) or
None)]
メソッド内のための名前空間はself.local
という辞書を作ることで実装しました。寿命は短いです。
次に、PlayerとEnemyのインスタンスを作ります。
[ns
for ns in [dict()] if not(
ns.update({"random" : __import__("random")}) or
ns.update({"LiveEntity": type("LiveEntity",(
),{
... #中略
})}) or
ns.update({"Player" : ns["LiveEntity"]("KTakahiro", 10, 2)}) or
ns.update({"Enemy" : ns["LiveEntity"]("Exam", 100, 100, 0.5)}) or
None)]
最後に、Whileの中で戦ってもらいます。
[ns
for ns in [ns
for ns in [dict()] if not(
ns.update({"random" : __import__("random")}) or
ns.update({"LiveEntity": type("LiveEntity",(
),{
"__init__": lambda self, name, HP, ATK, DEF = 0:(setattr(self, "local0", dict([])) or
setattr(self, "name", name) or
print("Hi, I'm {0}.".format(self.name)) or
setattr(self, "HP", HP) or
setattr(self, "ATK", ATK) or
setattr(self, "DEF", DEF) or
setattr(self, "is_alive", True) or
delattr(self, "local0") or None),
"attack": lambda self, target:(setattr(self, "local1", dict([])) or
self.local1.update({"attack_v": int(ns["random"].uniform(0, self.ATK))}) or
print("{0} attacked {1} : {2}".format(self.name, target.name, self.local1["attack_v"])) or
target.attacked(self.local1["attack_v"]) or
delattr(self, "local1") or None),
"attacked" : lambda self, attack_v:(setattr(self, "local2", dict([])) or
self.local2.update({"defence":ns["random"].uniform(0,self.DEF)}) or
self.local2.update({"damage" : int(attack_v * (1-self.local2["defence"]))}) or
print("{0} lost HP : {1} - {2}".format(self.name, self.HP, self.local2["damage"])) or
setattr(self,"HP", self.HP - self.local2["damage"]) or
(( #if
setattr(self, "HP", 0) or
self.die() or
None)
if self.HP <= 0
else (None)) or
delattr(self, "local2") or None),
"die" : lambda self:(setattr(self, "local3", dict([])) or
setattr(self, "is_alive", False) or
print("{0} is gone".format(self.name)) or
delattr(self, "local3") or None),
})}) or
ns.update({"Player" : ns["LiveEntity"]("KTakahiro", 10, 2)}) or
ns.update({"Enemy" : ns["LiveEntity"]("Exam", 100, 100, 0.5)}) or
print("-----\nFIGHT!\n-----") or
None)]
for l in [[None]] for _ in l if not (
(( #if
l.clear() or
None)
if (ns["Player"].is_alive and ns["Enemy"].is_alive)
else (
ns.update({"num":ns["num"] + 1}) or
l.append(None) or
None)) or
ns["Player"].attack(ns["Enemy"]) or
ns["Enemy"].attack(ns["Player"]) or
None)]
あとはこれを文字列として処理し、改行や余計なスペースをなくしてください。
[ns for ns in [ns for ns in [dict()] if not(ns.update({"random" : __import__("random")}) or ns.update({"LiveEntity": type("LiveEntity",( ),{ "__init__": lambda self, name, HP, ATK, DEF = 0:(setattr(self, "local0", dict([])) or setattr(self, "name", name) or print("Hi, I\'m {0}.".format(self.name)) or setattr(self, "HP", HP) or setattr(self, "ATK", ATK) or setattr(self, "DEF", DEF) or setattr(self, "is_alive", True) or delattr(self, "local0") or None), "attack": lambda self, target:(setattr(self, "local1", dict([])) or self.local1.update({"attack_v": int(ns["random"].uniform(0, self.ATK))}) or print("{0} attacked {1} : {2}".format(self.name, target.name, self.local1["attack_v"])) or target.attacked(self.local1["attack_v"]) or delattr(self, "local1") or None), "attacked" : lambda self, attack_v:(setattr(self, "local2", dict([])) or self.local2.update({"defence":ns["random"].uniform(0,self.DEF)}) or self.local2.update({"damage" : int(attack_v *(1-self.local2["defence"]))}) or print("{0} lost HP : {1} - {2}".format(self.name, self.HP, self.local2["damage"])) or setattr(self,"HP", self.HP - self.local2["damage"]) or(( setattr(self, "HP", 0) or self.die() or None) if self.HP <= 0 else(None)) or delattr(self, "local2") or None), "die" : lambda self:(setattr(self, "local3", dict([])) or setattr(self, "is_alive", False) or print("{0} is gone".format(self.name)) or delattr(self, "local3") or None), })}) or ns.update({"Player" : ns["LiveEntity"]("KTakahiro", 10, 2)}) or ns.update({"Enemy" : ns["LiveEntity"]("Exam", 100, 100, 0.5)}) or print("-----\nFIGHT!\n-----") or None)] for l in [[None]] for _ in l if not(((l.clear() or None) if(ns["Player"].is_alive and ns["Enemy"].is_alive) else( ns.update({"num":ns["num"] + 1}) or l.append(None) or None)) or ns["Player"].attack(ns["Enemy"]) or ns["Enemy"].attack(ns["Player"]) or None)]
出力
Hi, I'm KTakahiro.
Hi, I'm Exam.
-----
FIGHT!
-----
KTakahiro attacked Exam : 1
Exam lost HP : 100 - 0
Exam attacked KTakahiro : 83
KTakahiro lost HP : 10 - 83
KTakahiro is gone
お疲れさまでした。
#感想
なにやってんだおれ