Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

inheritance way to implement lazy objects #11

Open
felixcarmona opened this issue Sep 27, 2015 · 7 comments
Open

inheritance way to implement lazy objects #11

felixcarmona opened this issue Sep 27, 2015 · 7 comments

Comments

@felixcarmona
Copy link

I want to implement a new more unattended way to proxy objects, inheriting a LazyObject class instead of doing a = Proxy(lambda: A(5))

I'm doing this:

from lazy_object_proxy import Proxy

class LazyObject:
    def __new__(cls, *args, **kwargs):
        class_ = type(object.__class__.__name__, (cls,), {})
        class_.__new__ = lambda cls_, *args_, **kwargs_: object.__new__(cls_)
        return Proxy(lambda: class_(*args, **kwargs))

class A(LazyObject):
    def __init__(self, x):
        print("Init")
        self.x = 5

a = A(5)
print('GO')
print(a.x)

result:

GO
Init
5

What do you think about this implementation? do you think I've missed any concern? any way to implement this in C?

Regards

@ionelmc
Copy link
Owner

ionelmc commented Sep 27, 2015

object.__class__.__name__ will always be "type" - what did you meant to do there?

Is your intention roughly "delay object initialization" or "delay object creation"? It's not clear what's wrong with the regular way (lambda *args, **kwargs: Proxy(lambda: A(*args, **kwargs))) ...

@ionelmc
Copy link
Owner

ionelmc commented Sep 27, 2015

Doing something special in constructor (__new__) only makes sense if you want subclasses. Do you really need that?

Otherwise just use a class decorator:

def lazy(klass):
  def lazy_class(*args, **kwargs):
    return Proxy(lambda: klass(*args, **kwargs))
  lazy_class.__name__ = "lazy_" + klass.__name__
  return lazy_class

@lazy
class A(object):
  ....

@ionelmc
Copy link
Owner

ionelmc commented Sep 27, 2015

Now that I look again, your example seems fine (I didn't test it so it's a loose measure of "fine" 😁).

You should replace class_ = type(object.__class__.__name__, (cls,), {}) with class_ = type(cls.__name__, (cls,), {}) for better __repr__ output.

@felixcarmona
Copy link
Author

thank you for your quick answer, which way is better? (I implemented my own because I didn't find your alternative in the doc or in tests, is there any reason to not include it?)
regards!

@felixcarmona
Copy link
Author

I'm doing some tests:
with the decorator's way, the returned object is an instance of "function" and not of the object, so, you cannot be abled to use it in some builtins like isinstance(a, A)
With my first proposal, it works:

from lazy_object_proxy import Proxy


class LazyObject:
    def __new__(cls, *args, **kwargs):
        class_ = type(object.__name__, (cls,), {})
        class_.__new__ = lambda cls_, *args_, **kwargs_: object.__new__(cls_)
        return Proxy(lambda: class_(*args, **kwargs))

class B(LazyObject):
    def __init__(self, x):
        print("Init")
        self.x = x

b = B(5)
print('GO')
print(b.x)
print(isinstance(b, B))

returns:

GO
Init
5
True

and

from lazy_object_proxy import Proxy


def lazy(klass):
    def lazy_class(*args, **kwargs):
        return Proxy(lambda: klass(*args, **kwargs))
    lazy_class.__name__ = "lazy_" + klass.__name__
    return lazy_class

@lazy
class A:
    def __init__(self, x):
        print("Init")
        self.x = x

a = A(5)
print('GO')
print(a.x)
print(isinstance(a, A))

returns:

Traceback (most recent call last):
GO
  File "test.py", line 19, in <module>
Init
    print(isinstance(a, A))
5
TypeError: isinstance() arg 2 must be a type or tuple of types

@ionelmc
Copy link
Owner

ionelmc commented Sep 27, 2015

You're right. I suppose we could have a metaclass that roughly does what you do in your example in a lazy_object_proxy.contrib module (as opposed to just giving the complicated example in the readme).

@felixcarmona
Copy link
Author

Sounds good, thanks for your time, it's great to have contributors like you.
You can close this issue if you want,
Regards

edit: btw:
You can still using the decorator:

from lazy_object_proxy import Proxy


def lazy(cls):
    class Lazy:
        def __new__(cls, *args, **kwargs):
            original_class = type(object.__name__, (cls,), {})
            original_class.__new__ = lambda cls_, *args_, **kwargs_: object.__new__(cls_)
            return Proxy(lambda: original_class(*args, **kwargs))
    lazy_class = type(cls.__name__, (cls, Lazy), {})
    return lazy_class


@lazy
class B:
    def __init__(self, x):
        print("Init")
        self.x = x

b = B(5)
print('GO')
print(b.x)
print(isinstance(b, B))

output:

GO
Init
5
True

bebleo added a commit to bebleo/smtpdfix that referenced this issue Dec 31, 2020
Fixes #22

In order to enable environment variables to be mocked using either
monkeypatch or mock.patch we enable lazy initialization of the
AuthController class.

- Adds a lazy decorator and applies it to the AuthController following
  ionelmc/python-lazy-object-proxy#11
- Favour yield over request.add_finalizer for clean up.
- Adds tests
bebleo added a commit to bebleo/smtpdfix that referenced this issue Dec 31, 2020
Fixes #22

In order to enable environment variables to be mocked using either
monkeypatch or mock.patch we enable lazy initialization of the
AuthController class.

- Adds a lazy decorator and applies it to the AuthController following
  ionelmc/python-lazy-object-proxy#11
- Favour yield over request.add_finalizer for clean up.
- Adds tests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants