Iterate All The Things
So after the rousing success of making int iterable (which I now know I could have done with ForbiddenFruit), I started wondering, "Why aren't classes iterable?"
from itertools import repeat
class IterCls(type):
def __iter__(self):
return repeat(self)
class Thing(metaclass=IterCls):
def __init__(self, frob):
self.frob = frob
def __iter__(self):
return repeat(self.frob)
So, that's a thing. Works just like you'd expect. Any class that declares IterCls
to be it's metaclass can be iterated over unendingly.
from itertools import islice
list(islice(Thing, 3))
And yes, the __iter__
on the actual Thing
class works, too.
t = Thing(4)
list(islice(t, 10))
But check this out:
from inspect import getsource
print(getsource(Thing.__iter__))
Odd, huh? I'm green around the ears with metaclasses so I'm not even 10% what's going on here other than maybe since Thing
is an instance of IterCls
(classes are objects), it's instance dictionary would defer to the class dictionary in IterCls
when looking for methods. Hell if I know right now.
I'm not really sure what you'd with this. Maybe hook some sort of alternative initializer method on there? However, without using some sort of global or stashing class attributes (or are they instance variables in this case?), I don't think it'd be incredibly useful. And the two argument version of iter
with a factory function would be much clearer and way less magical. Something like this:
from itertools import count
def ThingFactory(start=0, step=1):
frobs = count(start, step)
def maker():
nonlocal frobs
return Thing(frob=next(frobs))
return maker
for f in islice(iter(ThingFactory(), None), 3):
print(f.frob)
And, in case, you're wondering, yes modules themselves can be made iterable as well. Inspired by fuckit module fuckery.
from runnables import itermodule
print(itermodule)
print(list(islice(itermodule, 4)))
It's a module that implements a __iter__
. And it just spits out 4 all day long. Code here I was musing about it in ##learnpython on Freenode and one user commented it might maybe possibly be useful as a datatype. Import the module and use it to represent a CSV file for example -- but the real question being, "Why not just use a class in that case?"
Why?
I was bored. Wanted to see what Python would let me get away with in terms of making things iterable. At this point, I'd be confident that everything can be made iterable. Object method?
from functools import partial
class IterMethod:
def __init__(self, f):
self.f = f
def __get__(self, inst, cls):
f = self.f
if inst:
f = partial(f, inst)
return repeat(f)
class Thing:
@IterMethod
def frob(self, frob):
return frob
print("As instance method")
for f in islice(Thing().frob, 2):
print(f(4), end=' ')
print("\nAs class method")
for f in islice(Thing.frob, 2):
print(f(None, 5), end=' ')
Though, I think a straight up @property
would be clearer and probably more in line with what was expected:
class Thing:
def __init__(self):
self.frobs = count()
@property
def frob(self):
return iter(self.frobs.__next__, None)
t = Thing()
print(list(islice(t.frob, 5)))
print(list(islice(t.frob, 5)))
Iterable function? Use repeat as a decorator...actually, don't really do that. Just use repeat as normal. But in the face of boredom, clearer minds rarely prevail.
@repeat
def frob():
return 4
fs = [f() for f in islice(frob, 4)]
print(fs)
I guess don't do this at home? I can't really think of any practical applications for these sorts of things. But if you need to do them...I guess use this as a reference point? Actually, I can think of an application of an iterable function: composing a function N times. I've borrowed the compose function from here:
from functools import reduce
def compose(*functions):
def compose2(f, g):
return lambda x: f(g(x))
return reduce(compose2, functions)
def frob(x):
return x + 4
n_times = compose(*repeat(frob, 4))
print(n_times(0))
If you find any other useful applications, let me know, I'll gladly add them as examples.
No comments:
Post a Comment