Welcome, guest | Sign In | My Account | Store | Cart

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

Python, 48 lines
 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

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.

1 comment

Sean Ross (author) 20 years, 4 months ago  # | flag

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
Created by Sean Ross on Fri, 28 Nov 2003 (PSF)
Python recipes (4591)
Sean Ross's recipes (8)

Required Modules

Other Information and Tasks