Automatically create (and keep) an object when accessed - python

I would like to do something like this:
class A:
def hello(): print "Hello"
# I do not want to explicitly setup a:
a = A()
# a = A() -> I want this to happen automatically when I access a
# My first try is this:
def a():
return A()
# Also, I do not want to call a as a function a(): it must be an object
# And it must stay alive and initialized
a.hello() # a is created, as object of class A
a.hello() # I do not want a second instantiation
How can I implement this? properties? cached-properties? They are only for classes: a is a module-level object.

Maybe something like this:
class A(object):
def hello(self):
print "Hello"
class LazyA(object):
def __init__(self):
self.instance = None
def __getattr__(self, k):
if self.instance is None:
self.instance = A()
return getattr(self.instance, k)
a = LazyA()

def lazyinit(cls):
class p(object):
def __init__(self, *args, **kws):
self._init = lambda: cls(*args, **kws)
self._obj = None
def __getattr__(self, k):
if not self._obj:
self._obj = self._init()
return getattr(self._obj, k)
return p
Example:
#lazyinit
class A(object):
def __init__(self, a, b):
print("initializing...")
self.x = a + b + 2
def foo(self):
return self.x
x = A(39, 1)
print x
print x.foo()
print x.foo()

Generalization of the answer by Pavel:
class LazyClass(object):
def __init__(self, myclass, *args, **kwargs):
self.instance = None
self.myclass = myclass
self.args = args
self.kwargs = kwargs
def __getattr__(self, k):
if self.instance is None:
self.instance = self.myclass(*self.args, **self.kwargs)
return getattr(self.instance, k)
class A(object):
def __init__ (self, name):
self.name = name
print "Created"
def hello(self):
print "Hello " + self.name
import unittest
class TestLazyClass(unittest.TestCase):
def setUp(self):
self.a = LazyClass(A, 'Daniel')
def test_it(self):
self.a.hello()
self.a.hello()

Related

Overriding parent's attributes with child's class attributes

