« KEMURI |Main| 日記あえ »

« KEMURI | English | »

« Pythonで指定したディレクトリの中を再帰的にdiff | Python | Pythonドキュメントの日英マッピングをするGreaseMonkey »

How to make oneliner in Python?

One day I fought on a boxing ring using my Thinkpad. It is a festival for nerds who loves lightweight language. (See Boxing in the LLRing?) My session was "Janken2.0", to make program to connect a server and play "Paper-Scissors-Rock". This entry is a FAQ, notFrequently Asked Question to make Python oneliner.

Is it difficult to make oneliners in Python?

You should learn Python more. Python use an indentation as a block. But what is the indentation? It is white region after "new line". No "new line" means no block, that is the most of syntax is disabled.

Is it impossible to make oneliners in Python?

No. See this. This is the "Paper-Scissors-Rock" agent.

jankenoneliner.png

How can I write "if" statement in a line?

"if" statement requires new line.

if condition:[new line]
    then-clause[new line]

if condition: then-clause[new line]

So, you can't use "if". Use "and" and "or".

condition and then-clause or else-clause

The right operand of "and" and "or" is evaluated lazily. But notice, if "bool(then-clause)" is False it doesn't work as you expected. You can envelope is with list

(a and [p(1)] or [p(2)])[0]

Given not-empty list as x, bool(x) is True.

How can I make a function?

"def" statement to make functions also requires "new line". So you must use "lamnda".

But lambda function can include expressions, not statements. You can't use assignments in lambda functions.

Oops, I can't use assignments?!

Don't worry. Python's great introspection allow you to remove assignment statements. The builtin function "globals()" returns global namespace as a dictionary. So when you want to do "x = 1", you just do "globals().__setitem__('x', 1)". Very easy. Sometimes you may use "locals()" for a local namespace and "x.__dict__" for a member of object x, but good programmers don't need them, because they use global namespace only.

How can I use "for" statements?

Why don't you use the list comprehension? I thought "for" statements were obsoleted.

>>> for i in range(5):
	print i

	
0
1
2
3
4
>>> import sys;[sys.stdout.write(str(i) + "\n") for i in range(5)]
0
1
2
3
4
[None, None, None, None, None]

[None, None, None, None, None] is the value of this expression. It is as True. You may have to use "and 0" to make it as False.

Is "while" statement replaced with recursive call?

Partially, yes. But if it loops too many times, it causes "RuntimeError: maximum recursion depth exceeded".

The other solution is lazy evaluation and infinite list. Import "ifilter" and "count" from itertools. "count" is a infinite list. "ifilter" creates a filtered list lazily. So "ifilter(bool, x).next()" evaluates x[0], x[1], x[2], ..., and stop iteration if x[n] is True.

>>> i = 1
>>> while i < 100:
	i *= 2

	
>>> i
128
>>> from itertools import ifilter, count; i = 1; ifilter(bool, ((i >= 100) or gl
obals().__setitem__("i", i * 2) for x in count())).next()
True
>>> i
128

How to substitute "break" statement and "else" clause?

It is very difficult. It is NOT difficult to write in Python, BUT difficult to write in English! "if condition: break" means "When the value of expression become paticular value, stop iteration". It is similar to the end of "while" statement. That is, the expression in "ifilter" should be as True when either the loop ended and the loop breaked. "else-clause" distinguish those two tarmination, so the value of expression must be different value.

I'll make this code to calculate prime numbers to oneliner.

>>> primes = []
>>> for i in range(2, 100):
	for p in primes:
		if i % p == 0:
			break
	else:
		primes.append(i)

>>> primes
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]

First, substitute the "for" statement by a "while" statement.

>>> primes = []
>>> for i in range(2, 100):
	j = 0
	while j < len(primes):
		if i % primes[j] == 0:
			break
		j += 1
	else:
		primes.append(i)

		
>>> primes
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]

I make the expression when "i % primes[j] == 0" the value becomes "BREAKED". When the loop tarminated without breaked, the value of loop become "True". After loop tarminated, if the value is "True", do "else-clause".

>>> globals().__setitem__("primes", []) or [
  globals().__setitem__("j", 0) or
  ifilter(bool,
    (
      not(j < len(primes)) or
      (i % primes[j] == 0 and "BREAKED") or
      globals().__setitem__("j", j + 1)
      for c in count()
    )
  ).next() == True and primes.append(i)
  for i in range(2, 100)
] and None
>>> primes
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]

And I removed newlines and redundant spaces.

>>> globals().__setitem__("primes",[])or[globals().__setitem__("j", 0)or ifilter
(bool,(not(j<len(primes))or(i%primes[j]==0 and"BREAKED")or globals().__setite
m__("j",j+1)for c in count())).next()==True and primes.append(i)for i in range(2
,100)]and primes
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]

