Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   Python (http://www.velocityreviews.com/forums/f43-python.html)
-   -   Python recursively __getattribute__ (http://www.velocityreviews.com/forums/t738595-python-recursively-__getattribute__.html)

Roman Dolgiy 11-22-2010 03:46 PM

Python recursively __getattribute__
 
Hello,

I need to implement such behavior:

obj.attr1.attr2.attr3 --> obj.attr1__attr2__attr3
It looks like I have to override obj's class __getattribute__ and also
use python descriptors somehow.

Any help will be much appreciated.
http://stackoverflow.com/questions/4...y-getattribute

Andreas Waldenburger 11-22-2010 04:04 PM

Re: Python recursively __getattribute__
 
On Mon, 22 Nov 2010 07:46:47 -0800 (PST) Roman Dolgiy <tosters@gmail.com> wrote:

> Hello,
>
> I need to implement such behavior:
>
> obj.attr1.attr2.attr3 --> obj.attr1__attr2__attr3
> It looks like I have to override obj's class __getattribute__ and also
> use python descriptors somehow.
>
> Any help will be much appreciated.
> http://stackoverflow.com/questions/4...y-getattribute


Why? No, really: Why?

In that link you say that you need to do this to support legacy code. I still don't see how this would be necessary. If you need to support legacy code, doesn't that mean that the solution you're asking for already exists?

I really think you should go into detail about why you need this. I'm certain that there's a better solution to your problem. ("Better" being one that is reasonably easy to implement and maintain.)

/W

--
To reach me via email, replace INVALID with the country code of my home
country. But if you spam me, I'll be one sour Kraut.


Roman Dolgiy 11-22-2010 04:41 PM

Re: Python recursively __getattribute__
 
On Nov 22, 6:04*pm, Andreas Waldenburger <use...@geekmail.INVALID>
wrote:
> On Mon, 22 Nov 2010 07:46:47 -0800 (PST) Roman Dolgiy <tost...@gmail.com> wrote:
>
> > Hello,

>
> > I need to implement such behavior:

>
> > obj.attr1.attr2.attr3 --> obj.attr1__attr2__attr3
> > It looks like I have to override obj's class __getattribute__ and also
> > use python descriptors somehow.

>
> > Any help will be much appreciated.
> >http://stackoverflow.com/questions/4...sively-getattr...

>
> Why? No, really: Why?
>
> In that link you say that you need to do this to support legacy code. I still don't see how this would be necessary. If you need to support legacy code, doesn't that mean that the solution you're asking for already exists?
>
> I really think you should go into detail about why you need this. I'm certain that there's a better solution to your problem. ("Better" being one that is reasonably easy to implement and maintain.)
>
> /W
>
> --
> To reach me via email, replace INVALID with the country code of my home
> country. *But if you spam me, I'll be one sour Kraut.


I have a django project.

obj is django-haystack's SearchResult instance, it contains a lot of
de-normalized data (user__name, user__address) from django model, and
I need to access it as result.user.name for compability reasons.

Andreas Waldenburger 11-22-2010 05:00 PM

Re: Python recursively __getattribute__
 
On Mon, 22 Nov 2010 08:41:49 -0800 (PST) Roman Dolgiy <tosters@gmail.com> wrote:

> On Nov 22, 6:04*pm, Andreas Waldenburger <use...@geekmail.INVALID>
> wrote:
> > On Mon, 22 Nov 2010 07:46:47 -0800 (PST) Roman Dolgiy
> > <tost...@gmail.com> wrote:
> >
> > > Hello,

> >
> > > I need to implement such behavior:

> >
> > > obj.attr1.attr2.attr3 --> obj.attr1__attr2__attr3
> > > It looks like I have to override obj's class __getattribute__ and
> > > also use python descriptors somehow.

> >
> > > Any help will be much appreciated.
> > >http://stackoverflow.com/questions/4...sively-getattr....

> >
> > Why? No, really: Why?
> >
> > [...]

>
> I have a django project.
>
> obj is django-haystack's SearchResult instance, it contains a lot of
> de-normalized data (user__name, user__address) from django model, and
> I need to access it as result.user.name for compability reasons.


I don't know anything about django, so I may be babbling nonsense. Caveat emptor.

How about taking your "user__whatever" thingies and have a function emit customized result instances. For this you can just create a plain object subclass, say ResultElement. If these "user__whatever" thingies are strings (you haven't told), split them by "__" and create a new plain object for every level of attributes, attaching it to the previous level.

You can probably make your life easier if you use defaultdicts first and then translate these to your object hierarchy.

That's how I'd do it. I don't know if that helps you. If not, please provide more info.

/W

--
To reach me via email, replace INVALID with the country code of my home
country. But if you spam me, I'll be one sour Kraut.


Terry Reedy 11-22-2010 05:57 PM

Re: Python recursively __getattribute__
 
On 11/22/2010 10:46 AM, Roman Dolgiy wrote:
> Hello,
>
> I need to implement such behavior:
>
> obj.attr1.attr2.attr3 --> obj.attr1__attr2__attr3


obj.attr1.attr2.attr3 is parsed as ((obj.attr1).attr2).attr3,
so this cannot work in general but only if attr1 and attr2 are known to
not be 'final' names.

> It looks like I have to override obj's class __getattribute__ and also
> use python descriptors somehow.
>
> Any help will be much appreciated.
> http://stackoverflow.com/questions/4...y-getattribute


The code posted there by THC4k depened on such knowledge, which you gave
there but not here.

--
Terry Jan Reedy


Roman Dolgiy 11-22-2010 08:44 PM

Re: Python recursively __getattribute__
 
On Nov 22, 7:57*pm, Terry Reedy <tjre...@udel.edu> wrote:
> On 11/22/2010 10:46 AM, Roman Dolgiy wrote:
>
> > Hello,

>
> > I need to implement such behavior:

>
> > obj.attr1.attr2.attr3 --> *obj.attr1__attr2__attr3

>
> obj.attr1.attr2.attr3 is parsed as ((obj.attr1).attr2).attr3,
> so this cannot work in general but only if attr1 and attr2 are known to
> not be 'final' names.
>
> > It looks like I have to override obj's class __getattribute__ and also
> > use python descriptors somehow.

>
> > Any help will be much appreciated.
> >http://stackoverflow.com/questions/4...sively-getattr...

>
> The code posted there by THC4k depened on such knowledge, which you gave
> there but not here.
>
> --
> Terry Jan Reedy


I need to support a lot of legacy code, with THC4k's approach I'll
have to modify project's existing code to use obj.attr1.val instead of
obj.attr1 but this is not suitable.

bruno.desthuilliers@gmail.com 11-23-2010 10:40 AM

Re: Python recursively __getattribute__
 
On 22 nov, 21:44, Roman Dolgiy <tost...@gmail.com> wrote:
>> http://stackoverflow.com/questions/4...sively-getattr...

>
> I need to support a lot of legacy code, with THC4k's approach I'll
> have to modify project's existing code to use obj.attr1.val instead of
> obj.attr1 but this is not suitable.


You should probably re-read THC4k's answer. His code works just fine
AFAICT:

"""
# the proxy maps attribute access to another object
class GetattrProxy(object):
def __init__(self, proxied, prefix=None):
self.proxied = proxied
self.prefix = prefix

def __getattr__(self, key):
attr = (key if self.prefix is None else self.prefix + '__' +
key)
try:
# if the proxied object has the attr return it
return getattr(self.proxied, attr)
except AttributeError:
# else just return another proxy
return GetattrProxy(self.proxied, attr)


# the thing you want to wrap
class Target(object):
attr1__attr2__attr3 = 5
attr2 = "attr2"


t = Target()
proxy = GetattrProxy(t)

print "proxy.attr1.attr2.attr3 : '%s'" % proxy.attr1.attr2.attr3
print "proxy.attr2 : '%s'" % proxy.attr2
"""

Roman Dolgiy 11-23-2010 02:54 PM

Re: Python recursively __getattribute__
 
Thanks to Andreas Waldenburger, THC4k (http://stackoverflow.com/
questions/4247036/python-recursively-getattribute) and others for
their tips. I was able to find solution:

class Null(object):
def __repr__(self):
return "<Null>"

def __str__(self):
return ''

def __nonzero__(self):
return False


class ResultAttrFactory(type):
_cache = {}

@classmethod
def prepare(cls, base, result):
dict_ = ResultAttr.__dict__.copy()
dict_.update({
'_ResultAttr__base': base,
'_ResultAttr__result': result})
return ('ResultAttr', (base,), dict_)

def __new__(cls, base, result):
if (base, result) in cls._cache:
type_ = cls._cache[(base, result)]
else:
type_ = super(ResultAttrFactory, cls).__new__(
cls, *cls.prepare(base, result))
cls._cache[(base, result)] = type_
return type_

def __init__(cls, base, result):
pass


class ResultAttr:
"""Should be used only with ResultAttrFactory"""
@staticmethod
def __new__(cls, arg1, name):
return cls.__base.__new__(cls, arg1)

def __init__(self, arg1, name):
self.__name = name
super(self.__class__, self).__init__(arg1)

def get_result_attr(self, name):
if self.__result.is_denorm_attr(name):
attr = getattr(self.__result, name, None)
else:
attr = getattr(self.__result, name)
return attr

def __getattr__(self, name):
lookup_name = "%s__%s" % (self.__name, name)
attr = self.get_result_attr(lookup_name)
if type(attr).__name__ == 'ResultAttr':
type_ = attr.__base
elif attr is None:
type_ = Null
else:
type_ = type(attr)
result_attr = ResultAttrFactory(
type_, self.__result)(attr, lookup_name)
return result_attr


class BaseResult(object):
"""
>>> class Result(BaseResult):

... def __init__(self, *args, **kwargs):
... self.x = 35
... self.y = 5
... self.y__a = 3
... self.y__b = 'hello'
... self.y__c__x = [1, 2, 3]
... super(Result, self, *args, **kwargs)
>>> r = Result()
>>> r.x

35
>>> type(r.x)

<type 'int'>
>>> r.y

5
>>> type(r.y) # doctest:+ELLIPSIS

<class '....ResultAttr'>
>>> r.y.a

3
>>> r.y.b

'hello'
>>> r.y.c # Is there any way to raise AtrributeError here?

<Null>
>>> r.y.d

Traceback (most recent call last):
AttributeError: 'Result' object has no attribute 'y__d'
>>> r.y.c.x

[1, 2, 3]
"""
def is_denorm_attr(self, name):
return bool([k for k in self.__dict__.keys() if "%s__" % name
in k])

def __getattribute__(self, name):
attr = super(BaseResult, self).__getattribute__(name)
if name in ('__dict__', 'is_denorm_attr'):
return attr
if self.is_denorm_attr(name):
return ResultAttrFactory(type(attr), self)(attr, name)
else:
return attr


if __name__ == '__main__':
import doctest
doctest.testmod()

https://gist.github.com/710977

The only problem is:

>>> r.y.c

<Null>

Is there any way to raise AtrributeError here?

Also is there any negative side effects of such method?


All times are GMT. The time now is 07:23 PM.

Powered by vBulletin®. Copyright ©2000 - 2014, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.