B
bearophileHUGS
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
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