[sage-devel] Re: A weiredness in Sage 5.0rc0
On May 3, 4:47 am, Keshav Kini wrote: > If such conditions exist (necessitating the calling of the parent > initializer at the end of the child initializer rather than at the > beginning) then the parent types are pretty clearly "abstract base > classes", in OOP terminology -- i.e. classes which cannot directly be > instantiated, because if they were, their __init__() would fail, being > called before anything else. I think what you are witnessing here is that in Python, construction of objects happens in two steps: First they are instantiated and then they are initialized. Instantiation indeed necessarily happens in reverse MRO. Initialization happens after that and, as you see, happens as an ordinary method lookup. The instantiation of the child has already run after the instantiation of the parent, so the object is in a state knowable to the child but not to the parent. As in the example given: The _repr_ method has been overridden to a form that cannot run yet because the new _repr_ method needs an extra attribute that needs to be set by the child's __init__ This doesn't make the parent into an abstract class. It just means that the child's instantiation has put the object in an inconsistent state (having a new _repr_ without having the requisite attributes) and the child better fix that in its __init__ before attempting to call the parent's __init__. Of course, the child could decide to modify its state even further after the parent has done its __init__, in which case the call would be neither at the start nor at the end. I think you'd unnaturally and unnecessarily restrict yourself if you would require a particular place where parent's __init__s should be called. -- To post to this group, send an email to sage-devel@googlegroups.com To unsubscribe from this group, send an email to sage-devel+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/sage-devel URL: http://www.sagemath.org
[sage-devel] Re: A weiredness in Sage 5.0rc0
Hi Julien On 3 Mai, 16:02, Julien Puydt wrote: > The parent class cannot expect anything if it's initialization code > hasn't been called, can it? Apparently both the string representation and the hash *are* available before calling init: sage: P_init = Parent(category=Rings()) sage: P_init sage: hash(P_init) 6922428270616034436 sage: P_no_init = Parent.__new__(Parent, category=Rings()) sage: P_no_init sage: hash(P_no_init) 6922428270616034436 Here, I demonstrate that P_no_init is really not initialised: sage: P_init._is_category_initialized() True sage: P_no_init._is_category_initialized() False So, the result is the same with and without initialisation, and indeed the hash *is* called before the initialisation is finished. Thank you for the links! Cheers, Simon -- To post to this group, send an email to sage-devel@googlegroups.com To unsubscribe from this group, send an email to sage-devel+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/sage-devel URL: http://www.sagemath.org
Re: [sage-devel] Re: A weiredness in Sage 5.0rc0
Le jeudi 03 mai, Simon King a écrit: > On the other hand: One could argue that the parent class can > expect that some basic tools (such as __hash__ and __repr__) already > work when being called. Hence, before calling __init__ of the parent > class, one is supposed to make sure that the "tool chain" is provided. The parent class cannot expect anything if it's initialization code hasn't been called, can it? > And one could argue that a commutative algebra is, in the *first* > place, a commutative algebra, then in the *second* place it is an > algebra, which is a module and a commutative ring (which then is a > ring), which are parents. From that point of view, one would expect > that the initialisation goes from the most specific to the most > general - hence, *parallel* to the mro. No, a commutative algebra is first an algebra, and then has some additional properties. I never saw a book define them in the reverse order... > I simply don't know if there is any convention established (by that I > mean: Documented, and I'd like to see a link) among Python developers, > or at least among Sage developers. Beware of conventions among sage developers : prefer conventions which apply among many other opensource projects. Here are some links (the last one is about C++ but it's still interesting to know what is done elsewhere) : http://stackoverflow.com/questions/1385759/should-init-call-the-parent-classs-init http://stackoverflow.com/questions/6187042/inheritance-in-python-and-constructor-calls http://stackoverflow.com/questions/120876/c-superclass-constructor-calling-rules Snark on #sagemath -- To post to this group, send an email to sage-devel@googlegroups.com To unsubscribe from this group, send an email to sage-devel+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/sage-devel URL: http://www.sagemath.org
[sage-devel] Re: A weiredness in Sage 5.0rc0
Simon King writes: > Hi Keshav, > > On 2012-05-03, Keshav Kini wrote: >> Simon King writes: >>> No idea. Clearly, unless there is a good reason, the init method of the >>> base class should be called at some point. But I was not aware of a >>> convention whether the base init should be called first or last or >>> whatever. Does someone have a pointer? >> >> I think the reasoning behind calling the parent class's initializer >> before the child class's initializer is that the parent class doesn't >> know about the child class, so the parent class shouldn't be expected to >> successfully initialize something that has already been modified by the >> child class. On the other hand, the child class does know about its >> parent classes and can be designed to handle the result of whatever >> initialization the parent class has done. > > I figured that the argument for an initialisation that is anti-parallel > to the mro would be like that. Some special initialisation steps would, > for example, expect that self.variable_names() is present, which is only > available *after* calling Ring.__init__. Well, if you want to bring the MRO into it, I have a more direct reason why initialization should be done in an order opposite to the MRO. In the MRO, you are choosing which methods override the other methods, i.e. the first overloaded method in the MRO becomes the method chosen. However, when doing initialization of objects, it is the *last* initialization which has the final say on how the object is initialized. So it is natural that the order is the opposite of the MRO. > On the other hand: One could argue that the parent class can > expect that some basic tools (such as __hash__ and __repr__) already > work when being called. Hence, before calling __init__ of the parent > class, one is supposed to make sure that the "tool chain" is provided. If such conditions exist (necessitating the calling of the parent initializer at the end of the child initializer rather than at the beginning) then the parent types are pretty clearly "abstract base classes", in OOP terminology -- i.e. classes which cannot directly be instantiated, because if they were, their __init__() would fail, being called before anything else. Since we don't want to force all classes that might ever be subclassed to become abstract base classes, this doesn't make a good argument for a uniform choice of which order to initialize things in, and we should try to avoid causing initializers of any class to require something to be done before they are called. > And one could argue that a commutative algebra is, in the *first* place, > a commutative algebra, then in the *second* place it is an algebra, > which is a module and a commutative ring (which then is a ring), which > are parents. From that point of view, one would expect that the > initialisation goes from the most specific to the most general - hence, > *parallel* to the mro. As for what characteristics of the object are "more important", I don't think that is really relevant here... Anyway, I am totally ignorant of how all the hierarchical frameworks in Sage work (such as the category / coercion frameworks). I was just theorizing based on what I've seen in other code, and my own thoughts. Passing initialization to the parent at the end rather than the beginning might be the correct way to do it in Sage, I have no idea :) I don't recall seeing any instructions on the matter in the Sage docs, nor have I heard any kind of standards for Python in this matter -- though some stackoverflow questions I found on Google seem to suggest that sometimes people just don't call the parent initializer at all, or they duplicate code from it, or something. But I'm no expert. -Keshav Join us in #sagemath on irc.freenode.net ! -- To post to this group, send an email to sage-devel@googlegroups.com To unsubscribe from this group, send an email to sage-devel+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/sage-devel URL: http://www.sagemath.org
[sage-devel] Re: A weiredness in Sage 5.0rc0
Hi Keshav, On 2012-05-03, Keshav Kini wrote: > Simon King writes: >> No idea. Clearly, unless there is a good reason, the init method of the >> base class should be called at some point. But I was not aware of a >> convention whether the base init should be called first or last or >> whatever. Does someone have a pointer? > > I think the reasoning behind calling the parent class's initializer > before the child class's initializer is that the parent class doesn't > know about the child class, so the parent class shouldn't be expected to > successfully initialize something that has already been modified by the > child class. On the other hand, the child class does know about its > parent classes and can be designed to handle the result of whatever > initialization the parent class has done. I figured that the argument for an initialisation that is anti-parallel to the mro would be like that. Some special initialisation steps would, for example, expect that self.variable_names() is present, which is only available *after* calling Ring.__init__. On the other hand: One could argue that the parent class can expect that some basic tools (such as __hash__ and __repr__) already work when being called. Hence, before calling __init__ of the parent class, one is supposed to make sure that the "tool chain" is provided. And one could argue that a commutative algebra is, in the *first* place, a commutative algebra, then in the *second* place it is an algebra, which is a module and a commutative ring (which then is a ring), which are parents. From that point of view, one would expect that the initialisation goes from the most specific to the most general - hence, *parallel* to the mro. I simply don't know if there is any convention established (by that I mean: Documented, and I'd like to see a link) among Python developers, or at least among Sage developers. Best regards, Simon -- To post to this group, send an email to sage-devel@googlegroups.com To unsubscribe from this group, send an email to sage-devel+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/sage-devel URL: http://www.sagemath.org
[sage-devel] Re: A weiredness in Sage 5.0rc0
Simon King writes: > Hi Kwankyu, > > On 2012-05-03, Kwankyu Lee wrote: >>> > As the "..Algebra.__init__" is expected to be >>> > placed at the beginning of the initialization code >>> >>> Why? Is that a Python convention? >> >> >> Isn't that a convention of objected-oriented programming? > > No idea. Clearly, unless there is a good reason, the init method of the > base class should be called at some point. But I was not aware of a > convention whether the base init should be called first or last or > whatever. Does someone have a pointer? I think the reasoning behind calling the parent class's initializer before the child class's initializer is that the parent class doesn't know about the child class, so the parent class shouldn't be expected to successfully initialize something that has already been modified by the child class. On the other hand, the child class does know about its parent classes and can be designed to handle the result of whatever initialization the parent class has done. -Keshav Join us in #sagemath on irc.freenode.net ! -- To post to this group, send an email to sage-devel@googlegroups.com To unsubscribe from this group, send an email to sage-devel+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/sage-devel URL: http://www.sagemath.org
[sage-devel] Re: A weiredness in Sage 5.0rc0
Hi Kwankyu, On 2012-05-03, Kwankyu Lee wrote: >> > As the "..Algebra.__init__" is expected to be >> > placed at the beginning of the initialization code >> >> Why? Is that a Python convention? > > > Isn't that a convention of objected-oriented programming? No idea. Clearly, unless there is a good reason, the init method of the base class should be called at some point. But I was not aware of a convention whether the base init should be called first or last or whatever. Does someone have a pointer? > Perhaps another idea, that I could easily implement: We could ensure >> that calling ...Ring.__init__(..., category=False) would initialise the >> ring except for the category framework. Then, the third solution of your >> problem would be >> def __init__(self, i): >> CommutativeRing.__init__(self, i.base_ring(), category=False) >> self._ideal = i >> self._init_category_(CommutativeRings()) # or whatever is >> appropriate >> > > This is similar to my "manual" idea. It is similar, but note that the default is preserved: If category=None (which is the default) or category="some explicitly given category", then initialisation would still occur. Hence, one would postpone category initialisation (by manual intervention) only if the default causes problems. > Would that be acceptable to you? I am happy as long as the category is initialised by default. But perhaps that kind of question should be asked on sage-combinat-devel. A bit later, I'll ask there. Cheers, Simon -- To post to this group, send an email to sage-devel@googlegroups.com To unsubscribe from this group, send an email to sage-devel+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/sage-devel URL: http://www.sagemath.org
[sage-devel] Re: A weiredness in Sage 5.0rc0
Hi Simon, The result is that the hash is broken by default, since the string > representation can be changed *after* creation of the object, which > means that the hash value would change as well. There is a ticket fixing > it, though. > > > I think the problem lies in that the hash is calculated when the object > is > > not fully constructed. The hash value should be calculated as the last > part > > of the initialization process, as the hash should be dependent on the > data > > that defines the object. As the "..Algebra.__init__" is expected to be > > placed at the beginning of the initialization code > > Why? Is that a Python convention? Isn't that a convention of objected-oriented programming? > , I think there should be > > a separate method say "..Algebra._set_hash_" that is executed manually > or > > automatically at the end of the initialization process. > > If it is automatic, then it will probably be at the end of the > initialisation process of Parent.__init__ - but then the problem would > not be solved, because you insist to call Parent.__init__ (indirectly > via ...Ring.__init__) before the initialisation of your object is > completed. > > If it is manual and *has* to be done, then obviously a common > mistake would result, namely to forget initialising the hash. > Yeah, the problem is not easy to solve... Perhaps another idea, that I could easily implement: We could ensure > that calling ...Ring.__init__(..., category=False) would initialise the > ring except for the category framework. Then, the third solution of your > problem would be > def __init__(self, i): > CommutativeRing.__init__(self, i.base_ring(), category=False) > self._ideal = i > self._init_category_(CommutativeRings()) # or whatever is > appropriate > This is similar to my "manual" idea. Would that be acceptable to you? > This seems to be a good solution for all, not just for me. :-) > > Cheers, > Simon > > -- To post to this group, send an email to sage-devel@googlegroups.com To unsubscribe from this group, send an email to sage-devel+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/sage-devel URL: http://www.sagemath.org
[sage-devel] Re: A weiredness in Sage 5.0rc0
On 2012-05-03, Simon King wrote: > Then, the third solution of your > problem would be > def __init__(self, i): > CommutativeRing.__init__(self, i.base_ring(), category=False) > self._ideal = i > self._init_category_(CommutativeRings()) # or whatever is appropriate Oops, I mean CommutativeAlgebra.__init__(...) and CommutativeAlgebras(i.base_ring()), of course. -- To post to this group, send an email to sage-devel@googlegroups.com To unsubscribe from this group, send an email to sage-devel+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/sage-devel URL: http://www.sagemath.org
[sage-devel] Re: A weiredness in Sage 5.0rc0
Hi Kwankyu, On 2012-05-03, Kwankyu Lee wrote: > I am not against using the string representation to compute the hash, as > the last resort. The result is that the hash is broken by default, since the string representation can be changed *after* creation of the object, which means that the hash value would change as well. There is a ticket fixing it, though. > I think the problem lies in that the hash is calculated when the object is > not fully constructed. The hash value should be calculated as the last part > of the initialization process, as the hash should be dependent on the data > that defines the object. As the "..Algebra.__init__" is expected to be > placed at the beginning of the initialization code Why? Is that a Python convention? > , I think there should be > a separate method say "..Algebra._set_hash_" that is executed manually or > automatically at the end of the initialization process. If it is automatic, then it will probably be at the end of the initialisation process of Parent.__init__ - but then the problem would not be solved, because you insist to call Parent.__init__ (indirectly via ...Ring.__init__) before the initialisation of your object is completed. If it is manual and *has* to be done, then obviously a common mistake would result, namely to forget initialising the hash. Perhaps another idea, that I could easily implement: We could ensure that calling ...Ring.__init__(..., category=False) would initialise the ring except for the category framework. Then, the third solution of your problem would be def __init__(self, i): CommutativeRing.__init__(self, i.base_ring(), category=False) self._ideal = i self._init_category_(CommutativeRings()) # or whatever is appropriate Would that be acceptable to you? Cheers, Simon -- To post to this group, send an email to sage-devel@googlegroups.com To unsubscribe from this group, send an email to sage-devel+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/sage-devel URL: http://www.sagemath.org
[sage-devel] Re: A weiredness in Sage 5.0rc0
Hi Simon, Thank you for the explanation. However, during the category initialisation, self is used as key in > some cache. Hence, it is needed that its hash value is available. By > default in sage.rings.ring.Ring, hash(self) is the self as > hash(repr(self)) > (which I don't like, by the way). > I am not against using the string representation to compute the hash, as the last resort. > There are two possible solutions: Either do > def __init__(self, i): > self._ideal = i > CommutativeAlgebra.__init__(self, i.base_ring()) > or define a __hash__ method that does not rely on the string > representation. > > The question is whether a meaningful hash is available before knowing > self._ideal. Hence, I recommend the first solution. > The first solution looks ugly. I think the problem lies in that the hash is calculated when the object is not fully constructed. The hash value should be calculated as the last part of the initialization process, as the hash should be dependent on the data that defines the object. As the "..Algebra.__init__" is expected to be placed at the beginning of the initialization code, I think there should be a separate method say "..Algebra._set_hash_" that is executed manually or automatically at the end of the initialization process. -- To post to this group, send an email to sage-devel@googlegroups.com To unsubscribe from this group, send an email to sage-devel+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/sage-devel URL: http://www.sagemath.org
[sage-devel] Re: A weiredness in Sage 5.0rc0
Hi Kwankyu, On 2012-05-03, Kwankyu Lee wrote: > In Sage 5.0rc0, I observe a weird phenomenon. See the following code > - > But the following modified code results in an error. The difference is the > addition of the method "_repr_". > - > from sage.rings.ring import CommutativeAlgebra > > class NA(CommutativeAlgebra): > """ > """ > > def __init__(self, i): > """ > """ > CommutativeAlgebra.__init__(self, i.base_ring()) > self._ideal = i > > def _repr_(self): > return "NA defined by %s." % self._ideal The reason is that in sage-5.0, all rings will (should, at least) have proper initialisation required to make the category framework work. That means, self._init_category is indirectly called in the line CommutativeAlgebra.__init__(self, i.base_ring()) However, during the category initialisation, self is used as key in some cache. Hence, it is needed that its hash value is available. By default in sage.rings.ring.Ring, hash(self) is the self as hash(repr(self)) (which I don't like, by the way). But that happens *before* you set self._ideal, thus, the crash. There are two possible solutions: Either do def __init__(self, i): self._ideal = i CommutativeAlgebra.__init__(self, i.base_ring()) or define a __hash__ method that does not rely on the string representation. The question is whether a meaningful hash is available before knowing self._ideal. Hence, I recommend the first solution. Cheers, Simon . -- To post to this group, send an email to sage-devel@googlegroups.com To unsubscribe from this group, send an email to sage-devel+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/sage-devel URL: http://www.sagemath.org