Подтвердить что ты не робот

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

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

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

    age = property(_get_age)


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

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

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

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

4b9b3361

Ответ 1

Я просто предпочитаю повторять 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

Ответ 2

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

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

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

Пример:

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"

Я обнаружил это на вундеркинде в игре.

Ответ 3

Другой способ сделать это, не создавая дополнительных классов. Я добавил метод 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)

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

Ответ 4

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

Ответ 5

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

Ответ 6

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

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

Ответ 7

То же, что @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'

Ответ 8

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

Ответ 9

class Foo:
    # Template method
    @property
    def age(self):
        return self.dothis()
    # Hook method of TM is accessor method of property at here
    def dothis(self):
        return 11
class Bar(Foo):
    def dothis(self):
        return 44

То же, что и Низам Мохамед, только упомянуть, что руководство по стилю 2.13.4 использует метод и свойство шаблона