0

I have something written in Perl/Moose that uses method modifiers (before/after/around) and I'm wondering what the "Pythonic" way of composing functionality into a method would be?

To expand, I have a data structure that needs to be processed, data is "tagged" to mark it in terms of how it should be processed. I check those tags and then dynamically build my processor accordingly. Using Perl's Moose I can pull in the "traits" I need and then run a single method to process the data. Each "Role" I pull in assumes there's a "process" method and it just bolts it's functionality on with an after 'process' => sub { }. This way if I have a new type of data to deal with I can simply add another Role without having to touch any other code. All this specifically avoids any inheritance and is very simple to manage.

I thought about trying to recreate Moose's "method modifiers" using decorators and/or metaclasses, but I'm wondering what the idiomatic way would be in Python...

Thanks

steverippl
  • 378
  • 4
  • 15
  • 1
    An example might be useful... – thebjorn Dec 13 '13 at 22:19
  • 1
    I agree with @thebjorn, some example code in Python would really help. You might be on the right track with decorators. Writing your own [context managers](http://docs.python.org/2/library/contextlib.html) and use their [`__enter__`](http://docs.python.org/2/reference/datamodel.html#object.__enter__) and [`__exit__`](http://docs.python.org/2/reference/datamodel.html#object.__exit__) methods could be another option. – Lukas Graf Dec 13 '13 at 22:30
  • I'm afraid I haven't written any Python code for this yet! I have a bunch of Perl code and am wondering how to approach the processor class. I hadn't considered context managers, I'll look into that... – steverippl Dec 13 '13 at 22:47
  • Also, don't discard decorators yet. They can be nested as well, and could also be used well for this purpose I think. – Lukas Graf Dec 13 '13 at 23:42

2 Answers2

1

I hardly know any Perl, but from looking at the first example in the MethodModifier docs, it seems to me context managers in Python would be the thing that most resembles Moose's method modifiers:

class MyContextManager(object):
    def __init__(self, data):
        self.data = data

    def __call__(self):
        print 'foo'

    def __enter__(self):
        print "entering... (data=%s)" % self.data
        # this will be bound to the name after the 'as' clause 
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        print "exiting... (data=%s)" % self.data

with MyContextManager('bar') as manager:
    print "before"
    manager()
    print "after"

Output:

entering... (data=bar)
before
foo
after
exiting... (data=bar)

Unfortunately, documentation for context managers is a bit spread out:

Lukas Graf
  • 30,317
  • 8
  • 77
  • 92
  • 1
    What I'm really looking for is the ability to alter what gets pulled in during the runtime, but I think context managers might be the right approach. @MartijnPieters answer to [Alternative to contextlib.nested with variable number of context managers](http://stackoverflow.com/questions/16083791/alternative-to-contextlib-nested-with-variable-number-of-context-managers) shows how to employ a variable number of contexts. – steverippl Dec 13 '13 at 23:16
  • I guess I was just wondering if this is an approach used in Python much (it doesn't seem so). Perl's Moose has this idiom with easy syntax and so that's how I've learnt to think about it. Thanks for pointing this out though... – steverippl Dec 13 '13 at 23:20
  • You're right, they're not used very often - there's some good uses for them though. Doing a block of statements in a different security context, or using a context manager as a logger that streams a long running HTTP response for example. You can implement context managers for that with a couple lines, and get a very nice syntax (the `with` statement) to use them. – Lukas Graf Dec 13 '13 at 23:34
0

Just to expand on @LukasGraf answer pulling in contexts dynamically... (in Python 3)

from contextlib import ExitStack

class Manager1():
    def __init__(self, data):
        self.data = data

    def __enter__(self):
        print("entering 1... (data=%s)" % self.data)
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        print("exiting 1... (data=%s)" % self.data)



class Manager2():
    def __init__(self, data):
        self.data = data

    def __enter__(self):
        print("entering 2... (data=%s)" % self.data)
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        print("exiting 2... (data=%s)" % self.data

contexts = [Manager2('test'), Manager1('test')]

with ExitStack() as stack:
    [ stack.enter_context(context) for context in contexts ]
    print('here')

generates...

entering 2... (data=test)
entering 1... (data=test)
here
exiting 1... (data=test)
exiting 2... (data=test)
steverippl
  • 378
  • 4
  • 15