pass object or use self.object?

T

Tim Arnold

Hi,
I have a few classes that manipulate documents. One is really a
process that I use a class for just to bundle a bunch of functions
together (and to keep my call signatures the same for each of my
manipulator classes).

So my question is whether it's bad practice to set things up so each
method operates on self.document or should I pass document around from
one function to the next?
pseudo code:

class ManipulatorA(object):
def process(self, document):
document = self.do_one_thing(document)
document = self.do_another_thing(document)
# bunch of similar lines
return document

or

class ManipulatorA(object):
def process(self, document):
self.document = document
self.do_one_thing() # operates on self.document
self.do_another_thing()
# bunch of similar lines
return self.document

I ask because I've been told that the first case is easier to
understand. I never thought of it before, so I'd appreciate any
comments.
thanks,
--Tim
 
B

Bruno Desthuilliers

Tim Arnold a écrit :
Hi,
I have a few classes that manipulate documents. One is really a
process that I use a class for just to bundle a bunch of functions
together (and to keep my call signatures the same for each of my
manipulator classes).

So my question is whether it's bad practice to set things up so each
method operates on self.document or should I pass document around from
one function to the next?

As far as I'm concerned, I strongly prefer passing the document around.
Makes thing clear, avoids useless preconditions (is self.document set
???) and race conditions (if two threads have to share the Manipulator
instance), makes the code easier to understand / maintain / refactor IMHO.

Also remember that modules are objects too, so - depending on parts of
your code we don't see here - you may even maintain your API without
having to use a "class as module".

My 2 cents
 
J

Jean-Michel Pichavant

Tim said:
Hi,
I have a few classes that manipulate documents. One is really a
process that I use a class for just to bundle a bunch of functions
together (and to keep my call signatures the same for each of my
manipulator classes).

So my question is whether it's bad practice to set things up so each
method operates on self.document or should I pass document around from
one function to the next?
pseudo code:

class ManipulatorA(object):
def process(self, document):
document = self.do_one_thing(document)
document = self.do_another_thing(document)
# bunch of similar lines
return document

or

class ManipulatorA(object):
def process(self, document):
self.document = document
self.do_one_thing() # operates on self.document
self.do_another_thing()
# bunch of similar lines
return self.document

I ask because I've been told that the first case is easier to
understand. I never thought of it before, so I'd appreciate any
comments.
thanks,
--Tim
Usually, when using classes as namespace, functions are declared as
static (or as classmethod if required).
e.g.


class Foo:
@classmethod
def process(cls, document):
print 'process of'
cls.foo(document)

@staticmethod
def foo(document):
print document

In [5]: Foo.process('my document')
process of
my document


There is no more question about self, 'cause there is no more self. You
don't need to create any instance of Foo neither.

JM
 
L

Lie Ryan

Hi,
I have a few classes that manipulate documents. One is really a
process that I use a class for just to bundle a bunch of functions
together (and to keep my call signatures the same for each of my
manipulator classes).

So my question is whether it's bad practice to set things up so each
method operates on self.document or should I pass document around from
one function to the next?
pseudo code:

class ManipulatorA(object):
def process(self, document):
document = self.do_one_thing(document)
document = self.do_another_thing(document)
# bunch of similar lines
return document

or

class ManipulatorA(object):
def process(self, document):
self.document = document
self.do_one_thing() # operates on self.document
self.do_another_thing()
# bunch of similar lines
return self.document

Since in function in python is a first-class object, you can instead do
something like:

def process(document):
# note: document should encapsulate its own logic
document.do_one_thing()
document.do_another_thing()

And when you need some complex logic, you can easily elevate your
function to a class:

class Appender(object):
def __init__(self, text):
self.text = text
def __call__(self, document):
mtext = self.manipulate(document, text)
document.append(mtext)

and I think for your purpose, the mixin pattern could cleanly separate
manipulation and document while still obeying object-oriented pattern
that document is self-sufficient:

# language with only single-inheritance can only dream to do this
class Appendable(object):
def append(self, text):
self.text += text
class Savable(object):
def save(self, fileobj):
fileobj.write(self.text)
class Openable(object):
def open(self, fileobj):
self.text = fileobj.read()
class Document(Appendable, Savable, Openable):
def __init__(self):
self.text = ''
 
B

Bruno Desthuilliers

Lie Ryan a écrit :
(snip)
Since in function in python is a first-class object, you can instead do
something like:

def process(document):
# note: document should encapsulate its own logic
document.do_one_thing()

Obvious case of encapsulation abuse here. Should a file object
encapsulate all the csv parsing logic ? (and the html parsing, xml
parsing, image manipulation etc...) ? Should a "model" object
encapsulate the presentation logic ? I could go on for hours here...
and I think for your purpose, the mixin pattern could cleanly separate
manipulation and document while still obeying object-oriented pattern
that document is self-sufficient:

