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?
-
1Getting 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– Robert Harvey2015年07月26日 17:35:01 +00:00Commented Jul 26, 2015 at 17:35
-
@RobertHarvey that's a good point; describing them as a struggle is probably a bad choice of wordsDan Oberlam– Dan Oberlam2015年07月26日 17:46:24 +00:00Commented Jul 26, 2015 at 17:46
-
1As 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– jonrsharpe2015年07月26日 20:55:24 +00:00Commented Jul 26, 2015 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.Dan Oberlam– Dan Oberlam2015年07月29日 19:12:56 +00:00Commented Jul 29, 2015 at 19:12
-
With the advant of more and more python IDEs the new use-case in my opinion is code-completion suggestions. Providing the properties enables better suggestions. When using an IDE one is able to use templates anyway which mitigate the problem a bit.RedX– RedX2021年01月28日 13:20:57 +00:00Commented Jan 28, 2021 at 13:20
1 Answer 1
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
Explore related questions
See similar questions with these tags.