3

I've been doing a lot of work in C# recently (my primary language is Python) and I love that I can do something like

public string MyProperty{ get; set; }

Going back to Python I miss that sort of functionality. I don't like that I would have to do

class MyClass:

    @property
    def foo(self):
        return self._foo

    @foo.setter
    def foo(self, value):
        self._foo = value

a = MyClass()
a.foo = 7
print(a.foo)

I thought that this was something I could do pretty easily with a descriptor, like so.

class MyClass:
    foo = AutoProperty()

a = MyClass()
a.foo = 7
print(a.foo)

Unfortunately, I realized while implementing this that I was violating my favorite design principle - KISS. After all, what I'd been thinking about were just instance variables.

class MyClass(): pass

a = MyClass()
a.foo = 7
print(a.foo)

The reason we don't always use properties by default in Python is that, usually, getting and setting instance variables isn't nearly as involved as languages like Java make it - mostly because the concept of private, protected, and public fields don't really exist in Python, except by (weak) conventions and language features, and because changing from a "field" to a "property" isn't a breaking change in Python.

If we later decide that we do need more complex validation while setting or calculations while getting, then we can easily do that by implementing properties and not affecting the interface of our class.

I'm still wondering though - is there any sort of way this technique, or a modified version of this technique, could be used to provide a useful and Pythonic feature?

  • 1
    Getting and setting in Java and C# aren't a struggle at all. We do it because public variables are a bad idea for several reasons, the most important of which (IMO) is that changing a public variable to a property is a breaking change. As you rightly point out, there isn't much point to setters and getters in a language that has a weak concept of encapsulation anyway. – Robert Harvey Jul 26 '15 at 17:35
  • @RobertHarvey that's a good point; describing them as a struggle is probably a bad choice of words – Dannnno Jul 26 '15 at 17:46
  • 1
    As you point out, you can easily go from an attribute to a property in Python without affecting the interface, so there's really no point in implementing a descriptor that only redirects to a private-by-convention attribute. – jonrsharpe Jul 26 '15 at 20:55
  • @jonrsharpe since this hasn't gotten much traction, and after further thought and research I tend to agree that there isn't much of a use case, feel free to answer to that effect and I'll accept. – Dannnno Jul 29 '15 at 19:12
2

In Python, switching between an attribute and a property isn't a breaking change, it doesn't alter the interface at all, so there's not much point to an auto-property system. You can start with a public attribute, then later on switch to a property without needing to alter the code that interfaces with your class at all.

This is also why defining get_ and set_ methods in Python is discouraged; the property is a much neater way of protecting access, and means that you don't have to implement those methods from the beginning in case you later need them to do something non-trivial. See e.g. Do you use the get/set pattern (in Python)? on SO.


If you did decide to implement an AutoProperty you should add __delete__, too; I use the following as a base for other descriptors:

class BaseDescriptor(object):
    """Descriptor base class, providing basic set, get and del methods.

    Arguments:
      name (str): The attribute to redirect access to (usually the name
        to which the descriptor is assigned with a leading underscore,
        indicating private-by-convention).

    Example:

        >>> class TestClass(object):
        ...     foo = BaseDescriptor('_foo')


        >>> inst = TestClass()
        >>> inst.foo = 'bar'
        >>> inst.foo
        'bar'
        >>> del inst.foo
        >>> inst.foo
        Traceback (most recent call last):
          ...
        AttributeError: 'TestClass' object has no attribute '_foo'

    """

    def __init__(self, name):
        self.name = name

    def __get__(self, obj, typ=None):
        return getattr(obj, self.name)

    def __set__(self, obj, val):
        setattr(obj, self.name, val)

    def __delete__(self, obj):
        delattr(obj, self.name)

Updated version for review at https://codereview.stackexchange.com/q/98892/32391

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.