Я меняю некоторые свои классы с широкого использования геттеров и сеттеров на более питоническое использование свойств.
Но теперь я застрял, потому что некоторые из моих предыдущих методов получения или установки вызывали соответствующий метод базового класса, а затем выполняли что-то еще. Но как этого добиться с помощью свойств? Как вызвать метод получения или установки свойства в родительском классе?
Конечно, простой вызов атрибута дает бесконечную рекурсию.
class Foo(object):
@property
def bar(self):
return 5
@bar.setter
def bar(self, a):
print a
class FooBar(Foo):
@property
def bar(self):
# return the same value
# as in the base class
return self.bar # --> recursion!
@bar.setter
def bar(self, c):
# perform the same action
# as in the base class
self.bar = c # --> recursion!
# then do something else
print 'something else'
fb = FooBar()
fb.bar = 7
источник
Foo.bar.fset(self, c)
унаследованный сеттер? Почему бы и нетFoo.bar.fset(c)
- без «Я» - я думал, это неявно прошло?self
получить из цепочкиFoo.bar.fset
?foo = Foo()
\foo.bar(c)
не само прошло, но бар () получает его из Python. Я не специалист по Python, более или менее новичок. Это просто мысль.self
передается только неявно, когда метод вызывается в экземпляре класса. Например, если у меня есть классA
с методомb(self, arg)
, и я создаю экземплярc = A()
, то вызовc.b(arg)
эквивалентенA.b(c, arg)
Super должен сделать свое дело:
return super().bar
В Python 2.x вам нужно использовать более подробный синтаксис:
return super(FooBar, self).bar
источник
AttributeError
.Существует альтернативный вариант использования
super
, который не требует явной ссылки на имя базового класса.Базовый класс A:
class A(object): def __init__(self): self._prop = None @property def prop(self): return self._prop @prop.setter def prop(self, value): self._prop = value class B(A): # we want to extend prop here pass
В B, доступ к получателю свойства родительского класса A:
Как уже ответили другие, это:
Или в Python 3:
Это возвращает значение, возвращаемое получателем свойства, а не сам получатель, но этого достаточно для расширения получателя.
В B доступ к установщику свойств родительского класса A:
Лучшая рекомендация, которую я видел до сих пор, следующая:
Я считаю, что это лучше:
В этом примере оба варианта эквивалентны, но использование super имеет то преимущество, что оно не зависит от базовых классов
B
. ЕслиB
бы мы наследовали отC
класса, также расширяющего свойство, вам не пришлось бы обновлятьB
код.Полный код B, расширяющий свойство A:
class B(A): @property def prop(self): value = super(B, self).prop # do something with / modify value here return value @prop.setter def prop(self, value): # do something with / modify value here super(B, self.__class__).prop.fset(self, value)
Одно предостережение:
Если у вашего свойства нет сеттера, вам нужно определить как сеттер, так и получатель,
B
даже если вы изменяете поведение только одного из них.источник
super(B, self.__class__)
именно работаетsuper(class, class)
? Где это задокументировано?пытаться
@property def bar: return super(FooBar, self).bar
Хотя я не уверен, поддерживает ли python вызов свойства базового класса. Свойство на самом деле является вызываемым объектом, который настраивается с указанной функцией, а затем заменяет это имя в классе. Это легко может означать, что суперфункции нет.
Вы всегда можете переключить свой синтаксис на использование функции property ():
class Foo(object): def _getbar(self): return 5 def _setbar(self, a): print a bar = property(_getbar, _setbar) class FooBar(Foo): def _getbar(self): # return the same value # as in the base class return super(FooBar, self)._getbar() def bar(self, c): super(FooBar, self)._setbar(c) print "Something else" bar = property(_getbar, _setbar) fb = FooBar() fb.bar = 7
источник
Некоторые небольшие улучшения в ответе Максима :
__class__
чтобы избежать записиB
. Обратите внимание, чтоself.__class__
это тип среды выполненияself
, но__class__
безself
имени определения включающего класса.super()
это сокращение отsuper(__class__, self)
.__set__
вместоfset
. Последнее характерно дляproperty
s, но первое применяется ко всем подобным свойствам объектам (дескрипторам).class B(A): @property def prop(self): value = super().prop # do something with / modify value here return value @prop.setter def prop(self, value): # do something with / modify value here super(__class__, self.__class__).prop.__set__(self, value)
источник
Вы можете использовать следующий шаблон:
class Parent(): def __init__(self, value): self.__prop1 = value #getter @property def prop1(self): return self.__prop1 #setter @prop1.setter def prop1(self, value): self.__prop1 = value #deleter @prop1.deleter def prop1(self): del self.__prop1 class Child(Parent): #getter @property def prop1(self): return super(Child, Child).prop1.__get__(self) #setter @prop1.setter def prop1(self, value): super(Child, Child).prop1.__set__(self, value) #deleter @prop1.deleter def prop1(self): super(Child, Child).prop1.__delete__(self)
Заметка! Все методы свойств должны быть переопределены вместе. Если вы не хотите переопределять все методы, используйте вместо этого следующий шаблон:
class Parent(): def __init__(self, value): self.__prop1 = value #getter @property def prop1(self): return self.__prop1 #setter @prop1.setter def prop1(self, value): self.__prop1 = value #deleter @prop1.deleter def prop1(self): del self.__prop1 class Child(Parent): #getter @Parent.prop1.getter def prop1(self): return super(Child, Child).prop1.__get__(self) #setter @Parent.prop1.setter def prop1(self, value): super(Child, Child).prop1.__set__(self, value) #deleter @Parent.prop1.deleter def prop1(self): super(Child, Child).prop1.__delete__(self)
источник
class Base(object): def method(self): print "Base method was called" class Derived(Base): def method(self): super(Derived,self).method() print "Derived method was called" d = Derived() d.method()
(если я что-то не упускаю из вашего объяснения)
источник