how to use instance attribute to define a class attribute? - python

I'm trying to use the name in my init for my class attribute, attr but it seems that's impossible.
here's the code:
class B:
def __init__(self, val):
self.val = val
def __get__(self, instance, owner):
return owner.valEditNew(self.val)
def __set__(self, instance, value):
return
class A:
def __init__(self, name = 'def_name'):
self.name = name
attr = B('G120')
def valEditNew(val):
val += ' #edited'
return val
a = A('JJ')
print(a.attr)
that's it if i use self.name or name or ... in place of G120>>>
builtins.NameError: name 'self' is not defined
if that's not possible, can you show me the way?

If you want to access attribute of the instance that contains the descriptor object, use instance parameter of __get__ / __set__:
class B:
def __get__(self, instance, owner):
return instance.valEditNew(instance.name) # <---
def __set__(self, instance, value):
return
class A:
attr = B()
def __init__(self, name='def_name'):
self.name = name
def valEditNew(self, val):
val += ' #edited'
return val
a = A('JJ')
print(a.attr)
# prints => JJ #edited

Related

Hijacking the getattr and setattr functions after __init__ completes

I need to intercept setattr and getattr after init completion, i.e. if main class doesn't have required attribute, it would look for it in subclass Extra, or when setting attribute, if it's not in main class then setting went to subclass Extra, how to understand that init was executed and intercept it only after completion? Here's the code I tried to do it with but it didn't work
class Test:
def __init__(self):
self.default_name = "Michael"
def __setattr__(self, key, value):
if not hasattr(self, key):
self.Extra.__dict__[key] = value;
self.__dict__[key] = v
def __getattr__(self, item):
if not hasattr(self, item):
return self.Extra.__dict__[item]
class Extra:
pass
user = Test()
user.default_name = "Tomas"
user.some_data = "test"
print(user.default_name)
print(user.some_data)
Direct operation attribute dictionary:
class Test:
def __init__(self):
vars(self)['default_name'] = "Michael"
vars(self)['extra'] = Test.Extra()
def __setattr__(self, key, value):
if key not in vars(self):
setattr(self.extra, key, value)
else:
vars(self)[key] = value
def __getattr__(self, item):
return getattr(self.extra, item)
class Extra:
pass
Test:
>>> user = Test()
>>> user.default_name
'Michael'
>>> user.default_name = 'Tomas'
>>> user.default_name
'Tomas'
>>> user.some_data = 'test'
>>> user.some_data
'test'
>>> vars(user)
{'default_name': 'Tomas', 'extra': <__main__.Test.Extra object at 0x000001D5151D6380>}

understanding a python descriptors example (TypedProperty)

Here is a slightly modified version of some code found in a python book:
class TypedProperty(object):
def __init__(self,name,type,default=None):
self.name = "_" + name
self.type = type
self.default = default if default else type()
def __get__(self,instance,cls):
return getattr(instance,self.name,self.default)
def __set__(self,instance,value):
if not isinstance(value,self.type):
raise TypeError("Must be a %s" % self.type)
setattr(instance,self.name,value)
class Foo(object):
name = TypedProperty("name",str)
num = TypedProperty("num",int,42)
f = Foo()
f.name = 'blah'
My question: why are we creating attributes in f? In the code above, TypedProperty is written such that f.name = 'blah' creates the attribute "_name" in the instance f.
Why not save the values as attributes of the class TypedProperty? Here is what I had in mind:
class TypedProperty2(object):
def __init__(self, val, typ):
if not isinstance(val, typ):
raise TypeError()
self.value = val
self.typ = typ
def __get__(self, instance, owner):
return self.value
def __set__(self, instance, val):
if not isinstance(val, self.typ):
raise TypeError()
self.value = val
Is this an arbitrary design decision?
All instances of the class will share the same instance of the descriptor (e.g. TypedProperty). So, if you store the value on the TypedProperty, then all instances of Foo will have the same value for the name and num values. This is usually not desirable (or expected) for descriptors.
e.g. if you run the following script:
class TypedProperty2(object):
def __init__(self, val, typ):
if not isinstance(val, typ):
raise TypeError()
self.value = val
self.typ = typ
def __get__(self, instance, owner):
return self.value
def __set__(self, instance, val):
if not isinstance(val, self.typ):
raise TypeError()
self.value = val
class Foo(object):
name = TypedProperty2("name", str)
f1 = Foo()
f1.name = 'blah'
f2 = Foo()
print(f2.name)
f2.name = 'bar'
print(f1.name)
You'll see the following output:
blah
bar
so we can see that initially f2 had f1's name and then, after changing the name of f2, f1 picked up f2's name.

