Форум сайта python.su
Столкнулся сегодня с очевидным фактом: перекрытый в потомке getter для свойства, как getter не воспринимается::
class C1(object):
def __init__(self):
self._p1 = 1
def p1_get(self):
return self._p1
def p1_set(self, v):
self._p1 = v
p1 = property(p1_get, p1_set)
class C2(C1):
def p1_get(self):
return self._p1 * 100
c1 = C1()
assert c1.p1 == 1
c2 = C2()
assert c1.p1 == 100 ### ERROR
class NiceProperty(object):
def __init__(self, defvalue=None):
self.defvalue = defvalue
def set_name(self, name):
self.name = name
self.getter_name = name + '_get'
self.setter_name = name + '_set'
self.private_name = '_' + name
def __get__(self, inst, owner):
f = getattr(inst, self.getter_name)
return f()
def __set__(self, inst, value):
f = getattr(inst, self.setter_name)
f(value)
def create_getter(self):
vars = dict()
exec 'def f(x):return x.{}'.format(self.private_name) in vars
return vars['f']
def create_setter(self):
vars = dict()
exec 'def f(x, y):x.{} = y'.format(self.private_name) in vars
return vars['f']
class MetaclassNiceProperty(type):
def __new__(cls, name, bases, dict_):
d1_ = dict(dict_) # copy dict_
for k, v in d1_.iteritems():
if isinstance(v, NiceProperty):
v.set_name(k)
if v.getter_name not in d1_:
dict_[v.getter_name] = v.create_getter()
if v.setter_name not in d1_:
dict_[v.setter_name] = v.create_setter()
# create private-attribute for property with default value
dict_[v.private_name] = v.defvalue
return type.__new__(cls, name, bases, dict_)
if __name__ == '__main__':
class Base(object):
__metaclass__ = MetaclassNiceProperty
class WithProp(Base):
prop1 = NiceProperty('s1')
prop2 = NiceProperty(1)
class WithProp2(WithProp):
def prop2_get(self):
return self._prop2 * 100
a1 = WithProp2()
assert a1.prop1 == 's1'
assert a1.prop2 == 100
a1.prop2 = 2
assert a1.prop2 == 200
Офлайн
Есть много забавных способов, ваш далеко не самый худший.
Вот как выглядело бы у меня (python 2.6+)
class C1(object):
def __init__(self):
self._p1 = 1
@property
def p1(self):
return self._p1
@p1.setter
def p1(self, v):
self._p1 = v
class C2(C1):
@property
def p1(self):
return self._p1 * 100
p1 = p1.setter(C1.p1.fset)
c1 = C1()
assert c1.p1 == 1
c1.p1 = 5
assert c1.p1 == 5
c2 = C2()
assert c2.p1 == 100
c2.p1 = 5
assert c2.p1 == 500
Офлайн
class C1(object):
def __init__(self):
self._p1 = 1
@property
def p1(self):
return self._p1
@p1.setter
def p1(self, v):
self._p1 = v
#p1 = (p1_get, p1_set)
class C2(C1):
@C1.p1.getter
def p1(self):
return self._p1 * 100
c1 = C1()
assert c1.p1 == 1
c2 = C2()
assert c2.p1 == 100 ### ERROR
Офлайн
bazooka
Этот, как по мне, наиболее читабельный.
Отредактировано (Янв. 6, 2011 11:49:22)
Офлайн
Да, все это работает, мне просто нужен был способ “с минимумом телодвижений”, ибо очень много наследников с перекрытием аксессоров для свойств и объектов с большим количеством свойств.
3 вещи раздражают в python:
1) синтаксис для свойств (отсутсвует)
2) вызов super (нужно указывать класс, в 3-ем конечно исправили…)
3) “Должен существовать один — и, желательно, только один — очевидный способ сделать это”, который нифика не соблюдается, чему пример эти варианты…
Офлайн
Смените язык на тот, в котором есть всё-всё-всё.
Метакласс всего лишь для свойств - это несколько перебор. К тому же метаклассы имеют привычку конфликтовать друг с другом.
Офлайн
Андрей СветловЭх… ткните носом :) Нету такого, да и не говорю я, что плох python, просто периодически возникает ощущение дискомфорта. В тех самых 3-х местах.
Смените язык на тот, в котором есть всё-всё-всё.
Андрей СветловСогласен, но я просто показал выжимку, на самом деле свойства там “как один из…”
Метакласс всего лишь для свойств - это несколько перебор.
Андрей СветловВ рамках одной, да и еще и собственной иерархии - вряд ли. А с таким уставом я конечно в монастыри (чужие :)) не полезу.
К тому же метаклассы имеют привычку конфликтовать друг с другом.
Офлайн
Предлагаю такой вариант: http://pastebin.com/uNq7WcBL
… и никакой возни с декораторами и повторением объявления свойств.
Но вариант bazooka меня заинтересовал больше. Я не знал, что методы getter, setter и deleter возвращают новую копию свойства, вместо изменения старого (как я думал раньше). Просто и удобно:)
Офлайн