Can __iter__ be used as a classmethod?
Alex Martelli
aleax at aleax.it
Tue Mar 4 16:45:28 EST 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