Using __setattr__ + __slots__ in a python3 class

I am trying to be all fancy with sub element attribute access in a custom class hierarchy.
My fanciness works in that I can successfully use descriptors to do this.
I want to be even more fancy and make the class RefHolder (shown below in the testcase) use slots to save space.
When I try to use slots though, I get RuntimeError: maximum recursion depth exceeded
Note that I have already tried looking at existing solutions for this, the most closely matching I could find being this one:
https://stackoverflow.com/a/19566973/1671693
I have tried this in the testcase below but I am still get the runtimeerror.
Note that in the testcase, if the commented lines are used instead of the ones directly beneath them and __slots__ is removed from RefHolder,
The testcase passes.
Any suggestions?
Additionally, I am creating an object for every attribute access which seems expensive, are there any suggestions on a more efficient way of achieving the same behavior? Thanks!
import unittest
class RefHolder():
__slots__ = ['__obj', 'get_value']
def __init__(self, obj, get_value=False):
self.__dict__['__obj'] = obj
self.__dict__['get_value']=get_value
def get_sub(self, name):
#attr = self.__dict__['__obj'].find_by_name(name)
attr = self.__dict__['__obj'].__get__(self, RefHolder).find_by_name(name)
if attr is None:
raise AttributeError("Can't find field {}".format(name))
return attr
def __getattr__(self, name):
attr = self.get_sub(name)
#if self.__dict__['get_value']:
if self.__dict__['get_value'].__get__(self, RefHolder):
return attr.Value
else:
return attr
def __setattr__(self, name, value):
attr = self.get_sub(name)
#if self.__dict__['get_value']:
if self.__dict__['get_value'].__get__(self, RefHolder):
attr.Value = value
else:
raise AttributeError("{} is read only in this context".format(name))
class ContainerAccess():
__slots__ = ['get_value']
def __init__(self, get_value=False):
self.get_value = get_value
def __get__(self, obj, objtype=None):
if obj is None:
return self
return RefHolder(obj, self.get_value)
def __set__(self, obj, value):
raise AttributeError("Read Only attribute".format(value))
class PropVal():
def __init__(self, val):
self.Value = val
#property
def Value(self):
return self._value
#Value.setter
def Value(self, value):
self._value = value
class T():
get = ContainerAccess()
getv = ContainerAccess(get_value=True)
def __init__(self):
self.store = {}
self._value = 0
def find_by_name(self, name):
return self.store.get(name)
class T2(T):
pass
class TestDesc(unittest.TestCase):
def test_it(self):
t = T()
t2 = T2()
t.store['my_val'] = PropVal(5)
t.store['my_val2'] = PropVal(6)
t2.store['my_val'] = PropVal(1)
self.assertEqual(t.get.my_val.Value, 5)
self.assertEqual(t.get.my_val2.Value, 6)
self.assertEqual(t2.get.my_val.Value, 1)
t.get.my_val.Value = 6
self.assertEqual(t.get.my_val.Value, 6)
with self.assertRaises(AttributeError):
t.get.blah.Value = 6
#self.assertEqual(t.get.my_other_val.Value, None)
self.assertEqual(t.getv.my_val, 6)
t.getv.my_val = 7
self.assertEqual(t.getv.my_val, 7)
with self.assertRaises(AttributeError):
t.get.my_val = 7

