Making every no-arg method a property?

Steven D'Aprano steve+comp.lang.python at pearwood.info
Tue Aug 5 20:49:24 EDT 2014


Christian Calderon wrote:

> I have been using python for 4 years now, and I just started learning
> ruby. I like that in ruby I don't have to type parenthesis at the end of
> each function call if I don't need to provide extra arguments.

That's one of the things which I dislike most about Ruby.

Python has a nice, clean design: obj.method returns the method object, x()
on any object calls it. (Possibly not successfully, if it is not a type,
function or method, but it makes the attempt.) Making the parentheses
optional just confuses the two, just for the sake of saving a couple of
keystrokes.


> I just 
> realized right now that I can do something similar in python, if I make
> all methods with only the implicitly passed 'self' into properties. 

I wouldn't expect that there should be very many of such methods (except in,
say, a type like str, where you have a bunch of transformation methods like
str.upper() etc. -- but they shouldn't be treated as properties at all). A
plethora of argument-less methods is a code smell -- that doesn't mean it's
*necessarily* a bad idea, but the class design really needs a careful
review. The problem with argument-less methods is that things which should
be temporary arguments are being stored as permanent state instead. E.g. if
you're calling methods just for their side-effect of setting up a bunch of
attributes, just so you can then do what you really want which is call
another method, that's a bad design which should be refactored to pass
arguments direct to the method:

# Bad
class SandwichMaker:
    def prepare_sandwich_ingredients(self, bread, butter, 
                fillings, openface=False):
        self._bread = bread
        self.butter = butter
        self.fillings = fillings
        self.openface = openface
    def make_sandwich(self):
        base = self.bread
        if not self.openface:
            cover = self.bread
        else:
            cover = None
        if self.butter in ('butter', 'margarine', 'mayo'):
            base += self.butter
            if cover: 
                cover += self.butter
        for filling in self.fillings:
            self.thing_to_get = filling
            base += self.get_ingredient()
        if cover:
            # butter goes on the inside, not the top
            self.thing_to_flip = cover
            self.flip_the_thing_that_needs_flipping()
            base += self.thing_to_flip
        return base            

Obviously this is a fanciful example, but I've seen too much real code where
too many methods existed solely to stuff data into an attribute so another
method could get to it, after which that attribute was unneeded. It's the
OOP equivalent of writing functions which communicate only by global
variable.


> Which 
> means I can either do some fancy coding and make a metaclass that does
> this auto-magically, or I have to have property decorators all over the
> place
> :-P . I was wondering what other thought of this, is it an overly fanciful
> way of coding python, or is it an acceptable thing to do in a real
> project? Also, would anyone be interested in helping me make this
> metaclass?

Personally, I think it's a horrible idea, but if you must do it, I'd suggest
doing it with a class decorator, not a metaclass. Something like this:


# Untested.
def make_zero_arg_methods_into_properties(cls):
    for name, obj in vars(cls).items():
        if inspect.isfunction(obj):
            if inspect.getargspec(obj) == (['self'], None, None, ()):
                setattr(cls, name, property(obj))
    return cls

@make_zero_arg_methods_into_properties
class Spam:
    def eggs(self):
        ...    



-- 
Steven




More information about the Python-list mailing list