Найти - Пользователи
Полная версия: property с минимумом телодвижений и правильным перекрытием в потомках
Начало » Python для экспертов » property с минимумом телодвижений и правильным перекрытием в потомках
1
gkraser
Столкнулся сегодня с очевидным фактом: перекрытый в потомке 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
При объявлении свойства xxx=NiceProperty(defvalue) подразумевается наличие getter/setter с именами xxx_get/xxx_set. Если методы отсутсвуют - они генерируются. defvalue - значение по умолчанию для private-атрибута.
В итоге - просто объявляем свойство и получаем getter/setter/private-attribute. И в потомках методы нормально отрабатываются.

Что скажете?
Андрей Светлов
Есть много забавных способов, ваш далеко не самый худший.

Вот как выглядело бы у меня (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
Дело вкуса…
bazooka
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
еще так
Ferroman
bazooka
Этот, как по мне, наиболее читабельный.
gkraser
Да, все это работает, мне просто нужен был способ “с минимумом телодвижений”, ибо очень много наследников с перекрытием аксессоров для свойств и объектов с большим количеством свойств.

3 вещи раздражают в python:
1) синтаксис для свойств (отсутсвует)
2) вызов super (нужно указывать класс, в 3-ем конечно исправили…)
3) “Должен существовать один — и, желательно, только один — очевидный способ сделать это”, который нифика не соблюдается, чему пример эти варианты…
Андрей Светлов
Смените язык на тот, в котором есть всё-всё-всё.
Метакласс всего лишь для свойств - это несколько перебор. К тому же метаклассы имеют привычку конфликтовать друг с другом.
gkraser
Андрей Светлов
Смените язык на тот, в котором есть всё-всё-всё.
Эх… ткните носом :) Нету такого, да и не говорю я, что плох python, просто периодически возникает ощущение дискомфорта. В тех самых 3-х местах.

Андрей Светлов
Метакласс всего лишь для свойств - это несколько перебор.
Согласен, но я просто показал выжимку, на самом деле свойства там “как один из…”

Андрей Светлов
К тому же метаклассы имеют привычку конфликтовать друг с другом.
В рамках одной, да и еще и собственной иерархии - вряд ли. А с таким уставом я конечно в монастыри (чужие :)) не полезу.
poltergeist
Предлагаю такой вариант: http://pastebin.com/uNq7WcBL
… и никакой возни с декораторами и повторением объявления свойств.

Но вариант bazooka меня заинтересовал больше. Я не знал, что методы getter, setter и deleter возвращают новую копию свойства, вместо изменения старого (как я думал раньше). Просто и удобно:)
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB