7

I'm trying to implement a lazy-evaluated str-like class. What I have now is simething like

class LazyString(object):

    __class__ = str

    def __init__(self, func):
        self._func = func

    def __str__(self):
        return self._func()

which works fine (for my purposes) in most cases, except one: str.join:

' '.join(['this', LazyString(lambda: 'works')])

fails with

TypeError: sequence item 1: expected string, LazyString found

And after some poking around there doesn't seem to be any magic functions available behind this. join seems to be hard-coded inside the core implementation, and only instances of limited built-in type can make it work without actually being a str.

So am I really out of options here, or is there another way that I'm not aware of?

uranusjr
  • 1,380
  • 12
  • 36
  • 1
    Is it possible for you to subclass string and then adjust it for what you need? – Ffisegydd Dec 14 '13 at 12:16
  • If you're feeling adventurous, swear to never use this, and are using CPython, you could [monkey-patch](https://github.com/clarete/forbiddenfruit) `str.join`. – Blender Dec 14 '13 at 12:50
  • There are other situations where it's slightly annoying that `join` doesn't attempt to coerce its arguments to strings. What you want is a less strongly-typed language, but of course that has its drawbacks too. – Steve Jessop Dec 14 '13 at 12:53
  • Sort of a dupe of http://stackoverflow.com/questions/5588685. This question looks like a special case (not every object with `__str__` is intended as a lazily-evaluated string) but actually isn't. – Steve Jessop Dec 14 '13 at 12:58
  • @SteveJessop You're correct, and I wasn't aware of the existance of the mentioned question before you brought it up - thanks. The problem is not that `join` doesn't coerce for me -- I don't think it should, really -- but the fact that I can't make something "pretend" to be a string and fool `join`, even though that disguise works in most cases. Looks like I'm screwed here, just hoping for someone to nail the coffin for me. – uranusjr Dec 16 '13 at 05:51
  • @uranusjr: the reason I put it that way is that the disguise you're using, the `__str__` function, comes into effect if and only if something ties to convert your object to string. Your disguise is *precisely*, "I will lazily evaluate if coerced to `str`". Another way to look at it, I suppose, is that strings are too fundamental in Python for their interface to be duck-typed. – Steve Jessop Dec 16 '13 at 08:59

1 Answers1

3

join takes strings, so give it strings:

' '.join(map(str, ['this', LazyString(lambda: 'works')]))

Python does not have support for the kind of transparent lazy evaluation you're looking for. If you want to force evaluation of a lazy object, you will have to do so explicitly, rather than having it done automatically when needed. Sometimes, Python will call some method of your object that you can rely on, such as __nonzero__ if you want a lazy boolean, but not always, and you won't generally be able to achieve full interoperability.

user2357112
  • 260,549
  • 28
  • 431
  • 505