I have a scheme of cooperative classes based on collection.abc. When I subclass them, I want to be able to define just a couple of class attributes that then become the default values at instantiation, like so:
class MyFancyClass:
# Defines various attributes, as class attributes and/or in the
# __init__ method
def __init__(self, a=1, b=1):
self.a = a
self.b = b
class A(myFancyClass):
# Instances of A should have these values, even if they override
# a value set in MyFancyClass's __init__ method:
a = 2
b = 2
c = SomeHelperClass
Currently, in the __init__ of FancyClass, I do:
def __init__(self, *args, **kwargs):
for k, v in vars(type(self)).items():
if k.startswith("_"):
continue
if k not in kwargs:
kwargs[k] = v
super().__init__(*args, **kwargs)
That works fine, but if I make a class B that is a subclass of A, I lose those values defined for A, and I want to keep them.
So playing around, I got stuck here...
class InitExtras:
def __init__(self, *args, **kwargs):
for cls in type(self).__mro__:
if cls == InitExtras:
break
for k, v in vars(cls).items():
if k.startswith("_") or callable(v):
continue
if k not in kwargs:
print(f"adding\n{k=}\n{v=}\n")
kwargs[k] = v
super().__init__(*args, **kwargs)
class Base:
def __init__(self, *args, **kwargs):
print(f"{args = }")
print(f"{kwargs = }")
class A(Base):
def fun1(self):
pass
class B(A):
def fun2(self):
pass
#property
def b(self):
return self._b
#b.setter
def b(self, value):
self._b = value
def __init__(self, *args, b=23, b2=32, **kwargs):
super().__init__(*args, **kwargs)
self.b = b
self.b2 = b2
class C(InitExtras, B):
b = 42
class D(C):
b2 = 420
class T:
pass
class E(C):
b2 = T
def fun3(self):
pass
This seem to do most of what I want, except that E().b2 is 32, not T. And if I remove the callable() filter, other stuff can get mixed in too, like extra functionalities one might define later to personalize classes even further if needed (fun3 in the example). I don't want to need to do a new __init__ each time.
So my question is, how to accomplish that?
I could solve it, I did by making a metaclass, and to distinguish between different class attributes I limit it to just properties
abc_recipes.py
from abc import ABCMeta, ABC, abstractmethod
class PropertyConfigMeta(ABCMeta):
def __new__(mcls, name, bases, namespace, /, **kwargs):
#list the properties that the new class would inherit
properties = {p for bcls in bases
for cls in bcls.__mro__
for p,v in vars(cls).items()
if isinstance(v,property)
}
#proceed to extract the attributes that would
#overwrite the properties inherited by non-property
new_default={}
new_namespace = {}
for k,v in namespace.items():
if k in properties:
if isinstance(v,property):
new_namespace[k] = v
else:
new_default[k] = v
else:
new_namespace[k] = v
cls = super().__new__(mcls, name, bases, new_namespace, **kwargs)
if hasattr(cls,"_new_default"):
cls._new_default = {**cls._new_default, **new_default}
else:
cls._new_default = new_default
return cls
class PropertyConfig(metaclass=PropertyConfigMeta):
"""cooperative class that transform
class A(SomeClass):
a = 1
b = 2
into
class A(SomeClass):
def __init__(self, *arg, a = 1, b = 2, **karg):
super().__init__(*arg, a = a, b = b, **karg)
so long as a and b are defined as properties in SomeClass
(or somewhere in the inheritance chain)
class SomeClass:
#property
def a(self):
...
#property
def b(self):
...
Use as
class A(PropertyConfig, SomeClass):
a = 1
b = 2
"""
def __init__(self,*arg,**kwargs):
for k,v in self._new_default.items():
if k not in kwargs:
kwargs[k]=v
super().__init__(*arg,**kwargs)
class ConfigClass(ABC):
"""Cooperative class that offer a default __repr__ method
based on the abstract property .config"""
#property
#abstractmethod
def config(self) -> dict:
"""configuration of this class"""
return {}
def __repr__(self):
return f"{type(self).__name__}({', '.join( f'{k}={v!r}' for k,v in self.config.items() )})"
sample use
import abc_recipes
class Base:
def __init__(self,*arg,**karg):
if arg:
print(f"{arg=}")
if karg:
print(f"{karg=}")
class A(Base):
pass
class B(abc_recipes.ConfigClass,A):
def __init__(self,*a, b=23, b2=32, **k):
super().__init__(*a,**k)
self.b = b
self.b2 = b2
#property
def b(self):
"b attribute"
#print("b getter")
return self._b
#b.setter
def b(self,v):
#print("b setter")
self._b=v
#property
def b2(self):
"b2 atrribute"
#print("b2 getter")
return self._b2
#b2.setter
def b2(self,v):
#print("b2 setter")
self._b2=v
#property
def config(self) -> dict:
"""configuration of this class"""
res = super().config
res.update(b=self.b, b2=self.b2)
return res
class C(abc_recipes.PropertyConfig,B):
b=42
pass
class D(C):
b2=420
pass
class T:
pass
class E(C):
b2 = T
pi = 3.14
class F(E):
#property
def b2(self):
#print("rewriten b2 getter")
return "rewriten b2"
#b2.setter
def b2(self, value):
#print("rewriten b2 setter")
pass
test
>>> F()
F(b=42, b2='rewriten b2')
>>> E()
E(b=42, b2=<class '__main__.T'>)
>>> D()
D(b=42, b2=420)
>>> C()
C(b=42, b2=32)
>>> B()
B(b=23, b2=32)
>>> e=E()
>>> e.pi
3.14
>>> f=F()
>>> f.pi
3.14
>>>

Pyro4: Call method on remote sub-object without it trying to return the sub-object