How can I define a class?

There is no need to define your own class.

First, find a nice not read-only class or import it. Make an instance of the class. And push all fields and methods you need into it! Push, push, push!

If you need multiple instance, envelope the pushing code with lambda and make "builder function".

Here I wrote a sample to define a class.

>>> class Counter:
	def __init__(self):
		self.count = 0
	def __call__(self):
		self.count += 1
		return self.count

	
>>> c = Counter()
>>> c()
1
>>> c()
2

Import HTMLParser from HTMLParser, and push and push and push.

>>> from HTMLParser import HTMLParser; self = HTMLParser(); self.count = 0; self
.__call__ = lambda :self.__dict__.__setitem__("count", self.__dict__["count"] + 
1) or self.count
>>> self()
1
>>> self()
2

If you envelope them with lambda and assign it in global namespace, you can emulate the class definition.

>>> globals().__setitem__("Counter", lambda :(lambda self: self.__dict__.__setit
em__("count", 0) or self.__dict__.__setitem__("__call__", lambda :self.__dict__.
__setitem__("count", self.__dict__["count"] + 1) or self.count) or self)(__impor
t__("HTMLParser").HTMLParser()))
>>> c = Counter()
>>> c()
1
>>> c()
2
>>> c2 = Counter()
>>> c2()
1
>>> c()
3

How to "try" without "try...except"?

To catch a exception without try-statement is almost impossible. But I had to catch "Software caused connection abort" in my oneliner. I found a solution, it is not perfect substitution of "try...except", but it works.

Run the dengerous code as subprocess. When the subprocess dead, read its error message from the pipe and parse it.

    if sys.argv[3] == "CATCH":

        ifilterfalse(bool,
            (
                re.search("Software caused connection abort",
                    os.popen3(
                        r"python %s %s %s GO" % (
                            sys.argv[0],
                            sys.argv[1],
                            sys.argv[2]
                        )
                    )[2].read()
                )
                for x in count()
            )
        ).next()


    elif sys.argv[3] == "TRY":
        # dengerous code

It is work like following code.

while True:
    try:
        #  dengerous code
    except ....: # catch "Software caused connection abort" and ignore it
        pass

If you need some values from subprocess, you can use "cPickle" library. "cPickle.dumps(...)" and "globals.update(cPickle.loads(...))".

How to make import statements to expression?

Is it nessesary? If it is really nessesary, use built-in function __import__. But if you use global namespace only, you can simply import on the head of your code.

How to make short oneliner?

Find repeated string and make shorter. For example, "globals().__setitem__" is very frequent. Do "globals().__setitem__("g",globals().__setitem__)" or "globals().__setitem__("g",lambda x,y,z=globals():z.__setitem__(x,y))". If you have "not read-only and not callable" object x, try "x.__dict__.__setitem__("__call__", x.__dict__.__setitem__)".

Or compress it.

# Brainf*ck interpreter in Python Onliner Compressed
exec(reduce(lambda x,y:x.replace(y[0],y[1:]),'O1)q|Pz"t",|Qz"c",|Rimport |SY:zp,
|T)%256X|UY:z"c",c+1qp",p|V)or ifilter(bool,(|W)for x in count())).next()|Xqc",c
+1)q|Y",lambda|Z(globals().get(p,0)|q)or z"|zglobals().__setitem__('.split('|'),
'Rsys;from itertools Rcount,ifilter;[Q0qp",0qjY k:lambda:Z==0)^(k==-1)and(Pc+kqd
",1Vz"d",d+{"]":-1,"[":1}.get(s[t],0)*k)or d==0 or Pt+kWand Qt+1)or 1X>U+O<U-O+S
Z+1T-SZ+255T.Y:sys.stdout.write(chrZ)X,Sord(raw_input(">")[0]T[",j(O]",j(-Os",fi
le(sys.argv[1]).read()Vc==len(s)or(globals()[s[c]]()and 0W]'))

afterword

Thank you for reading this awful code written in English. I appreciate your patch to debug my English. Please feel easy to contact me!

トラックバック(Trackback)

Trackback URL: http://www.nishiohirokazu.org/mt/mt-tb.cgi/343

フィードバック

by Matt Rosin | 2006年09月26日 02:53

Hi! Thanks for linking. Boy I read your article and it made me afraid python would bite me! I like the line about Python's great introspection. And you do write great English, wow. Matt

ご意見・ご感想をお送りください(フィードバック)

(フィードバックはメールで送信され、基本的に表示されませんが、内容によっては公開させていただくこともございます。ご了承ください。Your comment doesn't appear the page immediately. If the comment has value to other people, it will be put on the page or subsequent entries. Thank you.)

上の情報は、いずれも未記入でかまいません。 All of above questions are optional.