The Python docs say all that needs to be said, as far as I can see.
Answer from Chris Morgan on Stack Overflow
setattr(object, name, value)This is the counterpart of
getattr(). The arguments are an object, a string and an arbitrary value. The string may name an existing attribute or a new attribute. The function assigns the value to the attribute, provided the object allows it. For example,setattr(x, 'foobar', 123)is equivalent tox.foobar = 123.
The Python docs say all that needs to be said, as far as I can see.
setattr(object, name, value)This is the counterpart of
getattr(). The arguments are an object, a string and an arbitrary value. The string may name an existing attribute or a new attribute. The function assigns the value to the attribute, provided the object allows it. For example,setattr(x, 'foobar', 123)is equivalent tox.foobar = 123.
You are setting self.name to the string "get_thing", not the function get_thing.
If you want self.name to be a function, then you should set it to one:
setattr(self, 'name', self.get_thing)
However, that's completely unnecessary for your other code, because you could just call it directly:
value_returned = self.get_thing()
Why `self.__setattr__(attr, value)` trigger `__getattribute__`?
python - How to use __setattr__ correctly, avoiding infinite recursion - Stack Overflow
Why used setattr/getattr instead of writing my own methods?
python - How does __setattr__ work with class attributes? - Stack Overflow
You must call the parent class __setattr__ method:
class MyTest(object):
def __init__(self, x):
self.x = x
def __setattr__(self, name, value):
if name=="device":
print "device test"
else:
super(MyTest, self).__setattr__(name, value)
# in python3+ you can omit the arguments to super:
#super().__setattr__(name, value)
Regarding the best-practice, since you plan to use this via xml-rpc I think this is probably better done inside the _dispatch method.
A quick and dirty way is to simply do:
class My(object):
def __init__(self):
self.device = self
Or you can modify self.__dict__ from inside __setattr__():
class SomeClass(object):
def __setattr__(self, name, value):
print(name, value)
self.__dict__[name] = value
def __init__(self, attr1, attr2):
self.attr1 = attr1
self.attr2 = attr2
sc = SomeClass(attr1=1, attr2=2)
sc.attr1 = 3
Hello fellow pythoners!
I was introduced to the built-in function setattr() and getattr() functions today in regards to classes and started to question why I would want to use these functions instead of writing my own get_attribute and set_attribute methods.
So yeah, why? All my searches just gave me explanations as to how they're used, not why to use it instead of what I was taught back in school.
Is writing my own methods just a translation to how to handle this from someone who came from a language such as java or C# to setting and getting attributes?
Edit: Code example below
class Student:
def __init__(self, name, grade):
self.name = name
self.grade = grade
def get_name(self):
return self.name
def get_grade(self):
return self.grade
def set_name(self, name):
self.name = name
def set_grade(self, grade):
self.grade = grade
class Person:
def __init__(self, name, addr):
self.name = name
self.address = addr
if __name__ == "__main__":
george = Student("George", "B")
jessica = Person("Jessica", "221B Baker Steet")
print(f"{george.get_name()} has a {george.get_grade()} in English.")
george.set_grade("A")
print(f"{george.get_name()} now has a {george.get_grade()} in English")
print(f"{getattr(jessica, name)} lives on {getattr(jessica, address)}.")
setattr(jessica, address, "Abbey Road 5")
print(f"{getattr(jessica, name)} now lives on {getattr(jessica, address)}__setattr__() applies only to instances of the class. In your second example, when you define PublicAttribute1, you are defining it on the class; there's no instance, so __setattr__() is not called.
N.B. In Python, things you access using the . notation are called attributes, not variables. (In other languages they might be called "member variables" or similar.)
You're correct that the class attribute will be shadowed if you set an attribute of the same name on an instance. For example:
class C(object):
attr = 42
c = C()
print(c.attr) # 42
c.attr = 13
print(c.attr) # 13
print(C.attr) # 42
Python resolves attribute access by first looking on the instance, and if there's no attribute of that name on the instance, it looks on the instance's class, then that class's parent(s), and so on until it gets to object, the root object of the Python class hierarchy.
So in the example above, we define attr on the class. Thus, when we access c.attr (the instance attribute), we get 42, the value of the attribute on the class, because there's no such attribute on the instance. When we set the attribute of the instance, then print c.attr again, we get the value we just set, because there is now an attribute by that name on the instance. But the value 42 still exists as the attribute of the class, C.attr, as we see by the third print.
The statement to set the instance attribute in your __init__() method is handled by Python like any code to set an attribute on an object. Python does not care whether the code is "inside" or "outside" the class. So, you may wonder, how can you bypass the "protection" of __setattr__() when initializing the object? Simple: you call the __setattr__() method of a class that doesn't have that protection, usually your parent class's method, and pass it your instance.
So instead of writing:
self.PublicAttribute1 = "attribute"
You have to write:
object.__setattr__(self, "PublicAttribute1", "attribute")
Since attributes are stored in the instance's attribute dictionary, named __dict__, you can also get around your __setattr__ by writing directly to that:
self.__dict__["PublicAttribute1"] = "attribute"
Either syntax is ugly and verbose, but the relative ease with which you can subvert the protection you're trying to add (after all, if you can do that, so can anyone else) might lead you to the conclusion that Python doesn't have very good support for protected attributes. In fact it doesn't, and this is by design. "We're all consenting adults here." You should not think in terms of public or private attributes with Python. All attributes are public. There is a convention of naming "private" attributes with a single leading underscore; this warns whoever is using your object that they're messing with an implementation detail of some sort, but they can still do it if they need to and are willing to accept the risks.
The __setattr__ method defined in a class is only called for attribute assignments on instances of the class. It is not called for class variables, since they're not being assigned on an instance of the class with the method.
Of course, classes are instances too. They're instances of type (or a custom metaclass, usually a subclass of type). So if you want to prevent class variable creation, you need to create a metaclass with a __setattr__ method.
But that's not really what you need to make your class do what you want. To just get a read-only attribute that can only be written once (in the __init__ method), you can probably get by with some simpler logic. One approach is to set another attribute at the end of the __init__ method, which tells __setattr__ to lock down assignments after it is set:
class Foo:
def __init__(self, a, b):
self.a = a
self.b = b
self._initialized = True
def __setattr__(self, name, value):
if self.__dict__.get('_initialized'):
raise Exception("Attribute is Read-Only")
super().__setattr__(name, value)
Another option would be to use property descriptors for the read-only attributes and store the real values in "private" variables that can be assigned to normally. You'd not use __setattr__ in this version:
class Foo:
def __init__(self, a, b):
self._a = a
self._b = b
@property
def a(self):
return self._a
@property
def b(self):
return self._b
foo = Foo(3, 5)
foo.a = 7 # causes "AttributeError: can't set attribute"