Recursion error in python setattr [duplicate] - python

I want to define a class containing read and write methods, which can be called as follows:
instance.read
instance.write
instance.device.read
instance.device.write
To not use interlaced classes, my idea was to overwrite the __getattr__ and __setattr__ methods and to check, if the given name is device to redirect the return to self. But I encountered a problem giving infinite recursions. The example code is as follows:
class MyTest(object):
def __init__(self, x):
self.x = x
def __setattr__(self, name, value):
if name=="device":
print "device test"
else:
setattr(self, name, value)
test = MyTest(1)
As in __init__ the code tried to create a new attribute x, it calls __setattr__, which again calls __setattr__ and so on. How do I need to change this code, that, in this case, a new attribute x of self is created, holding the value 1?
Or is there any better way to handle calls like instance.device.read to be 'mapped' to instance.read?
As there are always questions about the why: I need to create abstractions of xmlrpc calls, for which very easy methods like myxmlrpc.instance,device.read and similar can be created. I need to 'mock' this up to mimic such multi-dot-method calls.

You must call the parent class __setattr__ method:
class MyTest(object):
def __init__(self, x):
self.x = x
def __setattr__(self, name, value):
if name=="device":
print "device test"
else:
super(MyTest, self).__setattr__(name, value)
# in python3+ you can omit the arguments to super:
#super().__setattr__(name, value)
Regarding the best-practice, since you plan to use this via xml-rpc I think this is probably better done inside the _dispatch method.
A quick and dirty way is to simply do:
class My(object):
def __init__(self):
self.device = self

Or you can modify self.__dict__ from inside __setattr__():
class SomeClass(object):
def __setattr__(self, name, value):
print(name, value)
self.__dict__[name] = value
def __init__(self, attr1, attr2):
self.attr1 = attr1
self.attr2 = attr2
sc = SomeClass(attr1=1, attr2=2)
sc.attr1 = 3

You can also use object.
class TestClass:
def __init__(self):
self.data = 'data'
def __setattr__(self, name, value):
print("Attempt to edit the attribute %s" %(name))
object.__setattr__(self, name, value)

or you can just use #property:
class MyTest(object):
def __init__(self, x):
self.x = x
#property
def device(self):
return self

If you don't want to specify which attributes can or cannot be set, you can split the class to delay the get/set hooks until after initialization:
class MyTest(object):
def __init__(self, x):
self.x = x
self.__class__ = _MyTestWithHooks
class _MyTestWithHooks(MyTest):
def __setattr__(self, name, value):
...
def __getattr__(self, name):
...
if __name__ == '__main__':
a = MyTest(12)
...
As noted in the code you'll want to instantiate MyTest, since instantiating _MyTestWithHooks will result in the same infinite recursion problem as before.

Related

how to access outer class properties inside the inner classes?

class Remote:
aa=7
def __init__(self):
self.name="Lenovo"
self.b=self.Battery()
print("this is outer",self.b.t)
class Battery:
def __init__(self):
self.name="Hp"
self.t="df"
self.c=self.Cover()
class Cover:
def __init__(self):
self.name="Arplastic"
c1=Remote()
I knew today about inner class but i don't know how to i access properties and methods of outer class into inner class please let me know anyone.
Change the constructor(s) of the inner class(es) to accept a parent argument and have the creating instance pass itself to it:
class Remote:
aa=7
def __init__(self):
self.name="Lenovo"
self.b=self.Battery(self)
print("this is outer",self.b.t)
class Battery:
def __init__(self,parent):
self.name="Hp"
self.t="df"
self.c=self.Cover(self)
self.parent=parent
class Cover:
def __init__(self,parent):
self.name="Arplastic"
self.parent=parent
c1=Remote()
print(c1.b.c.parent.parent.name) # prints 'Lenovo'
One approach is to make a metaclass that automatically creates self.parent attributes for nested classes. Note that there is a trade-off between readability and boilerplate here - many programmers would rather you just manually pass parents as arguments and add them to __init__ methods. This is more fun though, and there is something to be said for having less cluttered code.
Here is the code:
import inspect
def inner_class(cls):
cls.__is_inner_class__ = True
return cls
class NestedClass(type):
def __new__(metacls, name, bases, attrs, parent=None):
attrs = dict(attrs.items())
super_getattribute = attrs.get('__getattribute__', object.__getattribute__)
inner_class_cache = {}
def __getattribute__(self, attr):
val = super_getattribute(self, attr)
if inspect.isclass(val) and getattr(val, '__is_inner_class__', False):
if (self, val) not in inner_class_cache:
inner_class_cache[self, val] = NestedClass(val.__name__, val.__bases__, val.__dict__, parent=self)
return inner_class_cache[self, val]
else:
return val
attrs['__getattribute__'] = __getattribute__
attrs['parent'] = parent
return type(name, bases, attrs)
class Remote(metaclass=NestedClass):
aa = 7
def __init__(self):
self.name = "Lenovo"
self.b = self.Battery()
print("this is outer", self.b.t)
#inner_class
class Battery:
def __init__(self):
self.name = "Hp"
self.t = "df"
self.c = self.Cover()
#inner_class
class Cover:
def __init__(self):
self.name = "Arplastic"
print(f'{self.parent=}, {self.parent.parent=}')
c1 = Remote()
print(f'{c1.b.c.parent.parent is c1=}')
print(f'{isinstance(c1.b, c1.Battery)=}')
Output:
self.parent=<__main__.Battery object at 0x7f11e74936a0>, self.parent.parent=<__main__.Remote object at 0x7f11e7493730>
this is outer df
c1.b.c.parent.parent is c1=True
isinstance(c1.b, c1.Battery)=True
The way this works is by storing the parent as a class attribute (which is None by default), and replacing the __getattribute__ method so that all inner classes are replaced with NestedClasses with the parent attribute correctly filled in.
The inner_class decorator is used to mark a class as an inner class by setting the __is_inner_class__ attribute.
def inner_class(cls):
cls.__is_inner_class__ = True
return cls
This is not strictly necessary if all attributes that are classes should be treated as inner classes, but it's good practice to do something like this to prevent Bar.foo being treated as an inner class in this example:
class Foo:
pass
class Bar(metaclass=NestedClass):
foo = Foo
All the NestedClass metaclass does is take the description of the class and modify it, adding the parent attribute:
class NestedClass(type):
def __new__(metacls, name, bases, attrs, parent=None):
attrs = dict(attrs.items())
...
attrs['parent'] = parent
return type(name, bases, attrs)
...and modifying the __getattribute__ method. The __getattribute__ method is a special method that gets called every time an attribute is accessed. For example:
class Foo:
def __init__(self):
self.bar = "baz"
def __getattribute__(self, item):
return 1
foo = Foo()
# these assert statements pass because even though `foo.bar` is set to "baz" and `foo.remote` doesn't exist, accessing either of them is the same as calling `Foo.__getattribute(foo, ...)`
assert foo.bar == 1
assert foo.remote == 1
So, by modifying the __getattribute__ method, you can make accessing self.Battery return a class that has its parent attribute equal to self, and also make it into a nested class:
class NestedClass(type):
def __new__(metacls, name, bases, attrs, parent=None):
attrs = dict(attrs.items())
# get the previous __getattribute__ in case it was not the default one
super_getattribute = attrs.get('__getattribute__', object.__getattribute__)
inner_class_cache = {}
def __getattribute__(self, attr):
# get the attribute
val = super_getattribute(self, attr)
if inspect.isclass(val) and getattr(val, '__is_inner_class__', False):
# if it is an inner class, then make a new version of it using the NestedClass metaclass, setting the parent attribute
if (self, val) not in inner_class_cache:
inner_class_cache[self, val] = NestedClass(val.__name__, val.__bases__, val.__dict__, parent=self)
return inner_class_cache[self, val]
else:
return val
attrs['__getattribute__'] = __getattribute__
attrs['parent'] = parent
return type(name, bases, attrs)
Note that a cache is used to ensure that self.Battery will always return the same object every time rather than re-making the class every time it is called. This ensures that checks like isinstance(c1.b, c1.Battery) work correctly, since otherwise c1.Battery would return a different object to the one used to create c1.b, causing this to return False, when it should return True.
And that's it! You can now enjoy nested classes without boilerplate!

Can I change the default behavior of my parent class method in my subclass method?

I'm learning simple python inheritance and I want that one of my parent class method default argument is changed conditionally to one of my subclass argument value, and I don't know if this is possible.
Here is an example of what I'd like to do:
class Parent(object):
def __init__(self, name):
self.name = name
def doSomething(self, name, strict = True):
if strict:
return self.name
else:
return name
class Child(Parent):
def __init__(self, name, **kwargs):
super(Child, self).__init__(name)
if 'changeBehavior' in kwargs:
# Here is the thing:
# Can I change the default value of strict to kwargs['changeBehavior']
# in a way that when I later call doSomething(), it will behave according
# to its new default behavior?
def doSomething(self, name, strict = kwargs['changeBehavior']):
super(Child, self).doSomething(strict = kwargs['changeBehavior'])
If this can be done in this way, is there any other method to do so?
Thanks
You can use partial.
from functools import partial
class Parent(object):
def __init__(self, name):
self.name = name
def doSomething(self, name, strict=True):
print('Got strict={}'.format(strict))
if strict:
return self.name
else:
return name
class Child(Parent):
def __init__(self, name, **kwargs):
super().__init__(name)
change_behavior = kwargs.get('changeBehavior')
if change_behavior is not None:
self.doSomething = partial(self.doSomething, strict=change_behavior)
p = Parent('name')
c = Child('name', changeBehavior=False)
p.doSomething('name')
c.doSomething('name')
outputs
Got strict=True
Got strict=False

