web-dev-qa-db-fra.com

propriétés et héritage python

J'ai une classe de base avec une propriété que (la méthode get) je veux écraser dans la sous-classe. Ma première pensée a été quelque chose comme:

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

    age = property(_get_age)


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

Cela ne fonctionne pas (la sous-classe bar.age renvoie 11). J'ai trouvé une solution avec une expression lambda qui fonctionne:

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

Donc, est-ce la bonne solution pour utiliser des propriétés et les écraser dans une sous-classe, ou y a-t-il d'autres façons préférées de le faire?

53
Peter Hoffmann

Je préfère simplement répéter le property() ainsi que le décorateur @classmethod lors du remplacement d'une méthode de classe. 

Bien que cela semble très bavard, du moins pour les standards Python, vous remarquerez peut-être:

1) pour les propriétés en lecture seule, property peut être utilisé comme décorateur:

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

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

2) dans Python 2.6, properties a développé une paire de méthodessetter et deleter qui peuvent être utilisées pour appliquer aux propriétés générales le raccourci déjà disponible pour celles en lecture seule:

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

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

Une autre façon de le faire, sans avoir à créer de classes supplémentaires. J'ai ajouté une méthode set pour montrer ce que vous faites si vous ne remplacez que l'un des deux:

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)

Ceci est un bel exemple artificiel, mais vous devriez avoir l’idée.

11
Kamil Kisiel

Je ne pense pas que la réponse choisie soit le moyen idéal de permettre de remplacer les méthodes de propriété. Si vous vous attendez à ce que les accesseurs et les setters soient remplacés, vous pouvez utiliser lambda pour fournir un accès à soi-même, avec quelque chose comme lambda self: self.<property func>.

Cela fonctionne (au moins) pour les versions de Python 2.4 à 3.6.

Si quelqu'un connaît un moyen de le faire en utilisant propriété en tant que décorateur au lieu d'appeler directement propriété (), j'aimerais l'entendre!

Exemple:

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))

De cette façon, une substitution peut être facilement effectuée:

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

pour que:

>>> 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"

J'ai découvert cela sur geek en jeu .

11
Mr. B

Oui, c'est la façon de le faire. la déclaration de propriété s'exécute au moment de l'exécution de la définition de la classe parente, ce qui signifie qu'elle ne peut "voir" que les versions des méthodes existant dans la classe parente. Ainsi, lorsque vous redéfinissez une ou plusieurs de ces méthodes sur une classe enfant, vous devez re-déclarer la propriété à l'aide de la version de la ou des méthodes de la classe enfant.

7
James Bennett

Je suis d'accord avec votre solution, qui semble une méthode de modèle à la volée. Cet article traite de votre problème et fournit exactement votre solution.

2

Identique à @ mr-b mais avec un décorateur. 

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)

De cette façon, une substitution peut être facilement effectuée:

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

Quelque chose comme ça va marcher

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
Kozyarchuk

J'ai rencontré des problèmes lors de la définition d'une propriété dans une classe parent à partir d'une classe enfant. Le workround suivant étend une propriété d'un parent, mais en appelant directement la méthode _set_age du parent. Ridée devrait toujours être correcte. C'est un peu javathonique cependant.

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)
0
andrew pate
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

Identique à Nizam Mohamed, juste pour mentionner que guide de style 2.13.4 en utilisant à la fois la méthode template et la propriété

0
Robin