arjunaskykok
arjunaskykok

Reputation: 956

How to differentiate between hasattr and normal attribute access in __getattr__?

class a_class:
    def __getattr__(self, name):
        # if called by hasattr(a, 'b') not by a.b
        # print("I am called by hasattr")
        print(name)

a = a_class()
a.b_attr

hasattr(a, 'c_attr')

Please take a look the comment inside __getattr__. How do I do that? I am using Python 3. The reason is I want to create attribute dynamically but I don't want to do that when using hasattr. Thanks.

Upvotes: 8

Views: 1423

Answers (2)

berniey
berniey

Reputation: 2932

This discussion applies to Python 3. (turns out it works on Python 2.7 as well)

Not exactly the way you described but the following points might help:

  • __getattr__ will only be accessed when attribute is not found under normal way
  • hasattr() check if AttributeError is raised

See if the following code help!

>>> class A:
...     def __init__(self, a=1, b=2):
...         self.a = a
...         self.b = b
...
...     def __getattr__(self, name):
...         print('calling __getattr__')
...         print('This is instance attributes: {}'.format(self.__dict__))
...
...         if name not in ('c', 'd'):
...             raise AttributeError()
...         else:
...             return 'My Value'
...         return 'Default'
>>>         
>>> a = A()
>>> print('a = {}'.format(a.a))
a = 1
>>> print('c = {}'.format(a.c))
calling __getattr__
This is instance attributes: {'a': 1, 'b': 2}
c = My Value
>>> print('hasattr(a, "e") returns {}'.format(hasattr(a, 'e')))
calling __getattr__
This is instance attributes: {'a': 1, 'b': 2}
hasattr(a, "e") returns False
>>> 

Upvotes: 0

BrenBarn
BrenBarn

Reputation: 251355

You can't, without cheating. As the documentation says:

This [that is, hasattr] is implemented by calling getattr(object, name) and seeing whether it raises an exception or not.

In other words, you can't block hasattr without also blocking getattr, which basically means you can't block hasattr at all if you care about accessing attributes.

By "cheating" I mean one of these solutions that clever people like to post on here that involve an end-run around essentially all of Python. They typically involve reassigning builtins, inspecting/manipulating the call stack, using introspection to peek at the literal source code, modifying "secret" internal attributes of objects, and so on. For instance, you could look at the call stack to see if hasattr is in the call chain. This type of solution is possible, but extremely fragile, with possibility of breaking in future Python versions, on non-CPython implementations, or in situations where another equally ugly and devious hack is also being used.

You can see a similar question and some discussion here.

Upvotes: 12

Related Questions