Can __iter__ be used as a classmethod?

Alex Martelli aleax at aleax.it
Tue Mar 4 22:45:28 CET 2003


Giovanni Bajo wrote:

> 
> "Alex Martelli" <aleax at aleax.it> ha scritto nel messaggio
> news:b42ev802l51 at enews4.newsguy.com...
> 
>> Sure, let's -- no "classic classes" in the following (and, feel free
>> to switch the discussion to it.comp.lang == with [Python] at the start
>> of the subject == if discussing this in Italian will make it easier for
>> you... otherwise, keeping it on c.l.py does seem preferable).
> 
> Is my english that bad? :)

Not at all!  Sorry for the unintended implication...

> It's ok to keep it in here, thanks anyway for the offer.
> 
> Your reply was pretty clear and exhaustive. I think the main problem is
> that I was thinking of iter(B()) as a different way of calling
> B().__iter__ (usually 'inherited' from B.__iter__), and not
> type(B).__iter__. Everything makes more sense now.

OK.  It _used_ to be that way with classic classes, btw, but that
just didn't generalize well.


> I still have a small doubt by classmethod(). Let's consider this:
> 
> class A:

Better inherit it from object for clarity (or set __metaclass__ to type),
although offhand I think all you say below holds for classic classes too,
we did agree not to consider that source of potential complication anyway.

>     def f(cls):
>         pass
>     f = classmethod(f)
> 
>     def g(self):
>         pass
> 
> a = A()
> 
> Now, in my current understanding, the differences between A.f and A.g are:
> 
> 1) A.f() is a valid call (and the function will receive class A as
> parameter), while A.g() will raise a runtime exception (unbound method).

Yes.

> A.g(a) is correct because it is equivalent to a.g().

Yes again.

> 2) a.f() is a valid call (and the function will receive class A, aka
> type(a), as parameter); a.g() is a valid call as well, and the function
> will receive a as parameter

And yes to this too.

> Also, both are attributes of A and not of a. They can be used on a because
> they are automatically looked up on type(a), but they belong to A.

Correct.

> Another source of confusion in my mind is that type(A.g) is called
> "instance method", because it must be bound to an instance of the class,
> to become 'callable'. But it does not 'belong' to the instance, it is
> actually defined in A.__dict__, not in A().__dict__.

Yes, but the point is: type(A.g) is type(A().g) -- bound and unbound
methods are the SAME type, only the value of their im_self attribute
distinguishes them (None for the unbund, instance for the bound one).


>> > mean that rebinding B().__iter__ has no effect since B.__iter__ is
> always
>> > looked up when iterating over instances of B(), while B().f() can be
>> > freely rebound?
>>
>> It's not true that rebinding name '__iter__' on an instance has NO
>> effect --
> 
> Yes, it should have been 'has no effect with respect to iterating over B
> instances'.

Yep, this way it's correct.


>> it does affect calls DIRECTLY to thatinstance.__iter__, AND
>> iterations on INSTANCES of that instance (if, of course, that instance
>> is usable and gets used as a type. AKA class).  But it has no effect
>> on ITERATIONS on that instance, or calls to iter(...) with that
>> instance as their argument.
> 
> So, if I rebind B().__iter__, Python will use the rebound function when
> calling "for x in B().__iter__()" (assuming that 'B()' always refers to
> the same instance of course) but it will not change "for x in B", which is
> semantically the same of "for x in iter(B)", or "for x in
> type(B).__iter__()" (maybe modulo obscure situations that I can ignore for
> now).

Right, I think, but you should give names to things for clarity, e.g.:
after
   x = B()
rebinding x.__iter__ does effect any further EXPLICIT use of x.__iter__,
but NOT iter(x) and "for a in x:" (much less, iter(B) and "for a in B:",
i.e. iteration on the class object itself).


>> [snip]
>> This
>> may help explain why inheritance is not transitive AND why the
>> operations and built-ins access special attributes on the TYPES
>> of their arguments, not on their arguments themselves.
> 
> Yes, I understand very well why 'inheritance' cannot be transitive. By the
> way, it seems that the word 'inheritance' has been abused in this context:

Maybe, but it's what Lutz uses in "Learning Python" and I do think
it's not bad usage.  A lookup "x.goo" delegates to type(X).goo when
goo does not name an attribute in x itself, "just as" a lookup of
type(X).goo delegates to type(X).__bases__[0].goo &c -- well, it IS
deucedly close.

> this is not the normal inheritance as in class A(B), but something
> different (A() will lookup attributes from A if they have not been
> rebound, and A will lookup attributes from A.__metaclass__ -- more
> generically, A will lookup attributes in type(A)). Is there a more correct
> word for this? 'meta-inheritance'? :)

"inheritance from type(x)" is admittedly not quite the same as
"inheritance from my __bases__" (for example, the latter DOES apply
perfectly well to automatic lookup of special methods, and it IS
transitive while the former isn't).  But both mechanisms boil down,
90% or so, to delegating lookup of attribute names not found in
the object itself, so that using the same term for both does not
seem an "abuse" to me -- it seems quite sensible, though having a
way to distinguish the subtle differences would surely also help.


> Thanks again for your time

You're welcome!


Alex





More information about the Python-list mailing list