Let's say I need to call a method or change an attribute of the class, and I don't know exactly which it's going to be in advance. For now my code looks like this:
def change(obj, attr_name, val):
attr = getattr(obj, attr_name, None)
# check if attr exists
if type(attr) == 'NoneType':
print('no such attribute')
return
if callable(attr):
attr(val)
else:
setattr(obj, attr_name, val)
However, it doesn't seem very elegant to me.
I've tried googling this for a couple of days and can't find an answer.
I was using this to call methods of an object which names I didn't know in advance. As I understand from reading python literature it is valid to use getattr in this case. However, later I started to decorate some of my methods with #property and when I try to call them with getattr(obj, attr_name)(value) I get an error. Here's the code snippet to illustrate my problem:
class myclass1:
def __init__(self):
self._a = 'hello'
def getA(self):
return self._a
def setA(self, val):
self._a = val
class myclass2:
def __init__(self):
self._a = 'hello'
#property
def A(self):
return self._a
#A.setter
def A(self, val):
self._a = val
def changeOLD(obj, attr_name, val):
attr = getattr(obj, attr_name, None)(val)
obj1 = myclass1()
obj2 = myclass2()
changeOLD(obj1, 'setA', 'goodbye')
print(obj1.getA())
changeOLD(obj2, 'A', 'goodbye')
print(obj2.A)
When I run it:
$ python test.py
goodbye
Traceback (most recent call last):
File "test.py", line 45, in <module>
changeOLD(obj2, 'A', 'goodbye')
File "test.py", line 37, in changeOLD
attr = getattr(obj, attr_name, None)(val)
TypeError: 'str' object is not callable
So I figured I need a way to differentiate between attribute and method.
There's no major problems with the code you've provided for the use case you've described. There are some changes you could do though.
I'd change so you check against None instead of the type of None. And printing is usually not done in a method that handles logic. You'd might also want to return the value from the function you're calling. Lastly, change the method name to something more fitting.
def set_or_call(obj, name, value):
attr_or_function = getattr(obj, name, None)
if attr_or_function is None:
return None # Or raise an exception if this is suppose to be an error.
elif callable(attr_or_function):
return attr_or_function(value)
else:
setattr(obj, attr_or_function, value)
Related
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.
I have a class in which a method first needs to verify that an attribute is present and otherwise call a function to compute it. Then, ensuring that the attribute is not None, it performs some operations with it. I can see two slightly different design choices:
class myclass():
def __init__(self):
self.attr = None
def compute_attribute(self):
self.attr = 1
def print_attribute(self):
if self.attr is None:
self.compute_attribute()
print self.attr
And
class myclass2():
def __init__(self):
pass
def compute_attribute(self):
self.attr = 1
return self.attr
def print_attribute(self):
try:
attr = self.attr
except AttributeError:
attr = self.compute_attribute()
if attr is not None:
print attr
In the first design, I need to make sure that all the class attributes are set to None in advance, which can become verbose but also clarify the structure of the object.
The second choice seems to be the more widely used one. However, for my purposes (scientific computing related to information theory) using try except blocks everywhere can be a bit of an overkill given that this class doesn't really interact with other classes, it just takes data and computes a bunch of things.
Firstly, you can use hasattr to check if an object has an attribute, it returns True if the attribute exists.
hasattr(object, attribute) # will return True if the object has the attribute
Secondly, You can customise attribute access in Python, you can read more about it here: https://docs.python.org/2/reference/datamodel.html#customizing-attribute-access
Basically, you override the __getattr__ method to achieve this, so something like:
class myclass2():
def init(self):
pass
def compute_attr(self):
self.attr = 1
return self.attr
def print_attribute(self):
print self.attr
def __getattr__(self, name):
if hasattr(self, name) and getattr(self, name)!=None:
return getattr(self, name):
else:
compute_method="compute_"+name;
if hasattr(self, compute_method):
return getattr(self, compute_method)()
Make sure you only use getattr to access the attribute within __getattr__ or you'll end up with infinite recursion
Based on the answer jonrsharpe linked, I offer a third design choice. The idea here is that no special conditional logic is required at all either by the clients of MyClass or by code within MyClass itself. Instead, a decorator is applied to a function that does the (hypothetically expensive) computation of the property, and then that result is stored.
This means that the expensive computation is done lazily (only if a client tries to access the property) and only performed once.
def lazyprop(fn):
attr_name = '_lazy_' + fn.__name__
#property
def _lazyprop(self):
if not hasattr(self, attr_name):
setattr(self, attr_name, fn(self))
return getattr(self, attr_name)
return _lazyprop
class MyClass(object):
#lazyprop
def attr(self):
print('Generating attr')
return 1
def __repr__(self):
return str(self.attr)
if __name__ == '__main__':
o = MyClass()
print(o.__dict__, end='\n\n')
print(o, end='\n\n')
print(o.__dict__, end='\n\n')
print(o)
Output
{}
Generating attr
1
{'_lazy_attr': 1}
1
Edit
Application of Cyclone's answer to OP's context:
class lazy_property(object):
'''
meant to be used for lazy evaluation of an object attribute.
property should represent non-mutable data, as it replaces itself.
'''
def __init__(self, fget):
self.fget = fget
self.func_name = fget.__name__
def __get__(self, obj, cls):
if obj is None:
return None
value = self.fget(obj)
setattr(obj, self.func_name, value)
return value
class MyClass(object):
#lazy_property
def attr(self):
print('Generating attr')
return 1
def __repr__(self):
return str(self.attr)
if __name__ == '__main__':
o = MyClass()
print(o.__dict__, end='\n\n')
print(o, end='\n\n')
print(o.__dict__, end='\n\n')
print(o)
The output is identical to above.
I have a property that has an assertion to check if a value is of type str.
To catch this assertionError I have created a decorator according to the examples I have found online.
Decorator:
def catch_assertionerror(function):
def handle_problems(*args, **kwargs):
try:
return function(*args, **kwargs)
except AssertionError:
# log_error(err.args[0])
print "error caught"
return handle_problems
Property:
#catch_assertionerror
#name.setter
def name(self, value):
assert isinstance(value, str), "This value should be a string"
self._name = name
Setting the name property:
self.name = self.parse_name_from_xml()
When I run this code, there is no error shown, so I guess it is caught, but on the other hand, the error message is not printed to the screen.
Then I tried a more simple example I found on Stachoverflow:
def handleError(function):
def handleProblems():
try:
function()
except Exception:
print "Oh noes"
return handleProblems
#handleError
def example():
raise Exception("Boom!")
This also handled the error but did not print the error message to the screen.
Could someone explain to me what I am missing here?
Your latter example works for me, but your main problem lies in that you're not wrapping a function with catch_assertionerror in
#catch_assertionerror
#name.setter
def name(self, value):
assert isinstance(value, str), "This value should be a string"
self._name = name
but a descriptor. To make matters worse, you return a function instead, not a new descriptor wrapping the original. Now when you assign to name attribute you just replace your wrapper function with the assigned value.
Step by step, using your original class definition:
class X(object):
#property
def name(self):
return self._name
#catch_assertionerror
#name.setter
def name(self, value):
assert isinstance(value, str), "This value should be a string"
self._name = value
>>> x = X()
>>> x.name
<unbound method X.handle_problems>
>>> x.__dict__
{}
>>> x.name = 2
>>> x.name
2
>>> x.__dict__
{'name': 2}
What you must do is wrap the method function instead and then pass it to the descriptor handling decorator:
class X(object):
#property
def name(self):
return self._name
#name.setter
#catch_assertionerror
def name(self, value):
assert isinstance(value, str), "This value should be a string"
self._name = value
and so:
>>> x = X()
>>> x.name
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in name
AttributeError: 'X' object has no attribute '_name'
>>> x.name = 2
error caught
>>> x.name = "asdf"
>>> x.name
'asdf'
In the future consider using functools.wraps and functools.update_wrapper. Without them your classes and functions are harder to inspect, because your wrappers will hide the original:
>>> #catch_assertionerror
... def this_name_should_show(): pass
...
>>> this_name_should_show
<function handle_problems at 0x7fd3d69e22a8>
Defining your decorator this way:
def catch_assertionerror(function):
#wraps(function)
def handle_problems(*args, **kwargs):
...
return handle_problems
will preserve the original function's information:
>>> #catch_assertionerror
... def this_name_should_show(): pass
...
>>> this_name_should_show
<function this_name_should_show at 0x7fd3d69e21b8>
It would have also indicated to you in your case that there's a problem:
# When trying to define the class
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in X
File "<stdin>", line 2, in catch_assertionerror
File "/usr/lib/python2.7/functools.py", line 33, in update_wrapper
setattr(wrapper, attr, getattr(wrapped, attr))
AttributeError: 'property' object has no attribute '__module__'
This question already has answers here:
How to make an immutable object in Python?
(26 answers)
Closed 8 years ago.
I have read a lot about this subject here but i still can't find an appropriate answer.
I have a class like:
class A(object):
def __init__(self, first, second):
self.first = first
self.second = second
def __eq__(self, other):
return ****
def __str__(self):
return *****
def __repr__(self):
return ****
a = A("a", "b")
How can i forbid a.first = "c" for example ?
You can override __setattr__ to either prevent any changes:
def __setattr__(self, name, value):
raise AttributeError('''Can't set attribute "{0}"'''.format(name))
or prevent adding new attributes:
def __setattr__(self, name, value):
if not hasattr(self, name):
raise AttributeError('''Can't set attribute "{0}"'''.format(name))
# Or whatever the base class is, if not object.
# You can use super(), if appropriate.
object.__setattr__(self, name, value)
You can also replace hasattr with a check against a list of allowed attributes:
if name not in list_of_allowed_attributes_to_change:
raise AttributeError('''Can't set attribute "{0}"'''.format(name))
Another approach is to use properties instead of plain attributes:
class A(object):
def __init__(self, first, second):
self._first = first
self._second = second
#property
def first(self):
return self._first
#property
def second(self):
return self._second
You can disable __setattr__ as the last step of initializing the object.
class A(object):
def __init__(self, first, second):
self.first = first
self.second = second
self.frozen = True
def __setattr__(self, name, value):
if getattr(self, 'frozen', False):
raise AttributeError('Attempt to modify immutable object')
super(A, self).__setattr__(name, value)
>>> a = A(1, 2)
>>> a.first, a.second
(1, 2)
>>> a.first = 3
Traceback (most recent call last):
File "<pyshell#46>", line 1, in <module>
a.first = 3
File "<pyshell#41>", line 10, in __setattr__
raise AttributeError('Attempt to modify immutable object')
AttributeError: Attempt to modify immutable object
Edit: This answer has a flaw, which I'm sure is shared by every other solution: if the members themselves are mutable, nothing protects them. If your object contains a list for example, it's all over. This is in contrast to i.e. C++ where declaring an object const extends to all its members recursively.
Consider the following python code:
class Foo(object):
def __init__(self, value):
self._value = value
#property
def value(self):
return "value: {v}".format(v=self._value)
#value.setter
def value(self, value):
self._value = value
class Bar(object):
def __init__(self):
self.foo = Foo('foo')
def __getattr__(self, attr, *args, **kwargs):
"""
Intercepts attribute calls, and if we don't have it, look at the
webelement to see if it has the attribute.
"""
# Check first to see if it looks like a method, if not then just return
# the attribute the way it is.
# Note: this has only been tested with variables, and methods.
if not hasattr(getattr(self.foo, attr), '__call__'):
return getattr(self.foo, attr)
def callable(*args, **kwargs):
'''
Returns the method from the webelement module if found
'''
return getattr(self.foo, attr)(*args, **kwargs)
return callable
>>> b = Bar()
>>> b.foo
<__main__.Foo object at 0x819410>
>>> b.foo.value
'value: foo'
>>> b.foo.value = '2'
>>> b.foo.value
'value: 2'
>>> b.value
'value: 2'
>>> b.value = '3'
>>> b.value
'3'
That last part, I want it to be 'value: 3' instead of '3' because now my property 'value' is now an attribute instead.
Is it possible, and if it is how would I would I do that.
Your __getattr__ returns the property value, not the property itself. When you access getattr(self.foo, attr) it does the equivalent of self.foo.value and returns that, and the property is called at that time.
You thus need to implement a __setattr__ method too, to mirror the __getattr__ and pass on the value setting to the contained foo object.
Under the hood, Python implements properties as descriptors; their __get__() method is called by the lower-level __getattribute__ method, which causes them to return their computed value. It is never the property object itself that is returned.
Here's an example __setattr__:
def __setattr__(self, attr, value):
if hasattr(self, 'foo') and hasattr(self.foo, attr):
setattr(self.foo, attr, value)
return
super(Bar, self).__setattr__(attr, value)
Note: because your __init__ sets self.foo, you need to test if foo exists on your class (hasattr(self, 'foo'). You also need to call the original __setattr__ implementation to make sure that things like self.foo = Foo() work still.