On Apr 28, 2020, at 12:02, Alex Hall <alex.moj...@gmail.com> 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 -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/OGCFXBYXPT7AVJQLSW3HTNBP7SJJ7A5B/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to