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