Path: blob/master/src/sage/structure/element_wrapper.pyx
8814 views
"""1Element Wrapper23Wrapping Sage or Python objects as Sage elements.45AUTHORS:67- Nicolas Thiery (2008-2010): Initial version8- Travis Scrimshaw (2013-05-04): Cythonized version9"""10#*****************************************************************************11# Copyright (C) 2008-2010 Nicolas M. Thiery <nthiery at users.sf.net>12#13# Distributed under the terms of the GNU General Public License (GPL)14# http://www.gnu.org/licenses/15#******************************************************************************1617include "../ext/python.pxi"18from cpython cimport bool1920from sage.structure.parent cimport Parent21from sage.structure.element cimport Element22from copy import copy2324cdef class ElementWrapper(Element):25r"""26A class for wrapping Sage or Python objects as Sage elements.2728EXAMPLES::2930sage: from sage.structure.element_wrapper import DummyParent31sage: parent = DummyParent("A parent")32sage: o = ElementWrapper(parent, "bla"); o33'bla'34sage: isinstance(o, sage.structure.element.Element)35True36sage: o.parent()37A parent38sage: o.value39'bla'4041Note that ``o`` is not *an instance of* ``str``, but rather42*contains a* ``str``. Therefore, ``o`` does not inherit the string43methods. On the other hand, it is provided with reasonable default44implementations for equality testing, hashing, etc.4546The typical use case of ``ElementWrapper`` is for trivially47constructing new element classes from pre-existing Sage or Python48classes, with a containment relation. Here we construct the49tropical monoid of integers endowed with ``min`` as50multiplication. There, it is desirable *not* to inherit the51``factor`` method from ``Integer``::5253sage: class MinMonoid(Parent):54....: def _repr_(self):55....: return "The min monoid"56....:57sage: M = MinMonoid()58sage: class MinMonoidElement(ElementWrapper):59....: wrapped_class = Integer60....:61....: def __mul__(self, other):62....: return MinMonoidElement(self.parent(), min(self.value, other.value))63sage: x = MinMonoidElement(M, 5); x64565sage: x.parent()66The min monoid67sage: x.value68569sage: y = MinMonoidElement(M, 3)70sage: x * y7137273This example was voluntarily kept to a bare minimum. See the74examples in the categories (e.g. ``Semigroups().example()``) for75several full featured applications.7677.. WARNING::7879Versions before :trac:`14519` had parent as the second argument and80the value as the first.81"""82cdef public object value8384def __init__(self, parent, value):85"""86EXAMPLES::8788sage: from sage.structure.element_wrapper import DummyParent89sage: a = ElementWrapper(DummyParent("A parent"), 1)9091TESTS::9293sage: TestSuite(a).run(skip = "_test_category")9495sage: a = ElementWrapper(1, DummyParent("A parent"))96doctest:...: DeprecationWarning: the first argument must be a parent97See http://trac.sagemath.org/14519 for details.9899.. NOTE::100101:class:`ElementWrapper` is not intended to be used directly,102hence the failing category test.103"""104#assert isinstance(value, self.wrapped_class)105if not isinstance(parent, Parent):106from sage.misc.superseded import deprecation107deprecation(14519, 'the first argument must be a parent')108value, parent = parent, value109Element.__init__(self, parent=parent)110self.value = value111112# When self is an extension type without a __dict__ attribute,113# this prevents self.__dict__ to return whatever crap obtained by114# lookup through the categories ...115__dict__ = {}116117def __getstate__(self):118"""119Return a tuple describing the state of your object.120121This emulates :meth:`Element.__getstate__`, playing as if122``self.value`` was in the dictionary of ``self`` as it used to123be before :trac:`14519`.124125EXAMPLES::126127sage: from sage.structure.element_wrapper import DummyParent128sage: P = DummyParent("A parent")129sage: a = ElementWrapper(P, 1)130sage: a.__getstate__()131(A parent, {'value': 1})132sage: class A(ElementWrapper):133....: pass134sage: a = A(P, 1)135sage: a.x = 2136sage: a.__getstate__() == (P, {'value': 1, 'x': 2})137True138"""139d = self.__dict__.copy()140d['value'] = self.value141return (self._parent, d)142143def __setstate__(self, state):144r"""145Initialize the state of the object from data saved in a pickle.146147This emulates :meth:`Element.__setstate__`, playing as if148``self.value`` was to be put in the dictionary of ``self`` as149it used to be before :trac:`14519`.150151EXAMPLES::152153sage: from sage.structure.element_wrapper import DummyParent154sage: class A(ElementWrapper):155....: pass156sage: a = A(DummyParent("A parent"), 1)157sage: a.__setstate__((DummyParent("Another parent"), {'value':0,'x':3}))158sage: a.parent()159Another parent160sage: a.value1610162sage: a.x1633164165TESTS::166167sage: a = A(DummyParent("A parent"), 1)168sage: import __main__; __main__.A = A # Fake A being defined in a python module169sage: a.x = 2170sage: a == loads(dumps(a))171True172sage: a = ElementWrapper(DummyParent("A parent"), 1)173sage: a == loads(dumps(a))174True175176Checking that we can still load pickle from before :trac:`14519`::177178sage: f = loads('x\x9c\x85\x8d\xbb\n\xc2@\x14D\xf1\x11\x8d\xebO\xd8\xda,\xf8\t\x82\xf6\x12\x08\x96a\x8dC\x08f\xd7\xdc\xbb{\x15\x8b\x80\x16\xd1\xdf6\x10\xad,,\xcf0s\xe6>\xcc\xbd)\xa0}`\xc9\x8304*X\xb8\x90]\xd9\xd45Xm{\xde\x7f\x90\x06\xcb\x07\xfd\x8c\xc4\x95$\xc8\x185\xc3wm\x13\xca\xb3S\xe2\x18G\xc9\xa1h\xf4\xefe#\xd6\xdev\x86\xbbL\xd18\x8d\xd7\x8b\x1e(j\x9b\x17M\x12\x9a6\x14\xa7\xd1\xc5T\x02\x9a\xf56.]\xe1u\xe9\x02\x8a\xce`\xcd\t\xd9\x17H\xa5\x83U\x9b\xd0\xdc?\x0f\xfa\rl4S\xbc')179sage: f == ElementWrapper(DummyParent("A Parent"), 1)180True181"""182# Make sure the first part of the state is the parent183if not isinstance(state[0], Parent):184state[0], state[1] = state[1], state[0]185self._set_parent(state[0])186d = state[1].copy()187self.value = d.pop('value')188if d:189self.__dict__ = d190191def _repr_(self):192"""193EXAMPLES::194195sage: from sage.structure.element_wrapper import DummyParent196sage: ElementWrapper(DummyParent("A parent"), 1)1971198"""199return repr(self.value)200201def _latex_(self):202r"""203EXAMPLES::204205sage: from sage.structure.element_wrapper import DummyParent206sage: ElementWrapper(DummyParent("A parent"), 1)._latex_()2071208sage: ElementWrapper(DummyParent("A parent"), 3/5)._latex_()209\frac{3}{5}210"""211from sage.misc.latex import latex212return latex(self.value)213214def __hash__(self):215"""216Return the same hash as for the wrapped element.217218EXAMPLES::219220sage: from sage.structure.element_wrapper import DummyParent221sage: parent1 = DummyParent("A parent")222sage: parent2 = DummyParent("Another parent")223sage: hash(ElementWrapper(parent1, 1))2241225sage: hash(ElementWrapper(parent2, 1))2261227228.. TODO::229230Should this take the parent and/or the class into account?231"""232return hash(self.value)233234def __richcmp__(left, right, int op):235"""236Return ``True`` if ``left`` compares with ``right`` based on ``op``.237238Default implementation of ``self == other``: two elements are239equal if they have the same class, same parent, and same value.240241Default implementation of ``self < other``: two elements are242always incomparable.243244.. NOTE::245246Another option would be to not define ``__lt__``, but247given the current implementation of SageObject, sorted(...)248would break.249250TESTS:251252Testing equality::253254sage: from sage.structure.element_wrapper import DummyParent255sage: parent1 = DummyParent("A parent")256sage: parent2 = DummyParent("Another parent")257sage: parent1 == parent2258False259sage: l11 = ElementWrapper(parent1, 1)260sage: l12 = ElementWrapper(parent1, 2)261sage: l21 = ElementWrapper(parent2, 1)262sage: l22 = ElementWrapper(parent2, 2)263sage: l11 == l11264True265sage: l11 == l12266False267sage: l11 == l21268False269270Testing inequality::271272sage: from sage.structure.element_wrapper import DummyParent273sage: parent1 = DummyParent("A parent")274sage: parent2 = DummyParent("Another parent")275sage: parent1 == parent2276False277sage: l11 = ElementWrapper(parent1, 1)278sage: l12 = ElementWrapper(parent1, 2)279sage: l21 = ElementWrapper(parent2, 1)280sage: l22 = ElementWrapper(parent2, 2)281sage: l11 != l11282False283sage: l11 != l12284True285sage: l11 != l21286True287288Testing less than::289290sage: from sage.structure.element_wrapper import DummyParent291sage: parent = DummyParent("A parent")292sage: x = ElementWrapper(parent, 1)293sage: y = ElementWrapper(parent, 2)294sage: x.__lt__(x), x.__lt__(y), y.__lt__(x), x.__lt__(1)295(False, False, False, False)296sage: x < x, x < y, y < x, x < 1297(False, False, False, False)298sage: sorted([x,y])299[1, 2]300sage: sorted([y,x])301[2, 1]302"""303cdef ElementWrapper self304self = left305if self.__class__ != right.__class__ \306or self._parent != (<ElementWrapper>right)._parent:307return op == Py_NE308if op == Py_EQ or op == Py_LE or op == Py_GE:309return self.value == (<ElementWrapper>right).value310if op == Py_NE:311return self.value != (<ElementWrapper>right).value312return False313314cpdef bool _lt_by_value(self, other):315"""316Return whether ``self`` is strictly smaller than ``other``.317318With this implementation 'by value', they are always319incomparable unless ``self`` and ``other`` have the same320class, parent, and ``self.value < other.value``.321322EXAMPLES::323324sage: from sage.structure.element_wrapper import DummyParent325sage: class MyElement(ElementWrapper):326....: __lt__ = ElementWrapper._lt_by_value327....:328sage: parent1 = DummyParent("A parent")329sage: parent2 = DummyParent("Another parent")330sage: l11 = MyElement(parent1, 1)331sage: l12 = MyElement(parent1, 2)332sage: l21 = MyElement(parent2, 1)333sage: l22 = MyElement(parent2, 2)334sage: l11 < l11335False336sage: l11 < l12, l12 < l11 # values differ337(True, False)338sage: l11 < l21 # parents differ339False340sage: l11 < 1 # class differ341False342sage: 1 < l11 # random, since it depends on what the Integer 1 decides to do, which may just involve memory locations343False344"""345return self.__class__ is other.__class__ \346and self._parent is other.parent() \347and self.value < (<ElementWrapper>other).value348349cpdef int _cmp_by_value(self, other):350"""351Implementation of ``cmp`` by comparing first values, then352parents, then class. This behavior (which implies a total353order) is not always desirable and hard to override. Hence354derived subclasses that want to take advantage of this355feature need to explicitely set :meth:`.__cmp__`.356357EXAMPLES::358359sage: class MyElement(ElementWrapper):360....: __cmp__ = ElementWrapper._cmp_by_value361....:362sage: from sage.structure.element_wrapper import DummyParent363sage: parent1 = DummyParent("A parent")364sage: parent2 = DummyParent("Another parent")365sage: parent1 == parent2366False367sage: l11 = MyElement(parent1, 1)368sage: l12 = MyElement(parent1, 2)369sage: l21 = MyElement(parent2, 1)370sage: l22 = MyElement(parent2, 2)371sage: cmp(l11, l11)3720373sage: cmp(l11, l12), cmp(l12, l11) # values differ374(-1, 1)375sage: cmp(l11, l21) in [-1, 1] # parents differ376True377sage: cmp(l21, l11) == -cmp(l11, l21)378True379sage: cmp(l11, 1) in [-1,1] # class differ380True381"""382if self.__class__ != other.__class__:383return cmp(self.__class__, other.__class__)384if self.parent() != other.parent():385return cmp(self.parent(), other.parent())386return cmp(self.value, other.value)387388def __copy__(self):389"""390Copy ``self`` and in particular its ``value`` attribute.391392EXAMPLES::393394sage: from sage.structure.element_wrapper import DummyParent395sage: parent = DummyParent("A parent")396sage: o1 = ElementWrapper(parent, [1]); o1397[1]398sage: o2 = copy(o1); o2399[1]400sage: o1 is o2, o1.value is o2.value401(False, False)402sage: o2.value[0] = 3; o2403[3]404sage: o1405[1]406sage: class bla(ElementWrapper): pass407sage: o3 = bla(parent, [1])408sage: o4 = copy(o3)409sage: o3.value[0] = 3; o4410[1]411sage: o3.__class__412<class '__main__.bla'>413sage: o4.__class__414<class '__main__.bla'>415"""416# Note : copy(super(ElementWrapper, self)) does not work.417res = super(ElementWrapper, self).__copy__()418res.value = copy(self.value)419return res420421from sage.structure.parent import Parent422from sage.structure.unique_representation import UniqueRepresentation423class DummyParent(UniqueRepresentation, Parent):424"""425A class for creating dummy parents for testing ElementWrapper426"""427def __init__(self, name):428"""429EXAMPLES::430431sage: from sage.structure.element_wrapper import DummyParent432sage: parent = DummyParent("A Parent")433sage: TestSuite(parent).run(skip = ["_test_an_element",\434"_test_category",\435"_test_elements",\436"_test_elements_eq_reflexive",\437"_test_elements_eq_symmetric",\438"_test_elements_eq_transitive",\439"_test_elements_neq",\440"_test_some_elements"])441"""442self.name = name443444def _repr_(self):445"""446EXAMPLES::447448sage: from sage.structure.element_wrapper import DummyParent449sage: DummyParent("A Parent") # indirect doctest450A Parent451"""452return self.name453454class ElementWrapperTester(ElementWrapper):455"""456Test class for the default :meth:`.__copy` method of subclasses of457:class:`ElementWrapper`.458459TESTS::460461sage: from sage.structure.element_wrapper import ElementWrapperTester462sage: x = ElementWrapperTester()463sage: x.append(2); y = copy(x); y.append(42)464sage: type(y)465<class 'sage.structure.element_wrapper.ElementWrapperTester'>466sage: x, y467([n=1, value=[2]], [n=2, value=[2, 42]])468sage: x.append(21); x.append(7)469sage: x, y470([n=3, value=[2, 21, 7]], [n=2, value=[2, 42]])471sage: x.value, y.value472([2, 21, 7], [2, 42])473sage: x.__dict__, y.__dict__474({'n': 3}, {'n': 2})475"""476def __init__(self):477"""478TESTS::479480sage: from sage.structure.element_wrapper import ElementWrapperTester481sage: x = ElementWrapperTester(); x482[n=0, value=[]]483"""484from sage.categories.sets_cat import Sets485super(ElementWrapperTester, self).__init__(Sets().example("facade"), [])486self.n = 0487488def append(self, x):489"""490TESTS::491492sage: from sage.structure.element_wrapper import ElementWrapperTester493sage: x = ElementWrapperTester()494sage: x.append(2); x495[n=1, value=[2]]496"""497self.n +=1498self.value.append(x)499500def _repr_(self):501"""502TESTS::503504sage: from sage.structure.element_wrapper import ElementWrapperTester505sage: x = ElementWrapperTester506sage: x = ElementWrapperTester(); x507[n=0, value=[]]508sage: x.value = [2,32]; x # indirect doctest509[n=0, value=[2, 32]]510"""511return "[n=%s, value=%s]"%(self.n, self.value)512513514515