[sage-devel] Re: A weiredness in Sage 5.0rc0

2012-05-03 Thread Nils Bruin
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

2012-05-03 Thread Simon King
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

2012-05-03 Thread Julien Puydt
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

2012-05-03 Thread Keshav Kini
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

2012-05-03 Thread Simon King
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

2012-05-03 Thread Keshav Kini
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

2012-05-03 Thread Simon King
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

2012-05-03 Thread Kwankyu Lee
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

2012-05-02 Thread Simon King
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

2012-05-02 Thread Simon King
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

2012-05-02 Thread Kwankyu Lee
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

2012-05-02 Thread Simon King
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