@@ -498,20 +498,147 @@ def _make_boundmethod(meth, self):
498498The rest of the code does not need to be changed at all.
499499
500500
501- Meta-object protocol
501+ Meta-object protocols
502502----------------------
503503
504- - common approach in more dynamic languages
505- - pioneered in Lisp, Smalltalk, but common in most modern dynamically typed
506- languages (Python, Ruby, Javascript, Lua, ...)
507- - Primitive operations overridable by user code
508- - hooks to modify how exactly the object machinery does things
509- - Often use normal inheritance of the meta-hooks to get the base behavior (ie
510- OBJECT has a __ setattr__ with the default behavior)
504+ In addition to "normal" methods that are called directly by the program, many
505+ dynamic languages support * special methods* . These are methods that aren't meant
506+ to be called directly but will be called by the object system. In Python those
507+ special methods usually have names that start and end with two underscores, e.g.
508+ `` __init__ `` . Special methods can be used to override primitive operations and
509+ provide custom behaviour for them instead. Thus they are hooks that tell the
510+ object model machinery how exactly to do certain things. Python's object model
511+ has dozens of special methods. XXX find the list
512+
513+ Historically meta-object protocols have been introduced by Smalltalk but even
514+ more strongly by the object systems for Common Lisp, such as CLOS, which is also
515+ where the name was coined (XXX footnote: The Art of the Meta-Object Protocol).
516+
517+ In this chapter we will add three such meta-hooks to our object model. They are
518+ used to fine-tune what exactly happens when reading and writing attributes. The
519+ special methods we will add first are `` __getattr__ `` and `` __setattr__ `` , which
520+ follow closely the behaviour of Python's namesakes.
521+
522+
523+ Customizing Reading and Writing and Attribute
524+ ++++++++++++++++++++++++++++++++++++++++++++++
525+
526+ The method `` __getattr__ `` is called by the object model when the attribute that
527+ is being looked up currently is not found by normal means, i.e. neither on the
528+ instance nor on the class. It gets the name of the attribute being looked up as
529+ an argument. An equivalent of the `` __getattr__ `` special method was part of
530+ early Smalltalk systems under the name `` doesNotUnderstand: `` (footnote: A.
531+ Goldberg, Smalltalk-80: The Language and its Implementation. Addison-Wesley,
532+ 1983, page 61.)
533+
534+ The case of `` __setattr__ `` is a bit different. Since setting an attribute
535+ always creates it, `` __setattr__ `` is always called when setting an attribute.
536+ To make sure that a `` __setattr__ `` method always exists, the `` OBJECT `` class
537+ has a definition of `` __setattr__ `` . This base implementation simply does what
538+ setting an attribute did so far, which is write the attribute into the object's
539+ dictionary. This also makes it possible for a user-defined `` __setattr__ `` to
540+ call the base `` OBJECT.__setattr__ `` in some cases.
541+
542+ A test for these two special methods is the following:
543+
544+ ```` python
545+ def test_getattr ():
546+ # Python code
547+ class A (object ):
548+ def __getattr__ (self , name ):
549+ if name == " fahrenheit" :
550+ return self .celsius * 9 . / 5 . + 32
551+ raise AttributeError (name)
552+
553+ def __setattr__ (self , name , value ):
554+ if name == " fahrenheit" :
555+ self .celsius = (value - 32 ) * 5 . / 9 .
556+ else :
557+ # call the base implementation
558+ object .__setattr__ (self , name, value)
559+ obj = A()
560+ obj.celsius = 30
561+ assert obj.fahrenheit == 86 # test __getattr__
562+ obj.celsius = 40
563+ assert obj.fahrenheit == 104
564+
565+ obj.fahrenheit = 86
566+ assert obj.celsius == 30 # test __setattr__
567+ assert obj.fahrenheit == 86
568+
569+ # Object model code
570+ def __getattr__ (self , name ):
571+ if name == " fahrenheit" :
572+ return self .read_attr(" celsius" ) * 9 . / 5 . + 32
573+ raise AttributeError (name)
574+ def __setattr__ (self , name , value ):
575+ if name == " fahrenheit" :
576+ self .write_attr(" celsius" , (value - 32 ) * 5 . / 9 .)
577+ else :
578+ # call the base implementation
579+ OBJECT .read_attr(" __setattr__" )(self , name, value)
580+
581+ A = Class(" A" , OBJECT , {" __getattr__" : __getattr__ , " __setattr__" : __setattr__ }, TYPE )
582+ obj = Instance(A)
583+ obj.write_attr(" celsius" , 30 )
584+ assert obj.read_attr(" fahrenheit" ) == 86 # test __getattr__
585+ obj.write_attr(" celsius" , 40 )
586+ assert obj.read_attr(" fahrenheit" ) == 104
587+ obj.write_attr(" fahrenheit" , 86 ) # test __setattr__
588+ assert obj.read_attr(" celsius" ) == 30
589+ assert obj.read_attr(" fahrenheit" ) == 86
590+ ````
591+
592+ To pass these tests, the `` Base.read_attr `` and `` Base.write_attr `` methods
593+ needs to be changed as follows:
594+
595+ ```` python
596+ class Base (object ):
597+ ...
598+
599+ def read_attr (self , fieldname ):
600+ """ read field 'fieldname' out of the object """
601+ result = self ._read_dict(fieldname)
602+ if result is not MISSING :
603+ return result
604+ result = self .cls._read_from_class(fieldname)
605+ if _is_bindable(result):
606+ return _make_boundmethod(result, self )
607+ if result is not MISSING :
608+ return result
609+ meth = self .cls._read_from_class(" __getattr__" )
610+ if meth is not MISSING :
611+ return meth(self , fieldname)
612+ raise AttributeError (fieldname)
613+
614+ def write_attr (self , fieldname , value ):
615+ """ write field 'fieldname' into the object """
616+ meth = self .cls._read_from_class(" __setattr__" )
617+ return meth(self , fieldname, value)
618+ ````
619+
620+ Reading an attribute is changed to call the `` __getattr__ `` method with the
621+ fieldname as an argument instead of raising an error, if the method exists. Note
622+ that `` __getattr__ `` (and indeed all special methods in Python) is looked up on
623+ the class only, instead of recursively calling
624+ `` self.read_attr('__getattr__') `` . The reason for that is that the latter would
625+ lead to an infinite recursion of `` read_attr `` if `` __getattr__ `` is not defined
626+ on the object.
627+
628+ Writing an attribute is fully deferred to the `` __setattr__ `` method. To make
629+ this work, `` OBJECT `` needs to have a `` __setattr__ `` method that calls the
630+ default behaviour, as follows:
631+
632+ ```` python
633+ def OBJECT__setattr__ (self , fieldname , value ):
634+ self ._write_dict(fieldname, value)
635+ OBJECT = Class(" object" , None , {" __setattr__" : OBJECT__setattr__ }, None )
636+ ````
637+
638+ The behaviour of `` OBJECT__setattr__ `` is like the previous behaviour of
639+ `` write_attr `` . With these modifications, the new test passes.
511640
512641
513- - concretely: override what reading and writing an attribute means (__ getattr__
514- and __ setatttr__ )
515642- override what binding a "method" means with __ get__
516643
517644
@@ -678,7 +805,7 @@ at a fixed offset, getting rid of all dictionary lookups completely.
678805Potential Extensions
679806----------------------
680807
681- - support for constructors and __ new __
808+ - support for constructors, __ getattribute __ , __ set __
682809- Distinction between implementation code and user code
683810- More meta methods
684811- Multiple inheritance (easy!)
0 commit comments