ActiveState Code

Recipe 252180: Combining simple and specific property creation


This recipe provides a function that will automate simple property creation, while allowing its users to provide specific fget/fset/fdel methods.

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import sys

def attribute(attrname, permit='rwd', fget=None, fset=None, fdel=None, doc=''):
    """returns a property associated with 'attrname'.
    
       By default, a simple property with get, set, and delete methods
       will be created. Optionally, specific get/set/del methods may be
       supplied. You can also choose to omit the creation of one or more
       of the default get/set/del methods via the 'permit' flag:
           'r': readable, 'w':writable, 'd':deletable
       So, if you want a property that is readable and deletable, but not
       writable, use "permit='rd'".
    """
    if _isprivate(attrname):
        attrname = _mangle(_get_classname(), attrname)
    if 'r' in permit and fget is None:
        fget = lambda self: getattr(self, attrname)
    if 'w' in permit and fset is None:
        fset = lambda self, value: setattr(self, attrname, value)
    if 'd' in permit and fdel is None:
        fdel = lambda self: delattr(self, attrname)
    return property(fget, fset, fdel, doc)

def _isprivate(attrname):
    return attrname.startswith("__") and not attrname.endswith("__")

def _mangle(classname, attrname):
    '''mangles name according to python name-mangling 
       conventions for private variables'''
    return "_%s%s" % (classname, attrname)

def _get_classname():
    "returns the calling class' name"
    frame = sys._getframe(2)
    classname = frame.f_code.co_name
    return classname

# example
if __name__ == "__main__":
    class C(object):
        a = attribute('_a', fget=lambda self: self._a*2)
        def __init__(self):
            self._a = "A"

    c = C()
    print c.a  # uses a.fget, user supplied
    c.a = "AA" # uses a.fset, provided by default
    print c.a

Discussion

This recipe may be useful if you want to make a property where at least one of fget, fset, or fdel is non-trivial but you don't want to bother providing the other trivial implementations. By trivial implementations for fget, fset, fdel, I mean:

fget = lambda self: getattr(self, attrname) fset = lambda self, value: setattr(self, attrname, value) fdel = lambda self: delattr(self, attrname)

This recipe provides the trivial implementations for fget/fset/fdel by default. Users can substitute specific implementations for, or they can omit, any of these methods.

If you only intend to create simple properties, my other recipe (http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/157768) should suffice. But, if you want to mix simple property creation with specific, this recipe may be of some use.

Comments

  1. 1. At 1:36 p.m. on 28 nov 2003, Sean Ross (the author) said:

    Here's an alternative implementation of attribute() which does not require that self.attrname be initialized in __init__():

    def attribute2(attrname, default=None, permit='rwd',

            fget=None, fset=None, fdel=None, doc=''):
    
    "don't need to initialize self.attrname in __init__()"
    
    def wrap(f):
    
        if f is None: return f
    
        def wrapped(self, *value):
    
            if not hasattr(self, attrname):
    
                setattr(self, attrname, default)
    
            try: return f(self) # fget/fdel
    
            except TypeError: return f(self, value[0]) # fset
    
        return wrapped
    
    fget, fset, fdel = wrap(fget), wrap(fset), wrap(fdel)
    
    if _isprivate(attrname):
    
        attrname = _mangle(_get_classname(), attrname)
    
    if 'r' in permit and fget is None:
    
        def fget(self):
    
            value = default
    
            try: value = getattr(self, attrname)
    
            except AttributeError: setattr(self, attrname, default)
    
            return value
    
    if 'w' in permit and fset is None:
    
        def fset(self, value):
    
            setattr(self, attrname, value)
    
    if 'd' in permit and fdel is None:
    
        def fdel(self): # calling fget can restore this attribute!
    
            try: delattr(self, attrname)
    
            except AttributeError: pass
    
    return property(fget, fset, fdel, doc)
    

    if __name__ == "__main__":

    class C(object):
    
        a = attribute2('_a', "A", fget=lambda self: self._a*2)
    
    
    
    c = C()
    
    print c.a
    
    c.a = "AA"
    
    print c.a
    

Sign in to comment