1

I have a two-class inheritance hierarchy:

class TfIdfSimilarity(SimilarityModel):
    @property
    def corpus(self):
        return self.model[self._corpus]

    @property
    def model(self):
        return TfidfModel(self._corpus, dictionary=self._dict)


class LsiSimilarity(TfIdfSimilarity):
    @property
    def corpus(self):
        tfidf_corpus = super().corpus
        return self.model[tfidf_corpus]

    @property
    def model(self):
        tfidf_corpus = super().corpus
        return LsiModel(tfidf_corpus)

However, this is causing an infinite recursion error, because whenever I try to get the LsiSimilarity#model, it calls TfIdfSimilarity#corpus, which then tries to access the model, and thus calls LsiSimilarity#model again.

What I actually want is for TfIdfSimilarity#corpus to only ever call TfIdfSimilarity#model, and never consider the child class's version of it.

Is this possible in Python? (Python 3) If not, how can I better structure my inheritance hierarchy, while still providing the same API via the corpus and model functions (which are used by my main logic).

Migwell
  • 18,631
  • 21
  • 91
  • 160

2 Answers2

2

You can invoke descriptors directly by getting a reference to the descriptor from the class and then calling the __get__ method of the descriptor:

TfIdfSimilarity.model.__get__(self)

If you find yourself doing this a lot, it may be a sign that you should be using regular methods instead of properties.

Patrick Haugh
  • 59,226
  • 13
  • 88
  • 96
  • Note that while this works for `property`, this is due to the specific implementation of `property.__get__` (which returns `self` if no instance is provided) and is by no mean garanteed to be the case for all descriptors (ie in py2 `FunctionType.__get__` returns an unbound method, not the function itself). – bruno desthuilliers May 10 '19 at 13:47
  • After some searching, I now found a solution without a call to `__get__` :) – Sparky05 May 10 '19 at 15:27
1

You can always specify which method you want to call using instead of self.method_xy a call with SpecificClass.method(self, other_arguments).

I first missed the implications of the property decorator and switched to the solution with calling __get__ (TfIdfSimilarity.model.__get__(self)), but since it is a call to a private method I don't liked the solution.

Inspired by this How to call a property of the base class if this property is being overwritten in the derived class?, I found the method fget:

Minimal example

class Test:
    def __init__(self, text):
        self._text = text

    @property
    def text(self):
        return self._text

class InheritTest(Test):

    @property
    def text(self):
        return "test" + self._text    


test = InheritTest("Test")
print(Test.text.fget(test))
# Test
print(test.text)
# testTest
Sparky05
  • 4,692
  • 1
  • 10
  • 27
  • Did you try this? `TfIdfSimilarity.model` isn't a method, it's a [property](https://docs.python.org/3/library/functions.html#property). If you do this, you're going to be told that it's not callable. – Patrick Haugh May 10 '19 at 13:34
  • Yeah you'd need to "deref" the property descriptor by calling `__get__(self)` – Masklinn May 10 '19 at 13:37
  • I wasn't aware of additional call needed for properties. Have now included the additional call. Thanks to Masklinn – Sparky05 May 10 '19 at 13:41