On Apr 28, 2020, at 12:02, Alex Hall <[email protected]> wrote:
>
> Some libraries implement a 'lazy object' which forwards all operations to a
> wrapped object, which gets lazily initialised once:
>
> https://github.com/ionelmc/python-lazy-object-proxy
> https://docs.djangoproject.com/en/3.0/_modules/django/utils/functional/
>
> There's also a more general concept of proxying everything to some target.
> wrapt provides ObjectProxy which is the simplest case, the idea being that
> you override specific operations:
>
> https://wrapt.readthedocs.io/en/latest/wrappers.html
>
> Flask and werkzeug provide proxies which forward based on the request being
> handled, e.g. which thread or greenlet you're in, which allows magic like the
> global request object:
>
> https://flask.palletsprojects.com/en/1.1.x/api/#flask.request
>
> All of these have messy looking implementations and hairy edge cases. I
> imagine the language could be changed to make this kind of thing easier, more
> robust, and more performant. But I'm struggling to formulate what exactly
> "this kind of thing is", i.e. what feature the language could use.
For the case where you’re trying to do the “singleton pattern” for a complex
object whose behavior is all about calling specific methods, a proxy might
work, and the only thing Python might need, if anything, is ways to make it
possible/easier to write a GenericProxy that just delegates everything in some
clean way, but even that isn’t really needed if you’re willing to make the
proxy specific to the type you’re singleton-ing.
But often what you want to lazily initialize is a simple object—a str, a small
integer, a list of str, etc.
Guido’s example lazily initialized by calling getcwd(), and the first example
given for the Swift feature is usually a fullname string built on demand from
firstname and lastname. And if you look for examples of @cachedproperty (which
really is exactly what you want for @lazy except that it only works for
instance attributes, and you want it for class attributes or globals), the
singleton pattern seems to be a notable exception, not the usual case; mostly
you lazily initialize either simple objects like a str, a pair of floats, a
list of int, etc., or numpy/pandas objects.
And you can’t proxy either of those in Python.
Especially str. Proxies work by duck-typing as the target, but you can’t
duck-type as a str, because most builtin and extension functions that want a
str ignore its methods and use the PyUnicode API to get directly at its array
of characters. Numbers, lists, numpy arrays, etc. aren’t quite as bad as str,
but they still have problems.
Also, even when it works, the performance cost of a proxy would often be
prohibitive. If you write this:
@lazy
def fullname():
return firstname + " " + lastname
… presumably it’s because you need to eliminate the cost of string
concatenation every time you need the fullname. But if it then requires every
operation on that fullname to go through a dynamic proxy, you’ve probably added
more overhead than you saved.
So I don’t think proxies are the answer here.
Really, we either need descriptors that can somehow work for globals and class
attributes (which is probably not solveable), or some brand new language
semantics that aren’t built on what’s already there. The latter sounds like
probably way more work than this feature deserves, but maybe the experience of
Swift argues otherwise.
_______________________________________________
Python-ideas mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at
https://mail.python.org/archives/list/[email protected]/message/OGCFXBYXPT7AVJQLSW3HTNBP7SJJ7A5B/
Code of Conduct: http://python.org/psf/codeofconduct/