Let's say I have an object that is a property of another object.
import Pyro4
#Pyro4.expose
class ClassB:
def foo(self):
return 'Hello from ClassB::foo()'
#Pyro4.expose
class ClassA:
def __init__(self):
self._b = ClassB()
def foo(self):
return 'Hello from ClassA::foo()'
#property
def b(self):
return self._b
if __name__ == '__main__':
daemon = Pyro4.Daemon(host='localhost')
ns = Pyro4.locateNS(host='localhost', port=9090)
ns.register('ClassA', daemon.register(ClassA))
daemon.requestLoop()
And on the local machine, I run this
import Pyro4
if __name__ == '__main__':
ns = Pyro4.locateNS(host='localhost', port=9090)
uri = ns.lookup('ClassA')
a = Pyro4.Proxy(uri)
print(a.foo())
print(a.b.foo()) # Fails here
When I try to invoke a.b.foo, it's trying to serialize a.b and invoke foo on it locally, but I want to invoke foo on the instance of ClassB that already exists on the remote ClassA instance.
Of course, I could add a method to ClassA that delegates to b.foo(), e.g.
def classA:
# ...
def foo_on_b(self):
return self._b.foo()
But I'd rather not do that.
This is not possible, by design. It's a security vulnerability.
https://pyro4.readthedocs.io/en/stable/security.html#dotted-names-object-traversal
Managed to hack it like this.
remote.py
import Pyro4
class C:
def __init__(self):
self.val = 123
class B:
def __init__(self):
self.c = C()
class A:
def __init__(self):
self.b = B()
def getattr_r(obj, *attrs):
return getattr_r(getattr(obj, attrs[0]), *attrs[1:]) if attrs else obj
class ClassC:
def foo(self, x, y):
return 'Hello from ClassC::foo()! {} + {} = {}'.format(x, y, x + y)
class ClassB:
def __init__(self):
self.c = ClassC()
def foo(self, x, y):
return 'Hello from ClassB::foo()! {} + {} = {}'.format(x, y, x + y)
#Pyro4.expose
class ClassA:
def __init__(self):
self.b = ClassB()
def foo(self, x, y):
return 'Hello from ClassA::foo()! {} + {} = {}'.format(x, y, x + y)
def invoke_on_subobj(self, obj_chain, *args, **kwargs):
return getattr_r(self, *obj_chain)(*args, **kwargs)
if __name__ == '__main__':
daemon = Pyro4.Daemon(host='localhost')
ns = Pyro4.locateNS(host='localhost', port=9090)
ns.register('ClassA', daemon.register(ClassA))
daemon.requestLoop()
local.py
import Pyro4
class ObjTraversableProxy:
def __init__(self, proxy, bound_attrs=[]):
self._proxy = proxy
self._bound_attrs = bound_attrs
def __getattr__(self, attr):
return ObjTraversableProxy(self._proxy, self._bound_attrs + [attr])
def __call__(self, *args, **kwargs):
if len(self._bound_attrs) > 1:
return self._proxy.invoke_on_subobj(self._bound_attrs, *args, **kwargs)
else:
return getattr(self._proxy, self._bound_attrs[0])(*args, **kwargs)
if __name__ == '__main__':
ns = Pyro4.locateNS(host='localhost', port=9090)
uri = ns.lookup('ClassA')
a = ObjTraversableProxy(Pyro4.Proxy(uri))
print(a.foo(3, 4))
print(a.b.foo(5, 6))
print(a.b.c.foo(6, 7))
Result
Hello from ClassA::foo()! 3 + 4 = 7
Hello from ClassB::foo()! 5 + 6 = 11
Hello from ClassC::foo()! 6 + 7 = 13

multiple functions in Python

