strategy pattern and non-public virtual functions

Discussion in 'Python' started by pythoncurious, Jun 5, 2006.

  1. Hi python experts

    In C++ I can do something like this:
    class Base {
    public:
    void f() { this->f_(); }
    private:
    virtual void f_() = 0;
    };

    class Derived : public Base {
    private:
    void f_() { // Do something }
    };

    int main() {
    Derived d;
    d.f();
    }

    The point of this is that the a number of classes will inherit from
    Base and only implement a private member function that only will be
    accessed from the base class public 'f' function.
    The Base::f() can then perform validation of input/return values, add
    logging and things like that.
    The users of the derived classes are unable to bypass this base class
    function.

    I've been wanting to do the same thing in python, to make sure that
    there is no confusion about what function to call.

    Just translating this code to python won't work, due to the name
    mangling of private functions:
    class B(object):
    def f(self):
    self.__f()

    class D(B):
    def __f(self):
    pass

    d = D()
    d.f()

    Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    File "<stdin>", line 3, in f
    AttributeError: 'D' object has no attribute '_B__f'

    So my questions are:
    1. Is there a "pythonic" way to do what I'm trying to do?
    2. Should I be doing this at all? Any thoughts?
     
    pythoncurious, Jun 5, 2006
    #1
    1. Advertisements

  2. pythoncurious

    Duncan Booth Guest

    wrote:
    Just to be clear, the users of the derived C++ classes *are* able to bypass
    the base class function and call f_() directly, they just have to be
    prepared to twist the rules somewhat. I know: I've been in the situation
    where there was absolutely no other way to get the job done other than to
    call a private method in a class supplied in a library which I couldn't
    change.

    To do the same in Python you simply make a rule 'dont call f_ directly' and
    you get effectively the same thing: users can call f_ if they really want,
    but they are breaking the rules if they do. The difference in Python is
    that you don't have to go to such extremes to break the rules.

    The Pythonic way to do it:

    class B(object):
    def f(self):
    self._f()

    class D(B):
    def _f(self):
    pass

    d = D()
    d.f()

    Calling a method with a single leading underscore from outside the
    implementation of the class is generally accepted in Python to be 'breaking
    the rules' and therefore something only to be done by consenting adults in
    the full knowledge of the consquences of their actions.
     
    Duncan Booth, Jun 5, 2006
    #2
    1. Advertisements

  3. Just use a single underscore, i.e. "_f" instead of "__f".

    I can't spot the strategy pattern here; neither in the C++ code
    nor in the Python code. For this to be the strategy pattern,
    you should have two objects: the context, and the strategy
    object. So for example, you could have

    class Context:
    def f(self):
    return self.strategy.f()

    class D:
    def f(self):
    pass

    Then, assigning to context.strategy lets you change the
    strategy dynamically.

    It's not clear to what you are trying achieve with your
    pattern, so it is hard to tell whether you should do this
    at all. Most likely, the answer is "no".

    If you are really looking for the strategy pattern:
    be aware that people often use the strategy pattern
    as a work-around for functions not being first-class
    objects. In Python, they are, so you can often allow
    for arbitrary callables in the strategy pattern.

    Regards,
    Martin
     
    =?ISO-8859-1?Q?=22Martin_v=2E_L=F6wis=22?=, Jun 5, 2006
    #3
  4. Le Lundi 05 Juin 2006 16:07, a écrit :
    This is just polymorphism, not strategy pattern, and I would expect f_ to be
    protected here not private.

    You want to ensure derived class will use a given method in the Base class,
    this could be done explicit with a good naming convention as Duncan said, but
    here is a strategy pattern to ensure a sanity check for example :

    class strategyBase(object) :
    def __call__(self, *sa) : raise NotImplementedError('abstract class')

    class InputProcessor(object) :

    def sanitize(self, *a) :
    return a

    def f(self, *a) :
    sa = self.sanitize(*a)
    return self.strategy(*sa)

    def __setStrategy(self, strat) :
    if not isinstance(strat, strategyBase) :
    raise ValueError("strat must be of type strategyBase")
    self.__strat = strat

    strategy = property(fget=lambda s : s.__strat, fset=__setStrategy)

    The main purpose of this is to define a common API for all Strategies, and
    this is really useful if you intend to manage many of them.

    --
    _____________

    Maric Michaud
    _____________

    Aristote - www.aristote.info
    3 place des tapis
    69004 Lyon
    Tel: +33 426 880 097
     
    Maric Michaud, Jun 5, 2006
    #4
  5. pythoncurious

    John J. Lee Guest

    [...]

    The others have already answered the Python question and pointed out
    this isn't really the Strategy Pattern.

    Still, these slides from Alex Martelli specifically on the Template
    Method DP in Python are probably very relevant to you:

    http://www.aleax.it/Python/os03_template_dp.pdf


    John
     
    John J. Lee, Jun 5, 2006
    #5
  6. a écrit :
    <ot>
    This is eventually the template method pattern, but certainly not the
    strategy pattern.
    This is usually done in Python with function decorators. But the
    implementer of the derived class

    (snip - cf other posts in this thread)
    Doing what ? adding logging, validation etc, or using the template
    method pattern ?-)
     
    Bruno Desthuilliers, Jun 6, 2006
    #6
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.