свойства и наследование Python - PullRequest
55 голосов
/ 26 октября 2008

У меня есть базовый класс со свойством, которое (метод 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())

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

Ответы [ 8 ]

65 голосов
/ 26 октября 2008

Я просто предпочитаю повторять 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
16 голосов
/ 16 января 2013

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

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

11 голосов
/ 15 ноября 2008

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

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

7 голосов
/ 26 октября 2008

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

2 голосов
/ 21 мая 2016

То же, что и @ 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'
2 голосов
/ 26 октября 2008

Как то так будет работать

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
2 голосов
/ 26 октября 2008

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

0 голосов
/ 02 июня 2015

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