We have some variable, or other instance: a='?'.
We have such input:
f = a(3112).bas(443).ssad(34)
When we type
print(f)
Output should be:
3112a-443bas-34ssad
I've tried some ways to solve this and have found information about chaining, but I still have the problem. I can't return class name to the brginning of the string.
This, what I have:
class A():
def __getattribute__(self, item):
print (str(item))
return super(A, self).__getattribute__(item)
def __init__(self, x):
self.x = x
print (str(x))
def b(self, item):
print (str(item))
return self
def c(self, item):
print (str(item))
return self
def d(self, item):
print (str(item))
return self
A(100).b(200).c(300).d(400)
My output:
100
b
200
c
300
d
400
But I couldn't concatenate it in one string.
Dynamic way
class A(object):
def __init__(self, integer):
self._strings = ['{}a'.format(integer)]
def __getattr__(self, attrname, *args):
def wrapper(*args, **kwargs):
self._strings.append('{}{}'.format(args[0], attrname))
return self
return wrapper
def __str__(self):
return '-'.join(self._strings)
print(A(100).bas(200).ssad(300))
Output
100a-200bas-300ssad
But also
print(A(100).egg(200).bacon(300).SPAM(1000))
Output
100a-200egg-300bacon-1000SPAM
Static way
class A(object):
def __init__(self, integer):
self._strings = ['{}a'.format(integer)]
def bas(self, integer):
self._strings.append('{}bas'.format(integer))
return self
def ssad(self, integer):
self._strings.append('{}ssad'.format(integer))
return self
def __str__(self):
return '-'.join(self._strings)
print(A(100).b(200).c(300))
Output
100a-200bas-300ssad
More about __str__
You can override the __str__ method to define your specific output:
class A():
def __init__(self, a, b="", c="", d=""):
self._a = a
self._b = b
self._c = c
self._d = d
def __str__(self):
return '{}a-{}b-{}c-{}d'.format( self.a, self.b, self.c, self.d )
def b(self, item):
self._b = item
return self
def c(self, item):
self._c = item
return self
def d(self, item):
self._d = item
return self
f = A(100).b(200).c(300).d(400)
print(f) # 100a-200b-300c-400d
Here I tried it in another way , ie, If you want to take the function name instead of manually giving it you can use inspect in python. Try this code :
import inspect
class A():
l = []
def __init__(self, x):
self.x = x
print (str(x))
self.l.append(str(x) + "a")
def b(self, item):
print (str(item))
self.l.append(str(item) + inspect.stack()[0][3])
return self
def c(self, item):
print (str(item))
self.l.append(str(item) + inspect.stack()[0][3])
return self
def d(self, item):
print (str(item))
self.l.append(str(item) + inspect.stack()[0][3])
return self
print("-".join(A(100).b(200).c(300).d(400).l))
The o/p is like :
'100a-200b-300c-400d'

Decorate a class in Python by defining the decorator as a class

What is a simple example of decorating a class by defining the decorator as a class?
I'm trying to achieve what has been implemented in Python 2.6 using PEP 3129 except using classes not functions as Bruce Eckel explains here.
The following works:
class Decorator(object):
def __init__(self, arg):
self.arg = arg
def __call__(self, cls):
def wrappedClass(*args):
return cls(*args)
return type("TestClass", (cls,), dict(newMethod=self.newMethod, classattr=self.arg))
def newMethod(self, value):
return value * 2
#Decorator("decorated class")
class TestClass(object):
def __init__(self):
self.name = "TestClass"
print "init %s"%self.name
def TestMethodInTestClass(self):
print "test method in test class"
def newMethod(self, value):
return value * 3
Except, in the above, wrappedClass is not a class, but a function manipulated to return a class type. I would like to write the same callable as follows:
def __call__(self, cls):
class wrappedClass(cls):
def __init__(self):
... some code here ...
return wrappedClass
How would this be done?
I'm not entirely sure what goes into """... some code here ..."""
If you want to overwrite new_method(), just do it:
class Decorator(object):
def __init__(self, arg):
self.arg = arg
def __call__(self, cls):
class Wrapped(cls):
classattr = self.arg
def new_method(self, value):
return value * 2
return Wrapped
#Decorator("decorated class")
class TestClass(object):
def new_method(self, value):
return value * 3
If you don't want to alter __init__(), you don't need to overwrite it.
After this, the class NormalClass becomes a ClassWrapper instance:
def decorator(decor_arg):
class ClassWrapper:
def __init__(self, cls):
self.other_class = cls
def __call__(self,*cls_ars):
other = self.other_class(*cls_ars)
other.field += decor_arg
return other
return ClassWrapper
#decorator(" is now decorated.")
class NormalClass:
def __init__(self, name):
self.field = name
def __repr__(self):
return str(self.field)
Test:
if __name__ == "__main__":
A = NormalClass('A');
B = NormalClass('B');
print A
print B
print NormalClass.__class__
Output:
A is now decorated. <br>
B is now decorated. <br>
\__main__.classWrapper

How to intercept instance method calls?

I am looking for a way to intercept instance method calls in class MyWrapper below:
class SomeClass1:
def a1(self):
self.internal_z()
return "a1"
def a2(self):
return "a2"
def internal_z(self):
return "z"
class SomeClass2(SomeClass1):
pass
class MyWrapper(SomeClass2):
# def INTERCEPT_ALL_FUNCTION_CALLS():
# result = Call_Original_Function()
# self.str += result
# return result
def __init__(self):
self.str = ''
def getFinalResult(self):
return self.str
x = MyWrapper()
x.a1()
x.a2()
I want to intercept all function calls make through my wrapper class. In my wrapper class I want to keep track of all the result strings.
result = x.getFinalResult()
print result == 'a1a2'
Some quick and dirty code:
class Wrapper:
def __init__(self, obj):
self.obj = obj
self.callable_results = []
def __getattr__(self, attr):
print("Getting {0}.{1}".format(type(self.obj).__name__, attr))
ret = getattr(self.obj, attr)
if hasattr(ret, "__call__"):
return self.FunctionWrapper(self, ret)
return ret
class FunctionWrapper:
def __init__(self, parent, callable):
self.parent = parent
self.callable = callable
def __call__(self, *args, **kwargs):
print("Calling {0}.{1}".format(
type(self.parent.obj).__name__, self.callable.__name__))
ret = self.callable(*args, **kwargs)
self.parent.callable_results.append(ret)
return ret
class A:
def __init__(self, val): self.val = val
def getval(self): return self.val
w = Wrapper(A(10))
print(w.val)
w.getval()
print(w.callable_results)
Might not be thorough, but could be a decent starting point, I guess.
You could wrap your methods with decorators a instanciation time:
#!/usr/bin/env python
import inspect
def log(func):
def _logged(*args, **kw):
print "[LD] func", func.__name__, "called with:", args, kw
result = func(*args, **kw)
print "[LD] func", func.__name__, "returned:", result
return result
return _logged
class A(object):
def __init__(self):
for x in inspect.getmembers(self, (inspect.ismethod)):
if not x[0].startswith('__'):
setattr(self, x[0], log(getattr(self, x[0])))
def hello(self):
print "Hello"
def bye(self):
print "Bye"
return 0
Now if you call hello or bye, the call goes through log first:
a = A()
a.hello()
a.bye()
# [LD] func hello called with: () {}
# Hello
# [LD] func hello returned: None
# [LD] func bye called with: () {}
# Bye
# [LD] func bye returned: 0
What you want to do is quite similar to this question.
You should take your example code in the reverse order, i mean creating a class to record return values of method calls, and make the classes you want to watch inherit from it.
Which would give something like this
class RetValWatcher(object):
def __init__(self):
self.retvals = []
def __getattribute__(self, name):
attr = super(RetValWatcher, self).__getattribute__(name)
if callable(attr):
def wrapped(*args, **kwargs):
retval = attr(*args, **kwargs)
self.retvals.append(retval)
return retval
return wrapped
else:
return attr
def getFinalResult(self):
return ''.join(self.retvals)
class MyClass(RetValWatcher):
def a(self):
self.internal_z()
return 'a1'
def b(self):
return 'b1'
def internal_z(self):
return 'z'
x = MyClass()
x.a()
x.b()
print x.getFinalResult()
#'za1b1'
With some minor changes, this method would also allow you to record return values across all RetValWatcher instances.
Edit: added changes suggested by singularity's comment
Edit2: forgot to handle the case where attr is not a method (thx singularity again)
Edit3: fixed typo

Categories