свойства и наследование python


у меня есть базовый класс со свойством, которое (метод get) я хочу перезаписать в подклассе. Моя первая мысль была что-то вроде:

class Foo(object):
    def _get_age(self):
        return 11

    age = property(_get_age)


class Bar(Foo):
    def _get_age(self):
        return 44

это не работает (подкласс бар.возвращает возраст 11). Я нашел решение с лямбда-выражением, которое работает:

age = property(lambda self: self._get_age())

Так это правильное решение для использования свойств и перезаписать их в подклассе, или есть другие предпочтительные способы сделать это?

8 53

8 ответов:

Я просто предпочитаю повторить property() а также вы будете повторять @classmethod декоратор при переопределении метода класса.

хотя это кажется очень многословным, по крайней мере для стандартов Python, вы можете заметить:

1) для свойств только для чтения, property может использоваться в качестве декоратора:

class Foo(object):
    @property
    def age(self):
        return 11

class Bar(Foo):
    @property
    def age(self):
        return 44

2) в Python 2.6, свойства выросли пара методовsetter и deleter который может быть использован для применения к общим свойствам ярлык уже доступно только для чтения:

class C(object):
    @property
    def x(self):
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

другой способ сделать это без создания дополнительных классов. Я добавил метод set, чтобы показать, что вы делаете, если вы переопределяете только один из двух:

class Foo(object):
    def _get_age(self):
        return 11

    def _set_age(self, age):
        self._age = age

    age = property(_get_age, _set_age)


class Bar(Foo):
    def _get_age(self):
        return 44

    age = property(_get_age, Foo._set_age)

Это довольно надуманный пример, но вы должны уловить идею.

Я не согласен с тем, что выбранный ответ является идеальным способом для переопределения методов свойств. Если вы ожидаете, что геттеры и сеттеры будут переопределены, то вы можете использовать лямбда для обеспечения доступа к себе, с чем-то вроде lambda self: self.<property func>.

это работает (по крайней мере) для версий Python 2.4-3.6.

если кто-нибудь знает способ сделать это с помощью property в качестве декоратора вместо прямого вызова property (), я хотел бы услышать это!

пример:

class Foo(object):
    def _get_meow(self):
        return self._meow + ' from a Foo'
    def _set_meow(self, value):
        self._meow = value
    meow = property(fget=lambda self: self._get_meow(),
                    fset=lambda self, value: self._set_meow(value))

таким образом, переопределение может быть легко выполнено:

class Bar(Foo):
    def _get_meow(self):
        return super(Bar, self)._get_meow() + ', altered by a Bar'

так что:

>>> foo = Foo()
>>> bar = Bar()
>>> foo.meow, bar.meow = "meow", "meow"
>>> foo.meow
"meow from a Foo"
>>> bar.meow
"meow from a Foo, altered by a Bar"

я обнаружил это на технарь в игре.

Да, это способ сделать это; объявление свойства выполняется во время выполнения определения родительского класса, что означает, что он может только "видеть" версии методов, которые существуют в родительском классе. Поэтому, когда вы переопределяете один или несколько из этих методов в дочернем классе, вам нужно повторно объявить свойство, используя версию дочернего класса метода(ов).

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

что-то вроде этого будет работать

class HackedProperty(object):
    def __init__(self, f):
        self.f = f
    def __get__(self, inst, owner):    
        return getattr(inst, self.f.__name__)()

class Foo(object):
    def _get_age(self):
        return 11
    age = HackedProperty(_get_age)

class Bar(Foo):
    def _get_age(self):
        return 44

print Bar().age
print Foo().age

то же, что и @mr-bно с декоратором.

class Foo(object):
    def _get_meow(self):
        return self._meow + ' from a Foo'
    def _set_meow(self, value):
        self._meow = value
    @property
    def meow(self):
        return self._get_meow()
    @meow.setter
    def meow(self, value):
        self._set_meow(value)

таким образом, переопределение может быть легко выполнено:

class Bar(Foo):
    def _get_meow(self):
        return super(Bar, self)._get_meow() + ', altered by a Bar'

я столкнулся с проблемами, устанавливая свойство в родительском классе из дочернего класса. Следующий workround расширяет свойство родителя, но делает это путем прямого вызова метода _set_age родителя. Морщины всегда должны быть правильными. Это немного javathonic, хотя.

import threading


class Foo(object):
    def __init__(self):
        self._age = 0

    def _get_age(self):
        return self._age

    def _set_age(self, age):
        self._age = age

    age = property(_get_age, _set_age)


class ThreadsafeFoo(Foo):

    def __init__(self):
        super(ThreadsafeFoo, self).__init__()
        self.__lock = threading.Lock()
        self.wrinkled = False

    def _get_age(self):
        with self.__lock:
             return super(ThreadsafeFoo, self).age

    def _set_age(self, value):
        with self.__lock:
            self.wrinkled = True if value > 40 else False
            super(ThreadsafeFoo, self)._set_age(value)

    age = property(_get_age, _set_age)