property syntax

bearophileHUGS at lycos.com bearophileHUGS at lycos.com
Thu Mar 8 10:34:12 CET 2007


Probably you have already discussed this topic, but maybe you can
stand touching it again briefly.
Maybe properties aren't used so often to deserve a specific syntax,
but I don't like their syntax much. Here I show some alternative
solutions that work with the current Python, followed by a syntax
idea, that may be better fit for Python 3.0 (but maybe changing its
name it can be retrofitted into Python 2.6 too).

Some code is from:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/410698
That I have modified to suit my tastes.


# The first version doesn't work with Psyco:
# import psyco; psyco.full()

import sys

def nestedproperty(function):
    keys = '__get__', '__set__', '__del__'
    func_locals = {'doc':function.__doc__}
    def probe_function(frame, event, arg):
        if event == 'return':
            locals = frame.f_locals
            func_locals.update(dict((k, locals.get(k)) for k in keys))
            sys.settrace(None)
        return probe_function
    sys.settrace(probe_function)
    function()
    return property(fget=func_locals.get("__get__"),
                    fset=func_locals.get("__set__"),
                    fdel=func_locals.get("__del__"),
                    doc=func_locals.get("doc")
                   )


class Angle(object):
    def __init__(self, rad):
        self._rad = rad

    @nestedproperty
    def rad():
        "The angle in radians"
        def __get__(self):
            return self._rad
        def __set__(self, angle):
            if isinstance(angle, Angle):
                angle = angle.rad
            self._rad = float(angle)

a = Angle(5)
print a.rad
a.rad = 10
print a.rad
help(a)

# =====================================================

# The second version is cleaner, but at the moment it has a problem
with the property docstring (but that may be fixed) (it works with
Psyco):

class classpropertytype(property):
    "Pythonic property syntax."
    # Code from:
    # www.z3lab.org/sections/blogs/philipp-weitershausen/2006_05_29_pycon-06-lightning-talk/
    def __init__(self, name, bases=(), members=None):
        if members is None:
            members = {}
        doc = members.get('__doc__')
        if doc is None:
            doc = members.get('__get__').__doc__
        sup = super(classpropertytype, self)
        return sup.__init__(members.get('__get__'),
                            members.get('__set__'),
                            members.get('__del__'),
                            doc)
classproperty = classpropertytype('classproperty')

class Angle(object):
    def __init__(self, rad):
        self._rad = rad

    class rad(classproperty):
        "The angle in radians"
        def __get__(self):
            return self._rad
        def __set__(self,angle):
            if isinstance(angle, Angle):
                angle = angle.rad
            self._rad = angle

a = Angle(5)
print a.rad
a.rad = 10
print a.rad
help(a)

# =====================================================

# The third way has a bit of magic, but I like it best, and it works
with Psyco too:

class _property_meta(type):
    def __new__(meta, class_name, bases, new_attrs):
        if bases == (object,):
            # The property class itself
            return type.__new__(meta,class_name,bases,new_attrs)
        keys = "fget fset fdel __doc__".split()
        return property(*(new_attrs.get(k) for k in keys))

class classproperty(object):
    __metaclass__ = _property_meta

    def __new__(cls, fget=None, fset=None, fdel=None, fdoc=None):
        if fdoc is None and fget is not None:
            fdoc = fget.__doc__
        return property(fget, fset, fdel, fdoc)


class Angle(object):
    def __init__(self, rad):
        self._rad = rad

    class rad(classproperty):
        "The angle in radians"
        def fget(self):
            return self._rad
        def fset(self,angle):
            if isinstance(angle, Angle):
                angle = angle.rad
            self._rad = angle


a = Angle(5)
print a.rad
a.rad = 10
print a.rad
help(a)


# =====================================================

# I may appreciate a better syntax, this is just an idea:
# (George Sakkis has suggested a different syntax that I like less)

"""

class Angle(object):
    def __init__(self,rad):
        self._rad = rad

    property rad:
        "The angle in radians"
        def __get__(self):
            return self._rad
        def __set__(self, angle):
            if isinstance(angle, Angle):
                angle = angle.rad
            self._rad = angle

class Angle2(Angle):
    property rad:
        def __get__(self):
            return self._rad * 2

"""

# I'd like that possible syntax to mean this:

class Angle(object):
    def __init__(self, rad):
        self._rad = rad

    def get_rad(self):
        return self._rad

    def set_rad(self, angle):
        if isinstance(angle, Angle):
            angle = angle.rad
        self._rad = angle

    rad = property(fget=lambda self: self.get_rad(),
                   fset=lambda self, angle: self.set_rad(angle),
                   doc="The angle in radians"
                  )

class Angle2(Angle):
    def get_rad(self):
        return self._rad * 2

a = Angle(5)
print a.rad
a.rad = 10
print a.rad
help(a)

a = Angle2(5)
print a.rad
a.rad = 10
print a.rad
help(a)

Bye,
bearophile




More information about the Python-list mailing list