- E04 - Leadership! Google, Guido van Rossum, PSF

Alex Martelli aleax at mail.comcast.net
Tue Jan 3 21:47:10 EST 2006


Ilias Lazaridis <ilias at lazaridis.com> wrote:
   ...
> > One normally does not define large numbers of identical accessors (there
> [...] - (extensive elaboration)
> 
> possibly one can provide the code for something similar to the ruby 
> attr_accessor:
> 
> class Talker
>    def sayHello
>      puts "Hello world"
>    end
> 
>    attr_accessor :name, :age
> 
> end
> 
> thus they can later be accessed this way
> 
> john.age = 19
> 
> print john.age

Yes: the amount of code one needs to provide for this purpose is, NONE.

class Talker(object):
  pass

You can now write

john = Talker()
john.age = 19
print john.age

just as you request.  _No need to provide ANY code in the class_.

If you want an initial/default value for the .age parameter, you'll want
to add an __init__ setting it (otherwise, trying to print john.age
WITHOUT having previously set john.age will produce an AttributeError --
I wish all languages were so nice as to similarly let me know about
attempts to use uninitialized variables/attributes!-).

But still, you don't need any accessor methods -- setters and getters --
unless there's something "real" that you want code to perform upon any
setting and/or getting of an attribute (when there IS something special
of that kind, and only then, you code setter and/or getter and use
property, but then it won't be REPETITIVE [boilerplate] code, because
there WILL be something special in those methods you're coding...).

BTW, all I say about setting and getting an attribute also applies to
REMOVING an attribute, aka DELETING it.  Dunno why nobody ever seems to
think about it, but it should be possible to have some attributes that
are optional, and thus to remove them if/when they're not needed any
more.

Let me give an example: say that anybody under 18 must have a legal
guardian, but there is no concept of legal guardian for anybody who is
18 or over; then, you might have:

class Person(object):
  def __init__(self, name, parent):
    # the person is born: age 0, given name, legal guardian is parent
    self._age = 0
    self.name = name
    self.guardian = parent
  def getAge(self):
    return self._age
  def setAge(self, age):
    if hasattr(self, 'guardian') and age>=18: del self.guardian
    self._age = age
  age = property(getAge, setAge)

This is a good example of a case in which you need to run some code when
john.age is set, because if it's set to 18 or over you want to remove
john's attribute defining his legal guardian -- so, you use a property.
You don't need properties for the name and guardian attributes, because
so far at least we have not specified any code that needs to run when
those attributes are gotten, set, or deleted; if and when the specs
changes, you can change the definition of the class and NOT change any
client-code, because the client code still uses the attributes in
exactly the same way, e.g. "john.age += 1", whether a corresponding
property is defined, or not.


> > instance and introspecting on it.  Would such instantiation be OK here?
> 
> If I understand you right, it would be ok.
> 
> The requirements are given by the template:
> 
> john.sayYourClassDefinition()
> 
> "john" is instantiated, when asked for his class definition.

OK, I'll look into that in my copious spare time (unless somebody else
does the work first;-).


> >>"assign to it" with:
> >>
> >>setattr(Talker, 'meta', "Class meta information")
> >>
> >>but _not_ with this:
> >>
> >>Talker.meta = "Class meta information"
> >>
> >>correct?
> > 
> > Nope: both forms have IDENTICAL semantics.  They both work in just the
> > SAME way.
> > 
> > Try it out...!
> 
> But this means that "assignment of metadata" works fine.

Yes, on mutable objects such as ordinary classes it does work fine.  It
would not work on immutable objects such as strings or numbers.


> >>>>class Talker(object): pass
> > 
> > ... 
> > 
> >>>>Talker.meta = 'class metainfo'
> >>>>print Talker.meta
> > 
> > class metainfo
> 
> thus if I make a typo, I create a new attribute?

Yep, just like, I believe, in Ruby; if you meant to assign to @zappo but
happen to mistakenly assign to @zippo instead, you've created a new
attribute.  In Python, if you wish, you can check for such "oops"-level
errors by using tools such as pychecker or pyLint, which of course can
also check for other "oopses" besides "variable/attribute created once
but never used nor referenced".  I've seen some people program their
editors to routinely run pychecker when saving files with a .py
extension, for example.

Personally, I don't generally bother with pychecker (even though its
most excellent author is my friend and colleague Neal Norwitz) because
compilers and lint tools just can't catch ALL of my "oopses" -- for
example, if I meant to write count+=1 but made a typo and wrote count-=1
instead, no checker/lint/compiler is gonna catch it for me.  So, I have
learned that I need to write *UNIT-TESTS* for all of my code -- me and
another million programmers.  Of course, unit tests, which are
indispensable anyway to catch the += vs -= typoes, as a side effect also
catch any typos such as zippo vs zappo.  Very good expansions on these
fundamental ideas can be seen at
<http://www.mindview.net/WebLog/log-0025> and
<http://www.artima.com/weblogs/viewpost.jsp?thread=4639>, by excellent
authors Bruce Eckel and Robert Martin respectively (great experts of
such languages as Java and C++, but aficionados of Python, Ruby,
Smalltalk thanks to these considerations).


Alex



More information about the Python-list mailing list