UniqueRepresentation inheritance and __classcall__

76 views
Skip to first unread message

VulK

unread,
Sep 25, 2019, 6:22:23 PM9/25/19
to sage-devel
Dear All,
I would like to make a new class inheriting from a class based on
UniqueRepresentation. My goal, among other things, is to preparse the
arguments a little before calling the class I am inheriting from.
Unfortunately this class also does some preparsing via __classcall__ to make
the input hashable. Could you point me to the correct way of setting this up?

Sketch of the situation:

class OldFoo(UniqueRepresentation):

@staticmethod
def __classcall__(self, data):
# make data hashable
return super(oldFoo, self).__classcall__(self, hashable_data)

def __init__(self, hashable_data):
# do something

class NewFoo(OldFoo):

# First neutralize OldFoo __classcall__
__classcall__ = None

def __init__(self, data):
# preparse data then initialize OldFoo


If __classcall__ were not there I would call OldFoo.__init__ but this does
not seem to work with OldFoo.__classcall__


Thanks
S.



Travis Scrimshaw

unread,
Sep 25, 2019, 7:55:56 PM9/25/19
to sage-devel
Make OldFoo.__classcall_private__, then it is not inherited.

Best,
Travis

Travis Scrimshaw

unread,
Sep 25, 2019, 7:59:13 PM9/25/19
to sage-devel
Although it almost sounds like you should be doing the NewFoo preparsing inside NewFoo.__classcall__ (or NewFoo.__classcall_private__), which then should get passed up and handled by OldFoo.__classcall__. You might need some extra *args or **kwds to the __init__ and/or __classcall__ to handle this. The other option would be to separate out the preparsing portion into a separate @classmethod or @staticmethod.

Best,
Travis

VulK

unread,
Sep 26, 2019, 7:04:09 AM9/26/19
to sage-...@googlegroups.com
Thank you Travis for your reply. Here is an attempt at implementing your
suggestion that does not work: it looks like OldFoo.__classcall__ is skipped
and OldFoo.__init__ is run directly instead. This is not what I want: I would
like the preparsing in OldFoo to still be performed after the preparsing in
NewFoo

class OldFoo(UniqueRepresentation):

@staticmethod
def __classcall__(self, data, **kwargs):
hashable_data = tuple(data)
kwargs['some_default_option'] = 'bar'
return super(OldFoo, self).__classcall__(self, hashable_data, **kwargs)

def __init__(self, data, **kwargs):
self._data = data
self._some_default_option = kwargs['some_default_option']

def __repr__(self):
return 'Data = %s, option = %s'%(self._data, self._some_default_option)

class NewFoo(OldFoo):

@staticmethod
def __classcall__(self, data, **kwargs):
new_data = data+data
return super(OldFoo, self).__classcall__(self, new_data, **kwargs)


If you try this code it runs as follows:

sage: OldFoo([0,1,2,3])
Data = (0, 1, 2, 3), option = bar
sage: NewFoo([0,1,2,3])
...
TypeError: unhashable type: 'list'
sage: NewFoo((0,1,2,3))
...
KeyError: 'some_default_option'


Thanks
S.


* Travis Scrimshaw <tsc...@ucdavis.edu> [2019-09-25 16:59:13]:
>--
>You received this message because you are subscribed to the Google Groups "sage-devel" group.
>To unsubscribe from this group and stop receiving emails from it, send an email to sage-devel+...@googlegroups.com.
>To view this discussion on the web visit https://groups.google.com/d/msgid/sage-devel/5475d883-8c54-4a8b-8fb5-f33859e464a3%40googlegroups.com.

VulK

unread,
Sep 26, 2019, 10:42:23 AM9/26/19
to sage-...@googlegroups.com
Oops, I think that I figured out where the mistake was. Here is the correct
code:

class OldFoo(UniqueRepresentation):

@staticmethod
def __classcall__(self, data, **kwargs):
hashable_data = tuple(data)
kwargs['some_default_option'] = 'bar'
return super(OldFoo, self).__classcall__(self, hashable_data, **kwargs)

