9

I have a class that implements virtual attributes using __getattr__. The attributes can be expensive, e.g. performing a query. Now, I am using a library which checks if my object has the attribute before actually getting it.

As a consequence, a query is executed two times instead of one. Of course it makes sense to actually execute __getattr__ to really know if the attribute exists.

class C(object):
    def __getattr__(self, name):
        print "I was accessed"
        return 'ok'

c = C()
hasattr(c, 'hello')

Is there any way to prevent this?

If Python supported __hasattr__ then I could simply check if the query exists, has opposed to actually run it.

I can create a cache, but it is heavy since a query might have parameters. Of course, the server might cache the queries itself and minimise the problem, but it is still heavy if queries return a lot of data.

Any ideas?

SaidbakR
  • 13,303
  • 20
  • 101
  • 195
koriander
  • 3,110
  • 2
  • 15
  • 23
  • If I understand the situation correct, you need to cache based on `name` only, so that could be just a dictionary. BTW, why do you want to make it look like an attribute? – bereal May 17 '15 at 18:21
  • What do you want to happen if the object doesn't support the query? – Shashank May 17 '15 at 18:29
  • `hasattr` is not a light-weight test for an attribute; it calls `getattr` essentially like `try: getattr('name'); except AttributeError: return False; else: return True`. – chepner May 17 '15 at 18:58
  • I don't think there is any way to do what you want, other than by monkey patching `hasattr`, which would not be recommended. If there is no way to avoid calling `hasattr`, I believe your only choices are caching the result or allowing the query to run twice. – bjudson May 17 '15 at 19:04
  • @shashank, if the query is not available (by name), then it returns the exception for attribute not found. – koriander May 17 '15 at 19:25
  • @bereal: because queries are accessed has attributes of an object. Sorry, too long to explain it all properly here. – koriander May 17 '15 at 19:26

2 Answers2

6

Although initially I was not fond of the idea of monkey patching, being a "bad idea" in general, I came across a very neat solution from 1999!!

http://code.activestate.com/lists/python-list/14972/

def hasattr(o, a, orig_hasattr=hasattr):
    if orig_hasattr(o, "__hasattr__"):
        return o.__hasattr__(a)
    return orig_hasattr(o, a)

__builtins__.hasattr = hasattr

Essentially it creates support for __hasattr__ in Python, which is what I thought originally would be the optimal solution.

koriander
  • 3,110
  • 2
  • 15
  • 23
  • 2
    **Monkey patching is awesome.** Rubyists have long embraced the ugly monkey. So should Pythonistas. Your augmentation of the stock `hasattr()` builtin to support `__hasattr__()` special methods is the perfect example of that. Thanks for rejuvenating this elegant hackery! – Cecil Curry Mar 13 '16 at 05:28
0

I think this is generally probably a bad pattern, but you can always check an object's underlying __dict__.

In [1]: class A(object):
   ....:     @property
   ....:     def wha(self):
   ....:         print "was accessed"
   ....:

In [2]: A.__dict__
Out[2]:
<dictproxy {'__dict__': <attribute '__dict__' of 'A' objects>,
'__doc__': None,
'__module__': '__main__',
'__weakref__': <attribute '__weakref__' of 'A' objects>,
'wha': <property at 0x10f6b11b0>}>

In [3]: a = A()

In [4]: "wha" in a.__class__.__dict__
Out[4]: True
jwilner
  • 6,348
  • 6
  • 35
  • 47
  • 2
    I think OP is dealing with a third-party library that calls `hasattr`, so this would require forking the library. – bjudson May 17 '15 at 18:48
  • I think you misunderstand, the class is fine, and I need to use __getattr__(). Plus what @bjudson said. – koriander May 17 '15 at 19:29