views:

53

answers:

3

I recently discovered Python's property built-in, which disguises class method getters and setters as a class's property. I'm now being tempted to use it in ways that I'm pretty sure are inappropriate.

Using the property keyword is clearly the right thing to do if class A has a property _x whose allowable values you want to restrict; i.e., it would replace the getX() and setX() construction one might write in C++.

But where else is it appropriate to make a function a property? For example, if you have

class Vertex(object):
    def __init__(self):
        self.x = 0.0
        self.y = 1.0

class Polygon(object):
    def __init__(self, list_of_vertices):
        self.vertices = list_of_vertices
    def get_vertex_positions(self):
        return zip( *( (v.x,v.y) for v in self.vertices ) )

is it appropriate to add

    vertex_positions = property( get_vertex_positions )

?

Is it ever ok to make a generator look like a property? Imagine if a change in our code meant that we no longer stored Polygon.vertices the same way. Would it then be ok to add this to Polygon?

    @property
    def vertices(self):
        for v in self._new_v_thing:
            yield v.calculate_equivalent_vertex()
A: 

There is an obvious limitation in the use of properties: it does not accept any argument, and it will never do.

So you must be sure that the function you are transforming into a property will never be refactored into a function with, for example, extra default arguments.

Olivier
A: 

Yes, that's perfectly appropriate; you can use a @property everywhere something needs to look like a property. But be sure that you are clear to the clients of your interface that they should expect an iterable, not a list.

wump
+1  A: 
  • When you have a normal attribute and getting and/or setting it makes sense for a class's user, expose the attribute directly. One big reason that public members are anathema in some languages is that if you need to do something more complex later you would need an API change; in Python you can just define a property.

  • If you are using something you should abstract as attribute access, use property. If you want to make external state (your plot or website or something) aware of the change or if you are wrapping some library that uses members directly, properties might be the way to go.

  • If something isn't attribute-y, don't make it a property. There's no harm in making a method, and it can be advantageous: what it does is more obvious, you can pass around some bound method if you need, you can add keyword arguments without an API change.

    It's hard to imagine a situation where I would use a generator function as a property. The only way to have a normal attribute that would behave at all similarly would require quite a lot of complexity, so that case isn't very reminiscent of attribute access.

  • You point out you can use property for restricting access to some internal attribute _x. This can be true, but keep in mind

    • If you are going to do things like sanitize input for security or something important, explicit is better than implicit. You don't want to feel like code just-works when it comes to such things, because then you'll run into code that doesn't.

    • Sometimes people use property to implement read-only attributes. It's usually better just to have a normal attribute and realize you can't stop a user from doing something dumb and unsupported.

  • Nitpicking you might find interesting:

    • property isn't a keyword; it's a normal name you can rebind. This is sort of interesting since property isn't a syntax thing or anything: it's a normal class you could implement yourself in pure Python. It uses the same mechanism that makes methods work in Python—descriptors.

    • You describe what property does as "disguises class method getters and setters", which isn't quite the case. The things that property take are just normal functions, and needn't actually be defined in your class at all; property will pass self for you. Functions don't actually become methods until you look them up, when Python creates method objects on-the-fly. During the class definition, they are just functions. When you have a normal method it's called an "instance method"; in Python "class method" refers to another special thing sort of like properties that changes what happens when an attribute is looked up.

Mike Graham