def __init__(self, data, **kwargs):
self._data = data
self._some_default_option = kwargs['some_default_option']

def __repr__(self):
return 'Data = %s, option = %s'%(self._data, self._some_default_option)

class NewFoo(OldFoo):

@staticmethod
def __classcall__(self, data, **kwargs):
new_data = data+data
return super(NewFoo, self).__classcall__(self, new_data, **kwargs)


Thanks
S.



* Travis Scrimshaw <tsc...@ucdavis.edu> [2019-09-25 16:59:13]:

Travis Scrimshaw

unread,
Sep 26, 2019, 6:11:07 PM9/26/19
to sage-devel
So it works now, correct?

Best,
Travis
>To unsubscribe from this group and stop receiving emails from it, send an email to sage-...@googlegroups.com.

VulK

unread,
Sep 26, 2019, 6:14:07 PM9/26/19
to sage-...@googlegroups.com
The prototype does, now I have to implement the real thing.

Is there any way to add this info to the documentation? It took me a lot of
trial and error to figure out how to do what I wanted.
S.


* Travis Scrimshaw <tsc...@ucdavis.edu> [2019-09-26 15:11:07]:
>> * Travis Scrimshaw <tsc...@ucdavis.edu <javascript:>> [2019-09-25
>> email to sage-...@googlegroups.com <javascript:>.
>--
>You received this message because you are subscribed to the Google Groups "sage-devel" group.
>To unsubscribe from this group and stop receiving emails from it, send an email to sage-devel+...@googlegroups.com.
>To view this discussion on the web visit https://groups.google.com/d/msgid/sage-devel/869947d6-8388-4455-96a2-75550bad4dd1%40googlegroups.com.

Nils Bruin

unread,
Sep 26, 2019, 8:48:52 PM9/26/19
to sage-devel
On Thursday, September 26, 2019 at 3:14:07 PM UTC-7, Salvatore Stella wrote:
The prototype does, now I have to implement the real thing.

Is there any way to add this info to the documentation? It took me a lot of
trial and error to figure out how to do what I wanted.
S.

Definitely. The topical place would be in sage.misc.classcall_metaclass, since that is the module that implements the __classcall__ mechanism. That's also the documentation you find when you google "__classcall__" (once you convince google you don't want the results for __call__).

Travis Scrimshaw

unread,
Sep 26, 2019, 8:57:52 PM9/26/19
to sage-devel

The prototype does, now I have to implement the real thing.

Is there any way to add this info to the documentation? It took me a lot of
trial and error to figure out how to do what I wanted.
S.

Definitely. The topical place would be in sage.misc.classcall_metaclass, since that is the module that implements the __classcall__ mechanism. That's also the documentation you find when you google "__classcall__" (once you convince google you don't want the results for __call__).

This is actually documented. The problem is the ClasscallMetaclass.__call__ is not in the online documentation (in some sense, Google does know better :P).

Best,
Travis

Travis Scrimshaw

unread,
Sep 26, 2019, 8:59:38 PM9/26/19
to sage-devel
There is also an example of this inheritance and discussion in one of the sage.structure.unique_representation examples.

Best,
Travis

VulK

unread,
Sep 27, 2019, 6:42:43 AM9/27/19
to sage-...@googlegroups.com
Indeed it seems to be documented. Why is this not in the online
documentation?
S.


* Travis Scrimshaw <tsc...@ucdavis.edu> [2019-09-26 17:59:38]:
>--
>You received this message because you are subscribed to the Google Groups "sage-devel" group.
>To unsubscribe from this group and stop receiving emails from it, send an email to sage-devel+...@googlegroups.com.
>To view this discussion on the web visit https://groups.google.com/d/msgid/sage-devel/ef937635-dbf2-4e7e-ab85-6ef5c8a24335%40googlegroups.com.

Travis Scrimshaw

unread,
Sep 28, 2019, 7:44:00 PM9/28/19
to sage-devel
Because it is not a public method and nobody added the automethod to the class to make sure it is included. That is my guess. Should be simple to fix.

Best,
Travis
>To unsubscribe from this group and stop receiving emails from it, send an email to sage-...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages