理解__getattr__, __setattr__

 

The following methods can be defined to customize the meaning of attribute access (use of, assignment to, or deletion of x.name) for class instances.

object.__getattr__(self, name)

Called when an attribute lookup has not found the attribute in the usual places (i.e. it is not an instance attribute nor is it found in the class tree for self). name is the attribute name. This method should return the (computed) attribute value or raise an AttributeError exception.

Note that if the attribute is found through the normal mechanism, __getattr__() is not called. (This is an intentional asymmetry between __getattr__() and __setattr__().) This is done both for efficiency reasons and because otherwise __getattr__() would have no way to access other attributes of the instance. Note that at least for instance variables, you can fake total control by not inserting any values in the instance attribute dictionary (but instead inserting them in another object). See the __getattribute__() method below for a way to actually get total control in new-style classes.

object.__setattr__(self, name, value)

Called when an attribute assignment is attempted. This is called instead of the normal mechanism (i.e. store the value in the instance dictionary). name is the attribute name, value is the value to be assigned to it.

If __setattr__() wants to assign to an instance attribute, it should not simply execute self.name = value — this would cause a recursive call to itself. Instead, it should insert the value in the dictionary of instance attributes, e.g., self.__dict__[name] = value. For new-style classes, rather than accessing the instance dictionary, it should call the base class method with the same name, for example, object.__setattr__(self, name, value).

 

例子(Python Recipe6.1):

 

#!/usr/bin/python

class Temperature(object):
    coefficients = {'c': (1.0, 0.0, -273.15), 'f': (1.8, -273.15, 32.0),
                    'r': (1.8, 0.0, 0.0)}
    def __init__(self, **kwargs):
        # default to absolute (Kelvin) 0, but allow one named argument,
        # with name being k, c, f or r, to use any of the scales
        try:
            name, value = kwargs.popitem()
        except KeyError:
            # no arguments, so default to k=0
            name, value = 'k', 0
        # error if there are more arguments, or the arg's name is unknown
        if kwargs or name not in 'kcfr':
            kwargs[name] = value             # put it back for diagnosis
            raise TypeError, 'invalid arguments %r' % kwargs
        setattr(self, name, float(value))
    def __getattr__(self, name):
        # maps getting of c, f, r, to computation from k
        print '__getattr__: %s' % str(name)
        try:
                        eq = self.coefficients[name]
        except KeyError:
            # unknown name, give error message
            raise AttributeError, name
        return (self.k + eq[1]) * eq[0] + eq[2]
    def __setattr__(self, name, value):
        # maps settings of k, c, f, r, to setting of k; forbids others
        print '__setattr__: name = %s, value = %s' % (str(name), str(value))
        if name in self.coefficients:
            # name is c, f or r -- compute and set k
            eq = self.coefficients[name]
            self.k = (value - eq[2]) / eq[0] - eq[1]
        elif name == 'k':
            # name is k, just set it
            object.__setattr__(self, name, value)
        else:
            # unknown name, give error message
            raise AttributeError, name
    def __str__(self):
        # readable, concise representation as string
        return "%s K" % self.k
    def __repr__(self):
        # detailed, precise representation as string
        return "Temperature(k=%r)" % self.k

if __name__ == '__main__':
        t = Temperature(f=70)
        print t.__dict__
        print t.c
        print t.f
        print t.k

输出为:

simplyzhao@simplyzhao-laptop:~/code/python$ ./recipe2ed_6_1.py
__setattr__: name = f, value = 70.0
__setattr__: name = k, value = 294.261111111
{'k': 294.26111111111106}
__getattr__: c
21.1111111111
__getattr__: f
70.0
294.261111111