# language with only single-inheritance can only dream to do this
>
class Appendable(object):
def append(self, text):
self.text += text
class Savable(object):
def save(self, fileobj):
fileobj.write(self.text)
class Openable(object):
def open(self, fileobj):
self.text = fileobj.read()
class Document(Appendable, Savable, Openable):
def __init__(self):
self.text = ''

Anyone having enough experience with Zope2 knows why this sucks big time.
 
T

Tim Arnold

Tim said:
Hi,
I have a few classes that manipulate documents. One is really a
process that I use a class for just to bundle a bunch of functions
together (and to keep my call signatures the same for each of my
manipulator classes).
So my question is whether it's bad practice to set things up so each
method operates on self.document or should I pass document around from
one function to the next?
pseudo code:
class ManipulatorA(object):
    def process(self, document):
        document = self.do_one_thing(document)
        document = self.do_another_thing(document)
        # bunch of similar lines
        return document

class ManipulatorA(object):
    def process(self, document):
        self.document = document
        self.do_one_thing() # operates on self.document
        self.do_another_thing()
        # bunch of similar lines
        return self.document
I ask because I've been told that the first case is easier to
understand. I never thought of it before, so I'd appreciate any
comments.
thanks,
--Tim

Usually, when using classes as namespace, functions are declared as
static (or as classmethod if required).
e.g.

class Foo:
    @classmethod
    def process(cls, document):
        print 'process of'
        cls.foo(document)

    @staticmethod
    def foo(document):
        print document

In [5]: Foo.process('my document')
process of
my document

There is no more question about self, 'cause there is no more self. You
don't need to create any instance of Foo neither.

JM

Thanks for the input. I had always wondered about static methods; I'd
ask myself "why don't they just write a function in the first place?"

Now I see why. My situation poses a problem that I guess static
methods were invented to solve. And it settles the question about
using self.document since there is no longer any self. And as Bruno
says, it's easier to understand and refactor.

thanks,
--Tim
 
L

Lie Ryan

Lie Ryan a écrit :
(snip)


Obvious case of encapsulation abuse here. Should a file object
encapsulate all the csv parsing logic ? (and the html parsing, xml
parsing, image manipulation etc...) ? Should a "model" object
encapsulate the presentation logic ? I could go on for hours here...

Yes, but no; you're taking it out of context. Is {csv|html|xml|image}
parsing logic a document's logic? Is presentation a document's logic? If
they're not, then they do not belong in document.
 
B

Bruno Desthuilliers

Lie Ryan a écrit :
Yes, but no; you're taking it out of context. Is {csv|html|xml|image}
parsing logic a document's logic? Is presentation a document's logic? If
they're not, then they do not belong in document.

Is len() a list logic ? If yes, it should belong to list !-)

There are two points here : the first is that we (that is, at least, you
and me) just don't know enough about the OP's project to tell whether
something should belong to the document or not. period. The second point
is that objects don't live in a splendid isolation, and it's perfectly
ok to have code outside an object's method working on the object.

wrt/ these two points, your "document should encapsulate its own logic"
note seems a bit dogmatic (and not necessarily right) to me - hence my
answer.
 
T

Tim Arnold

Lie Ryan a écrit :







Is len() a list logic ? If yes, it should belong to list !-)

There are two points here : the first is that we (that is, at least, you
and me) just don't know enough about the OP's project to tell whether
something should belong to the document or not. period. The second point
is that objects don't live in a splendid isolation, and it's perfectly
ok to have code outside an object's method working on the object.

wrt/ these two points, your "document should encapsulate its own logic"
note seems a bit dogmatic (and not necessarily right) to me - hence my
answer.

The 'document' in this case is an lxml Elementtree, so I think it
makes sense to have code outside the object (e.g. static methods)
working on the object.
thanks,
--Tim
 
L

Lie Ryan

Lie Ryan a écrit :

Is len() a list logic ? If yes, it should belong to list !-)

Yes, that's why list.__len__() belongs to list while len() is a
convenience function that doesn't carry any concrete implementation.
There are two points here : the first is that we (that is, at least, you
and me) just don't know enough about the OP's project to tell whether
something should belong to the document or not. period.

I think I see your point here. I retract my suggestion that it is
suitable for OP's purpose since I just realized OP is in a better
position to make the decision.
The second point
is that objects don't live in a splendid isolation, and it's perfectly
ok to have code outside an object's method working on the object.
wrt/ these two points, your "document should encapsulate its own logic"
note seems a bit dogmatic (and not necessarily right) to me - hence my
answer.

I agree with you about there are certain logics that should not be
inside the object (that's why I qualify the statement with `should`).
Glue logic, by definition, cannot be inside an object. I don't think we
are actually in disagreement here. But I think the dogma, followed with
caution, is generally good.
 
B

Bruno Desthuilliers

Tim Arnold a écrit :
On Apr 8, 4:20 am, Bruno Desthuilliers <bruno. (snip)

The 'document' in this case is an lxml Elementtree, so I think it
makes sense to have code outside the object.

Indeed. It's the only sensible thing to do here.
 

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. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top