Уведомления

Группа в Telegram: @pythonsu

#1 Янв. 4, 2011 21:26:11

gkraser
От:
Зарегистрирован: 2007-08-25
Сообщения: 57
Репутация: +  0  -
Профиль   Отправить e-mail  

property с минимумом телодвижений и правильным перекрытием в потомках

Столкнулся сегодня с очевидным фактом: перекрытый в потомке 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. И в потомках методы нормально отрабатываются.

Что скажете?



Офлайн

#2 Янв. 5, 2011 00:16:16

Андрей Светлов
От:
Зарегистрирован: 2007-05-15
Сообщения: 3137
Репутация: +  14  -
Профиль   Адрес электронной почты  

property с минимумом телодвижений и правильным перекрытием в потомках

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

Вот как выглядело бы у меня (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
Дело вкуса…



Офлайн

#3 Янв. 5, 2011 14:08:49

bazooka
От:
Зарегистрирован: 2009-04-12
Сообщения: 165
Репутация: +  0  -
Профиль   Отправить e-mail  

property с минимумом телодвижений и правильным перекрытием в потомках

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
еще так



Офлайн

#4 Янв. 6, 2011 11:49:12

Ferroman
От:
Зарегистрирован: 2006-11-16
Сообщения: 2759
Репутация: +  1  -
Профиль   Отправить e-mail  

property с минимумом телодвижений и правильным перекрытием в потомках

bazooka
Этот, как по мне, наиболее читабельный.

Отредактировано (Янв. 6, 2011 11:49:22)

Офлайн

#5 Янв. 6, 2011 16:53:40

gkraser
От:
Зарегистрирован: 2007-08-25
Сообщения: 57
Репутация: +  0  -
Профиль   Отправить e-mail  

property с минимумом телодвижений и правильным перекрытием в потомках

Да, все это работает, мне просто нужен был способ “с минимумом телодвижений”, ибо очень много наследников с перекрытием аксессоров для свойств и объектов с большим количеством свойств.

3 вещи раздражают в python:
1) синтаксис для свойств (отсутсвует)
2) вызов super (нужно указывать класс, в 3-ем конечно исправили…)
3) “Должен существовать один — и, желательно, только один — очевидный способ сделать это”, который нифика не соблюдается, чему пример эти варианты…



Офлайн

#6 Янв. 6, 2011 17:54:22

Андрей Светлов
От:
Зарегистрирован: 2007-05-15
Сообщения: 3137
Репутация: +  14  -
Профиль   Адрес электронной почты  

property с минимумом телодвижений и правильным перекрытием в потомках

Смените язык на тот, в котором есть всё-всё-всё.
Метакласс всего лишь для свойств - это несколько перебор. К тому же метаклассы имеют привычку конфликтовать друг с другом.



Офлайн

#7 Янв. 6, 2011 18:23:15

gkraser
От:
Зарегистрирован: 2007-08-25
Сообщения: 57
Репутация: +  0  -
Профиль   Отправить e-mail  

property с минимумом телодвижений и правильным перекрытием в потомках

Андрей Светлов
Смените язык на тот, в котором есть всё-всё-всё.
Эх… ткните носом :) Нету такого, да и не говорю я, что плох python, просто периодически возникает ощущение дискомфорта. В тех самых 3-х местах.

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

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



Офлайн

#8 Янв. 6, 2011 23:25:22

poltergeist
От:
Зарегистрирован: 2007-02-28
Сообщения: 522
Репутация: +  0  -
Профиль   Отправить e-mail  

property с минимумом телодвижений и правильным перекрытием в потомках

Предлагаю такой вариант: http://pastebin.com/uNq7WcBL
… и никакой возни с декораторами и повторением объявления свойств.

Но вариант bazooka меня заинтересовал больше. Я не знал, что методы getter, setter и deleter возвращают новую копию свойства, вместо изменения старого (как я думал раньше). Просто и удобно:)



Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version