Python - Adding attribute to descriptor

I've this descriptor:
# Generic descriptor
class Attribute(object):
def __init__(self, value):
self.value = value
def __get__(self, instance, value):
return self.value
def __set__(self, instance, value):
self.value = value
I would add an attribute 'modified' to check if an instance of descriptor is modified. Es.
# Generic descriptor
class Attribute(object):
def __init__(self, value):
self.value = value
self.modified = False
def __get__(self, instance, value):
return self.value
def __set__(self, instance, value):
self.value = value
self.modified = True
How I can do this ?
Note that in your __get__ and __set__ methods, you actually want to access instance, not self (self being the actual Attribute object).
Here is one way of doing it:
class Attribute(object):
def __init__(self, attr):
self.attr = attr
def __get__(self, instance, owner):
return getattr(instance, self.attr)
def __set__(self, instance, value):
setattr(instance, self.attr, value)
instance.modified = True
class A(object):
def __init__(self):
self._f = 0
self.modified = False
f = Attribute('_f')
a = A()
a.f
=> 0
a.modified
=> False
a.f = 33
a.modified
=> True
Of course, this snippet can be improved in many ways, depending on what you're trying to achieve.

How can I make __init__ take a new value or an existing instance?

I have a pattern that looks similar to the following:
class Foobar(object): # instances of this class will be referenced by others
def __init__(self, value):
self.value = value
class Foo(object):
def __init__(self, value, foobar)
self.value = value
if isinstance(foobar, Foobar):
self.foobar = foobar
else:
self.foobar = Foobar(foobar)
class Bar(object):
def __init__(self, value, foobar)
self.value = value
if isinstance(foobar, Foobar):
self.foobar = foobar
else:
self.foobar = Foobar(foobar)
This allows Foo and Bar to take either a new value (to create a Foobar) or an existing instance of Foobar as their foobar argument.
I would like to get rid of this redundant code:
# ...
if isinstance(foobar, Foobar):
self.foobar = foobar
else:
self.foobar = Foobar(foobar)
I considered the following, but it doesn't work due to infinite recursion in Foobar.__new__():
class Foobar(object):
def __new__(cls, value):
if isinstance(value, cls):
return value
else:
return Foobar(value)
def __init__(self, value):
self.value = value
class Foo(object):
def __init__(self, value, foobar)
self.value = value
self.foobar = Foobar(foobar)
class Bar(object):
def __init__(self, value, foobar)
self.value = value
self.foobar = Foobar(foobar)
What is the best way to allow classes to create new instances or use existing instances depending on the values passed to __init__?
You can get rid of the recursion by calling the base class __new__():
class Foobar(object):
def __new__(cls, value):
if isinstance(value, cls):
return value
else:
return object.__new__(cls, value)
def __init__(self, value):
self.value = value
Note that the first parameter to __new__() is a class, not self.
That said, I'm not convinced that this is a useful pattern. In general, I'd recommend to accept instances in the constructor and leave the object construction to the calling code. While magic that does the Right Thing often seems convenient, it usually causes more problems down the road than it is worth.
Another option would be to factor out the duplicated code with a mixin class...
class Foobar(object):
def __init__(self, value):
self.value = value
class FoobarMixin(object):
def __init__(self, **kwargs):
foobar = kwargs['foobar']
if isinstance(foobar, Foobar):
self.foobar = foobar
else:
self.foobar = Foobar(foobar)
class Foo(FoobarMixin):
def __init__(self, value, **kwargs):
super(Foo, self).__init__(**kwargs)
self.value = value
print self.value, self.foobar
class Bar(FoobarMixin):
def __init__(self, value, **kwargs):
super(Bar, self).__init__(**kwargs)
self.value = value
print self.value, self.foobar
foo = Foo('foo', foobar='foobar')
bar = Bar('bar', foobar=Foobar('foobar'))
...which prints...
foo <__main__.Foobar object at 0x7fa0fedf6050>
bar <__main__.Foobar object at 0x7fa0fedeaf10>

Categories