list element of a class with the for statement

Jim Dennis jimd at vega.starshine.org
Tue Mar 26 18:22:57 EST 2002


In article <mailman.1017132816.23221.python-list at python.org>, 
  cedric_briner wrote:

> hi,
> I'm interesting to know which method of a class I have to define to
> list the __records of this class:

> class CRdb:
>	__fieldsName= []
>	__keyname= []
>	__records   = ['first','secnd','third']
>	__iRecord= 0

> because I would like to write something like:

> db=CRdb()
> for entry in db:
>	print entry

> and then get
> first
> secnd
> third

> please let me know any pointer refering this topic...or even better a
> quick solution ..

 Topics: read "What's new in Python 2.2" by A.M. Kuchling
 at: http://www.amk.ca/python/2.2/index.html
 and pay particular attention to the discussion of "iterators."
 in section 3 
 
 (amusingly referred to by a URL referencing section 00040*; probably 
 an artifact of the editing process and the use of LaTeX2HTML):

 http://www.amk.ca/python/2.2/index.html#SECTION000400000000000000000

 Meanwhile I wrote a long answer before I actually understood what
 you were asking.  The "quick solution" is all the way at the bottom.
 
 I was lead astray by the fact that your CRdb class here shows only
 some class members with no methods and no instance members (which
 would normally be initialized by an object's __init__() method.
 So I rambled on at great length about using class methods and
 properties to manage your class members (and a little bit about the
 effect of using __ prefixes on those member names, in any event).

> thanks. Briner

 You've created a class with a set of "class members" (as opposed
 to "instance members").  By using the __ precedents in these names
 you asked Python to "mangle" them for you as shown in this transcript
 from an interactive Python session:

 >>> class CRdb:
 ...     __fieldsName = []
 ...     __keyname = []
 ...     __records = [ 1, 2, 3, 4 ]
 ...     __Irecord = 0
 ...
 >>> CRdb.__dict__
   {'_CRdb__records': [1, 2, 3, 4], '__module__': '__main__',
   '_CRdb__Irecord': 0, '_CRdb__fieldsName': [], '__doc__': None,
   '_CRdb__keyname': []}
 >>>
 >>> class CRdb:
 ...     __fields = []
 ...     __keyname = []
 ...     __records = [ 1, 2, 3, 4 ]
 ...     Rdb)
 ['_CRdb__Irecord', '_CRdb__fieldsName', '_CRdb__keyname', '_CRdb__records',
 '__doc__', '__module__']
 >>> dir(CRdb)
 ['_CRdb__Irecord', '_CRdb__fieldsName', '_CRdb__keyname', '_CRdb__records',
 '__doc__', '__module__']
 >>> 

 This mangling is primarily of interest in cases where you might
 subclass CRdb (since the _CRdb__* members will retain their mangled
 names in 
 

 >>> class subCRdb(CRdb):
 ...    pass
 ...
 >>> dir(subCRdb)
 ['_CRdb__Irecord', '_CRdb__fieldsName', '_CRdb__keyname', '_CRdb__records',
 '__doc__', '__module__']
 >>>

 So, to access these members from outside the class you could simply
 use:

 CRdb._CRdb__FieldsName

 ... or any of the other name that you used in your class.

 However, that's considered poor OOP since it violates the intention
 of encapsulating access to the members through class/instance methods.

 Under Python 2.1 (?) and ealier it was not possible to create 
 class methods.  All functions had to be *instance* methods (they
 *had* to be presented with a "self" argument as their first parameter).

 If you added the following to your class definition:

 class CRdb:
 	# ... (your extant members)
	def setFields(self,fields)
		self.__fieldsName = fields
	def getFields(self)
		return self.__fieldsName 

 ... then you'd be adding "get" and "set" methods to your class, which
 could be used through any instance.  So to call these you'd need to 
 instantiate from CRdb like so:

 inst = CRdb()
 inst.getFields()
 inst.setFields(['one','two','three'])

 ... and so on.

 In Python 2.2 (?) some features have been added that could be used 
 to create class methods (so that you could rewrite the getFields 
 method such that it didn't require a "self" argument, and you could 
 call CRdb.getFields() directly, without instantiating and going through
 one of the instances.  

 Here's an example:

 >>> class tst(object):
 ...    __foo = 0
 ...    def setfoo(cls,val):
 ...       cls.__foo = val
 ...    setfoo = classmethod(setfoo)
 ... 
 >>> tst._tst__foo
 0
 >>> tst.setfoo(1000)
 >>> tst._tst__foo
 1000
 >>> 

 ... notice that, in order to bind classmethods (or staticmethods, 
 not shown here) into my class, I have to subclass one of the Python
 2.2 "new style" classes; such as the archetypical "object."
 A class method takes a class reference as its first argument 
 (which is analogous to the "self" reference we'd expect in instance
 methods).  The tricky part is how we have to rebind our class method
 by sort of passing it through a "classmethod()" factory.  I don't
 fully understand what this does, but it allowed Guido et al to 
 make this change without any fundamental syntactic change to the 
 core language.  
 
 We don't need a new keyword and/or changes to the parser since 
 it was never a syntax error to define methods in classes with no 
 references to "self."  (Any attempt to use such a method in a 
 "classical (old style) class" was doomed, since the arguments 
 would be all wrong, but that was a runtime issue, not a parsing
 or syntactical problem per se).

 So where have we come:  
 
 You can access the members of your class directly by applying our 
 a priori knowledge of how the names are mangled.  This would be 
 considered "bad practice (TM)" by OOP purists (and in fact it's 
 tacky enough that I have to cry foul even though I'm a bit of an 
 anti-OOP neanderthal).  
 
 You can create methods to set and get these members through instances 
 (but you need to instantiate and go through them).  That seems 
 inelegant, since if "feels" like you're going though the back
 door to get to the class members.  But it's the only way for earlier
 versions of Python (other than the "even-more-uglier-ly" technique
 of reaching in from outside the class entirely).

 You can subclass "object" and use class methods (under 2.2 or later).

 But that's not all.  You might also be able to use another classy new 
 Python 2.2 feature called "properties" to manage access to your
 class (or instance) members.

 Here's an example of that:

 >>> class tst(object):
 ...   def getfoo(cls):
 ...     return cls.__foo
 ...   getfoo = classmethod(getfoo)
 ...   def setfoo(cls,val):
 ...     cls.__foo = val
 ...   setfoo = classmethod(setfoo)
 ...   __foo = property(getfoo, setfoo, None, "tst class' foo member")
 ...
 >>> tst._tst__foo
 <property object at 0x806f754>
 >>> tst.getfoo()
 <property object at 0x806f754>
 >>> tst.setfoo("tst")
 >>> tst.getfoo()
 'tst'
 >>>

 The important points above are:

 	I define a pair of functions (using the convention "get" and "set"
	names) to manipulate __foo.  I then make them classmethods.
	Now I use the property "factory" to create my member *and* to
	bind it to a set of "accessors" (get, set, and del functions).
	In this example I'm binding the property "__foo" to the 
	accessors "getfoo" and "setfoo" (following the common naming
	conventions), and I've omitted binding it to a delete function.
	(I have also associated __foo with a doc string, as shown).

 In my example I see that tst._tst__foo is initially bound to 
 a "property object."  I see that accessing it via the getfoo()
 class method returns a reference to exactly the same object.  In 
 practice I might want to set an initial value to such a member 
 (in my class definition) since I don't know what happens if other
 code encounters this "property object" (by access tst.getfoo()
 before any tst.setfoo() calls).   Oddly enough I found that I couldn't
 just add: tst.setfoo(0) to my class definition (I still get the 
 property references from an initial "tst.getfoo()" call).  However,
 adding a line like: __foo = 0 (or whatever) after the call to
 __foo = property(...) does seem to work.  __foo then contains a 
 value *and* it retains its special "properties" of being accessible
 via the get and set methods that I bound to it.

 So, that answers a question which is implicit to yours: how can you the 
 the class members at all?  Now on to your real question: how can you 
 define an iterator for instances of your class, and bind that iterator
 to a specific member sequence?

 At this point I have to assume that your pasted code is completely 
 bogus.  It would make no sense to create __records as a *class*
 member.  I must assume that you really want it to be an *instance*
 member (which would presumably be instantiated or bound through 
 your __init__() method).  (Silly me!  I spend all of that time 
 explaining how to work with "private" (__mangled) *class* members 
 before understanding your question).

"Iterators" are another new Python 2.2 feature.  
Here's a trivial example (similar to the one you'd want in your code):

 class tstIter:
 	# Notice, works with "classical" classes 
	# and with "classy" new classes
 	__foo = [1,2,3,4,5,6,7]
  	def __iter__(self):
		return iter(self.__foo)

 Now it is possible to say:

 	stoopid_example = tstIter()
	for each in stoopidexample:
		print each

 ... which seems straightforward enough.  However, it probably
 make no sense in reality.  You probably need for __records to be
 an *instance* variable (member).  That would look a little more
 like:


 class CRdb:
	__fieldsName= []
	__keyname= []
	__iRecord= 0
	def __init__(self, recs=None):
		if recs:
			self.__records = recs
		else:
			self.__records = [] 
	def __iter__(self):
		return iter(self.__records)

 That (minimally) would allow your sample code to run as you 
 *probably* intended it.

 Of course you might want to combine this with a set of accessor
 functions to ensure that self.__records is always some sort of 
 sequence.  You could write get* and set* functions and bind them
 to self.__records with a call to property()

 I'll leave all of that as an exercise.





More information about the Python-list mailing list