Ticket #10605: trac_10605-lazy_class_attribute-fh.patch

File trac_10605-lazy_class_attribute-fh.patch, 6.1 KB (added by hivert, 11 years ago)
  • sage/misc/all.py

    # HG changeset patch
    # User Florent Hivert <Florent.Hivert@univ-rouen.fr>
    # Date 1294862547 -3600
    # Node ID fbba034e98cb5adb94f51a53a56498f1973f33ce
    # Parent  4b0c66f368681d18d96b5afd60a7e3e4bceec854
    #10605: Added lazy attribute for class attribute.
    
    diff --git a/sage/misc/all.py b/sage/misc/all.py
    a b from constant_function import ConstantFu 
    152152
    153153from cachefunc import CachedFunction, cached_function, cached_method, cached_in_parent_method, disk_cached_function
    154154
    155 from lazy_attribute import lazy_attribute
     155from lazy_attribute import lazy_attribute, lazy_class_attribute
     156
    156157
    157158from abstract_method import abstract_method
    158159
  • sage/misc/lazy_attribute.py

    diff --git a/sage/misc/lazy_attribute.py b/sage/misc/lazy_attribute.py
    a b class lazy_attribute(object): 
    4949
    5050    For an instance a of A, a.x is calculated the first time it is accessed,
    5151    and then stored as a usual attribute::
    52    
     52
    5353        sage: a = A()
    5454        sage: a.x
    5555        calculating x in A
    class lazy_attribute(object): 
    7878    This shows that, after the first calculation, the attribute x
    7979    becomes a usual attribute; in particular, there is no time penalty
    8080    to access it.
    81    
     81
    8282    A lazy attribute may be set as usual, even before its first access,
    8383    in which case the lazy calculation is completely ignored::
    8484
    class lazy_attribute(object): 
    190190        calculating x from y in B
    191191        1
    192192
     193
    193194    .. rubric:: lazy attributes and introspection
    194195
    195196    TODO: make the following work nicely::
    class lazy_attribute(object): 
    223224        sage: a.__dict__['x']=5
    224225        sage: a.x
    225226        5
    226        
     227
    227228        sage: class A (object):
    228229        ...       @property
    229230        ...       def x(self):
    class lazy_attribute(object): 
    271272        3
    272273        sage: timeit('a.x') # random
    273274        625 loops, best of 3: 115 ns per loop
    274        
     275
    275276        sage: a = A()
    276277        sage: a.x = 4
    277278        sage: a.x
    278279        4
    279280        sage: a.__dict__
    280281        {'a': 2, 'x': 4}
    281        
     282
    282283        sage: class B(A):
    283284        ...       @lazy_attribute
    284285        ...       def x(self):
    class lazy_attribute(object): 
    324325        ...       def __get__(self, obj, cls):
    325326        ...           print cls
    326327        ...           return 1
    327         ...   
    328328        sage: class A(object):
    329329        ...       x = descriptor()
    330         ...   
    331330        sage: class B(A):
    332331        ...       pass
    333332        ...
    class lazy_attribute(object): 
    349348
    350349    From the specifications for Super Binding, it would be expected to
    351350    get A and not B as cls parameter::
    352    
     351
    353352        sage: super(B, B()).x
    354353        <class '__main__.B'>
    355354        1
    class lazy_attribute(object): 
    490489        from sage.misc.sageinspect import sage_getsourcelines
    491490        return sage_getsourcelines(self.f)
    492491
    493        
     492
    494493    def __get__(self, a, cls):
    495494        """
    496495        Implements the attribute access protocol.
    class lazy_attribute(object): 
    518517            return getattr(super(cls, a),self.f.__name__)
    519518        setattr(a, self.f.__name__, result)
    520519        return result
     520
     521
     522class lazy_class_attribute(lazy_attribute):
     523    """
     524    A lazy class attribute for an class is like a usual class attribute,
     525    except that, instead of being computed when the class is constructed, it
     526    is computed on the fly the first time it is accessed, either through the
     527    class itself or trough on of its objects.
     528
     529    This is very similar to :class:`lazy_attribute` except that the attribute
     530    is a class attribute. More precisely, once computed, the lazy class
     531    attribute is stored in the class rather than in the object. The lazy class
     532    attribute is only computed once for all the objects::
     533
     534        sage: class Cl(object):
     535        ...       @lazy_class_attribute
     536        ...       def x(cls):
     537        ...            print "computing x"
     538        ...            return 1
     539        sage: Cl.x
     540        computing x
     541        1
     542        sage: Cl.x
     543        1
     544
     545    As for a any usual class attribute it is also possible to access it from
     546    an object::
     547
     548        sage: b = Cl()
     549        sage: b.x
     550        1
     551
     552    First access from an object also porperly triggers the computation::
     553
     554        sage: class Cl1(object):
     555        ...       @lazy_class_attribute
     556        ...       def x(cls):
     557        ...            print "computing x"
     558        ...            return 1
     559        sage: Cl1().x
     560        computing x
     561        1
     562        sage: Cl1().x
     563        1
     564
     565    .. warning::
     566
     567        The behavior of lazy class attributes with respect to inheritance is
     568        not specified. It currently depends on the evaluation order::
     569
     570            sage: class A(object):
     571            ...       @lazy_class_attribute
     572            ...       def x(cls):
     573            ...            print "computing x"
     574            ...            return str(cls)
     575            ...       @lazy_class_attribute
     576            ...       def y(cls):
     577            ...            print "computing y"
     578            ...            return str(cls)
     579            sage: class B(A):
     580            ...       pass
     581
     582            sage: A.x
     583            computing x
     584            "<class '__main__.A'>"
     585            sage: B.x
     586            "<class '__main__.A'>"
     587
     588            sage: B.y
     589            computing y
     590            "<class '__main__.B'>"
     591            sage: A.y
     592            computing y
     593            "<class '__main__.A'>"
     594            sage: B.y
     595            "<class '__main__.B'>"
     596
     597
     598    TESTS::
     599
     600        sage: "x" in b.__dict__
     601        False
     602    """
     603
     604    def __get__(self, _, cls):
     605        """
     606        Implements the attribute access protocol.
     607
     608        EXAMPLES::
     609
     610            sage: class A: pass
     611            sage: def f(x): return 1
     612            ...
     613            sage: f = lazy_class_attribute(f)
     614            sage: f.__get__(A(), A)
     615            1
     616        """
     617        result = self.f(cls)
     618        if result is NotImplemented:
     619            return getattr(super(cls, cls),self.f.__name__)
     620        setattr(cls, self.f.__name__, result)
     621        return result
     622