Does __dict__ exist or not in Python?

The first code snippet:
class A:
def __init__(self):
print(self.__dict__)
def __getattr__(self, name):
print("get")
def __setattr__(self, name, value):
print("set")
# def __getattribute__(self, name):
# print("getatrr")
a = A()
It prints {} and the function __getattr__ isn't invoked, which means the attribute__dict__ exists.
The second snippet:
class A:
def __init__(self):
print(self.__dict__)
def __getattr__(self, name):
print("get")
def __setattr__(self, name, value):
print("set")
def __getattribute__(self, name):
print("getatrr")
a = A()
It prints getatrr and None, which means the attribute __dict__ doesn't exist.
Why is __dict__ {} in the first case, but None in the second case?
the issue is that when you define this:
def __getattribute__(self, name):
print("getatrr")
you're overriding __getattribute__ which is supposed to return something. Since you're not returning anything, you get None for every attribute you'll try.
Documentation states:
This method should return the (computed) attribute value or raise an AttributeError exception
A viable way to define it is to call object.__getattribute__ in the fallback case (in my example, I have added a small test on __dict__ which prints:
def __getattribute__(self, name):
if name == "__dict__":
print("get attribute invoked with __dict__")
return object.__getattribute__(self,name)
In the end, the hard attribute lookup work is done with object.__getattribute__ that invokes python runtime.

How can I keep track of object instances in a list with python without using constructors (__init__)?

I want to keep track of object instances of a given class/subclasses in Python given my particular framework I cannot use constructors. As an alternative I could use either decorators or Singletons objects.
Are there any other alternatives except using global variables ?
You could use a descriptor. That is, it's an object that can and will keep track of what things are where with the caveat that it will only do so if you've assigned a value to do it. Here's an example:
class RevealAccess(object):
"""
A data descriptor that sets and returns values
normally and prints a message logging their access.
"""
def __init__(self, initval=None, name='var'):
self.val = initval
self.name = name
def __get__(self, obj, objtype):
print 'Retrieving', self.name
return self.val
def __set__(self, obj, val):
print 'Updating', self.name
self.val = val
This is taken out from the documentation. However, add the following from this SO Q&A:
_instances = []
def __new__(cls, *args, **kw):
instance = object.__new__(cls)
cls._instances.append(instance)
return instance
#classmethod
def get_instances(cls):
return self._instances
and you've achieved your goals:
class Foo(object):
desc = MyDesc('yo')
foo = Foo()
Foo.get_instances().length

Can I refactor this simple callback pattern that uses the property decorator?

I'm just getting to grips with decorators in Python and using them to add callbacks to some instance variables using the following simple pattern:
class A(object):
def __init__(self):
self._var = 0
self.var_callbacks = []
#property
def var(self):
return self._var
#var.setter
def var(self, x):
self._var = x
for f in self.var_callbacks:
f(x)
The property decorator is a neat way of allowing me to introduce callbacks where necessary without changing the class interface. However, after the third or fourth variable it's making the code a bit repetitive.
Is there a way to refactor this pattern into something along the following:
class A(object):
def __init__(self):
self.var = 0
enable_callback(self, 'var', 'var_callbacks')
You'll need to set the property on the class (since it is a descriptor), so using a enable_callback call in the initializer is not going to work.
You could use a class decorator to set the properties from a pattern:
def callback_properties(callbacks_attribute, *names):
def create_callback_property(name):
def getter(self):
return getattr(self, '_' + name)
def setter(self, value):
setattr(self, '_' + name, value)
for f in getattr(self, callbacks_attribute):
f(value)
return property(getter, setter)
def add_callback_properties(cls):
for name in names:
setattr(cls, name, create_callback_property(name)
return cls
return add_callback_properties
Then use that as:
#add_callback_properties('var_callbacks', 'var1', 'var2')
class A(object):
# everything else
Have a look at the Python descriptor protocol. In essence, you can define a class that handles the getting, setting and deleting of a property. So you could define a descriptor that runs your callbacks on setting the attribute.
Descriptors are regular classes, and can be parameterized. So you could implement a descriptor that takes the destination variable its constructor. Something like the following:
class A(object):
var = CallbackDescriptor('var')
foo = CallbackDescriptor('foo')

Categories